diff --git a/core/httpd.js b/core/httpd.js index 0fd5a5d7..93ce0a66 100644 --- a/core/httpd.js +++ b/core/httpd.js @@ -5,6 +5,7 @@ var gSocketHandlers = []; var gBadRequests = {}; const kRequestTimeout = 15000; +const kStallTimeout = 60000; function logError(error) { print("ERROR " + error); @@ -388,10 +389,12 @@ function allowRequest(client) { function handleConnection(client) { if (!allowRequest(client)) { print('Rejecting client for too many bad requests: ', client.peerName, gBadRequests[client.peerName].reason); + client.info = 'rejected'; client.close(); return; } + client.info = 'accepted'; var inputBuffer = new Uint8Array(0); var request; var headers = {}; @@ -399,17 +402,37 @@ function handleConnection(client) { var bodyToRead = -1; var body; var requestCount = -1; + var readCount = 0; + var isWebsocket = false; function resetTimeout(requestIndex) { - setTimeout(function() { - if (bodyToRead == -1 && requestCount == requestIndex) { - if (requestCount == 0) { - badRequest(client, 'Timed out waiting for request.'); - } else { - client.close(); + if (isWebsocket) { + return; + } + if (bodyToRead == -1) { + setTimeout(function() { + if (requestCount == requestIndex) { + client.info = 'timed out'; + if (requestCount == 0) { + badRequest(client, 'Timed out waiting for request.'); + } else { + client.close(); + } } - } - }, kRequestTimeout); + }, kRequestTimeout); + } else { + var lastReadCount = readCount; + setTimeout(function() { + if (readCount == lastReadCount) { + client.info = 'stalled'; + if (requestCount == 0) { + badRequest(client, 'Request stalled.'); + } else { + client.close(); + } + } + }, kStallTimeout); + } } resetTimeout(++requestCount); @@ -421,10 +444,12 @@ function handleConnection(client) { lineByLine = true; bodyToRead = -1; body = undefined; + client.info = 'reset'; resetTimeout(++requestCount); } function finish() { + client.info = 'finishing'; var requestObject = new Request(request[0], request[1], request[2], headers, body, client); var response = new Response(requestObject, client); try { @@ -468,11 +493,14 @@ function handleConnection(client) { return false; } body = new Uint8Array(bodyToRead); + client.info = 'waiting for body'; return true; } else if (headers["connection"] && headers["connection"].toLowerCase().split(",").map(x => x.trim()).indexOf("upgrade") != -1 && headers["upgrade"] && headers["upgrade"].toLowerCase() == "websocket") { + isWebsocket = true; + client.info = 'websocket'; var requestObject = new Request(request[0], request[1], request[2], headers, body, client); var response = new Response(requestObject, client); handleWebSocketRequest(requestObject, response, client); @@ -504,7 +532,11 @@ function handleConnection(client) { }); client.read(function(data) { + readCount++; if (data) { + if (bodyToRead != -1 && !isWebsocket) { + resetTimeout(requestCount); + } const kMaxLineLength = 4096; var newBuffer = new Uint8Array(inputBuffer.length + data.length); newBuffer.set(inputBuffer, 0); @@ -537,6 +569,9 @@ function handleConnection(client) { inputBuffer = new Uint8Array(0); } } + } else { + client.info = 'EOF'; + client.close(); } }); } diff --git a/src/socket.js.c b/src/socket.js.c index fad83c7c..8e0b7b1f 100644 --- a/src/socket.js.c +++ b/src/socket.js.c @@ -45,6 +45,7 @@ typedef struct _socket_t { JSValue _onConnect; JSValue _onRead; JSValue _onError; + JSValue _info; uint64_t created_ms; } socket_t; @@ -67,6 +68,7 @@ static JSValue _socket_getPeerName(JSContext* context, JSValueConst this_val, in static JSValue _socket_getPeerCertificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _socket_getNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _socket_setNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); +static JSValue _socket_setInfo(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _sockets_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static void _socket_onClose(uv_handle_t* handle); @@ -122,6 +124,7 @@ static void _socket_gc_mark(JSRuntime* runtime, JSValueConst value, JS_MarkFunc JS_MarkValue(runtime, socket->_onConnect, mark_func); JS_MarkValue(runtime, socket->_onRead, mark_func); JS_MarkValue(runtime, socket->_onError, mark_func); + JS_MarkValue(runtime, socket->_info, mark_func); } } @@ -212,6 +215,11 @@ socket_t* _socket_create_internal(JSContext* context) JS_DefinePropertyGetSet(context, object, atom, get_no_delay, set_no_delay, 0); JS_FreeAtom(context, atom); + atom = JS_NewAtom(context, "info"); + JSValue set_info = JS_NewCFunction(context, _socket_setInfo, "setInfo", 1); + JS_DefinePropertyGetSet(context, object, atom, JS_UNDEFINED, set_info, 0); + JS_FreeAtom(context, atom); + ++_open_count; uv_tcp_init(tf_task_get_loop(socket->_task), &socket->_socket); socket->_socket.data = socket; @@ -1065,6 +1073,15 @@ JSValue _socket_setNoDelay(JSContext* context, JSValueConst this_val, int argc, return JS_UNDEFINED; } +JSValue _socket_setInfo(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + socket_t* socket = JS_GetOpaque(this_val, _classId); + JSValue old = socket->_info; + socket->_info = JS_DupValue(context, argv[0]); + JS_FreeValue(context, old); + return JS_UNDEFINED; +} + JSValue _sockets_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { JSValue array = JS_NewArray(context); @@ -1077,6 +1094,7 @@ JSValue _sockets_get(JSContext* context, JSValueConst this_val, int argc, JSValu JS_SetPropertyStr(context, entry, "connected", JS_NewBool(context, s->_connected)); JS_SetPropertyStr(context, entry, "tls", JS_NewBool(context, s->_tls != NULL)); JS_SetPropertyStr(context, entry, "age_seconds", JS_NewFloat64(context, (uv_now(tf_task_get_loop(s->_task)) - s->created_ms) / 1000.f)); + JS_SetPropertyStr(context, entry, "info", JS_DupValue(context, s->_info)); JS_SetPropertyUint32(context, array, i, entry); } return array;