From 5622db92a75c8c9016087aca06c62670a90c2525 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Thu, 2 Jun 2022 10:58:22 +0000 Subject: [PATCH] Trying to fix socket lifetime issues. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3886 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- core/httpd.js | 6 +- src/socket.js.c | 177 +++++++++++++++++++++++++++--------------------- src/task.c | 2 + 3 files changed, 105 insertions(+), 80 deletions(-) diff --git a/core/httpd.js b/core/httpd.js index 210c1b2a..27dd5966 100644 --- a/core/httpd.js +++ b/core/httpd.js @@ -175,10 +175,12 @@ function handleRequest(request, response) { if (promise) { promise.catch(function(error) { response.reportError(error); + request.client.close(); }); } } catch (error) { response.reportError(error); + request.client.close(); } } else { response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8"}); @@ -412,6 +414,7 @@ function handleConnection(client) { } } catch (error) { response.reportError(error); + client.close(); } } @@ -561,8 +564,9 @@ if (tildefriends.https_port) { tls.keyStat = stat[1]; } + let result = client.startTls(tls.context); handleConnection(client); - return client.startTls(tls.context); + return result; } catch (error) { logError("[" + new Date() + "] [" + client.peerName + "] " + error); } diff --git a/src/socket.js.c b/src/socket.js.c index f0cec254..632adbd1 100644 --- a/src/socket.js.c +++ b/src/socket.js.c @@ -31,6 +31,8 @@ typedef struct _socket_t { promiseid_t _closePromise; bool _connected; bool _noDelay; + bool _reading; + bool _listening; char _peerName[256]; socket_direction_t _direction; JSValue _object; @@ -80,6 +82,30 @@ static void _socket_processOutgoingTls(socket_t* socket); static void _socket_reportTlsErrors(socket_t* socket); static void _socket_reportError(socket_t* socket, const char* error); +static void _socket_set_handler(socket_t* socket, JSValue* handler, JSValue new_value) +{ + JSContext* context = tf_task_get_context(socket->_task); + bool had_handler = !JS_IsUndefined(*handler); + if (!had_handler && !JS_IsUndefined(new_value)) + { + JS_DupValue(context, socket->_object); + } + if (had_handler) + { + JSValue value = *handler; + *handler = JS_UNDEFINED; + JS_FreeValue(context, value); + } + if (!JS_IsUndefined(new_value)) + { + *handler = JS_DupValue(context, new_value); + } + if (had_handler && JS_IsUndefined(new_value)) + { + JS_FreeValue(context, socket->_object); + } +} + static void _socket_gc_mark(JSRuntime* runtime, JSValueConst value, JS_MarkFunc mark_func) { socket_t* socket = JS_GetOpaque(value, _classId); @@ -127,9 +153,6 @@ socket_t* _socket_create_internal(JSContext* context) socket_t* socket = malloc(sizeof(socket_t)); memset(socket, 0, sizeof(*socket)); - socket->_onRead = JS_UNDEFINED; - socket->_onError = JS_UNDEFINED; - socket->_onConnect = JS_UNDEFINED; socket->_closePromise = -1; socket->_startTlsPromise = -1; @@ -139,6 +162,10 @@ socket_t* _socket_create_internal(JSContext* context) socket->_object = object; JS_SetOpaque(object, socket); + socket->_onRead = JS_UNDEFINED; + socket->_onError = JS_UNDEFINED; + socket->_onConnect = JS_UNDEFINED; + JS_SetPropertyStr(context, object, "bind", JS_NewCFunction(context, _socket_bind, "bind", 2)); JS_SetPropertyStr(context, object, "connect", JS_NewCFunction(context, _socket_connect, "connect", 2)); JS_SetPropertyStr(context, object, "listen", JS_NewCFunction(context, _socket_listen, "listen", 2)); @@ -183,30 +210,16 @@ JSValue _socket_create(JSContext* context, JSValueConst this_val, int argc, JSVa void _socket_close_internal(socket_t* socket) { - if (!JS_IsUndefined(socket->_onRead)) - { - JSValue value = socket->_onRead; - socket->_onRead = JS_UNDEFINED; - JS_FreeValue(tf_task_get_context(socket->_task), value); - } - if (!JS_IsUndefined(socket->_onError)) - { - JSValue value = socket->_onError; - socket->_onError = JS_UNDEFINED; - JS_FreeValue(tf_task_get_context(socket->_task), value); - } - if (!JS_IsUndefined(socket->_onConnect)) - { - JSValue value = socket->_onConnect; - socket->_onConnect = JS_UNDEFINED; - JS_FreeValue(tf_task_get_context(socket->_task), value); - } + _socket_set_handler(socket, &socket->_onRead, JS_UNDEFINED); + _socket_set_handler(socket, &socket->_onError, JS_UNDEFINED); + _socket_set_handler(socket, &socket->_onConnect, JS_UNDEFINED); if (socket->_tls) { tf_tls_session_destroy(socket->_tls); socket->_tls = NULL; } - if (!uv_is_closing((uv_handle_t*)&socket->_socket)) + if (socket->_socket.data && + !uv_is_closing((uv_handle_t*)&socket->_socket)) { uv_close((uv_handle_t*)&socket->_socket, _socket_onClose); } @@ -222,28 +235,32 @@ void _socket_close_internal(socket_t* socket) void _socket_finalizer(JSRuntime *runtime, JSValue value) { socket_t* socket = JS_GetOpaque(value, _classId); + if (socket->_listening) + { + printf("FINALIZING A LISTENING SOCKET!\n"); + abort(); + } socket->_object = JS_UNDEFINED; _socket_close_internal(socket); } void _socket_reportError(socket_t* socket, const char* error) { - if (JS_IsFunction(tf_task_get_context(socket->_task),socket-> _onError)) + JSContext* context = tf_task_get_context(socket->_task); + JSValue ref = JS_DupValue(context, socket->_object); + if (JS_IsFunction(context, socket-> _onError)) { - JSValue exception = JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error); - JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onError, socket->_object, 1, &exception); - if (JS_IsException(result)) - { - printf("Socket error.\n"); - js_std_dump_error(tf_task_get_context(socket->_task)); - } - JS_FreeValue(tf_task_get_context(socket->_task), exception); - JS_FreeValue(tf_task_get_context(socket->_task), result); + JSValue exception = JS_ThrowInternalError(context, "%s", error); + JSValue result = JS_Call(context, socket->_onError, socket->_object, 1, &exception); + tf_util_report_error(context, result); + JS_FreeValue(context, exception); + JS_FreeValue(context, result); } else { fprintf(stderr, "Socket::reportError: %s\n", error); } + JS_FreeValue(context, ref); } void _socket_reportTlsErrors(socket_t* socket) @@ -501,11 +518,12 @@ void _socket_onConnect(uv_connect_t* request, int status) JSValue _socket_listen(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { socket_t* socket = JS_GetOpaque(this_val, _classId); + socket->_listening = true; int backlog = 1; JS_ToInt32(context, &backlog, argv[0]); if (JS_IsUndefined(socket->_onConnect)) { - socket->_onConnect = JS_DupValue(context, argv[1]); + _socket_set_handler(socket, &socket->_onConnect, argv[1]); int result = uv_listen((uv_stream_t*)&socket->_socket, backlog, _socket_onNewConnection); if (result != 0) { @@ -524,15 +542,15 @@ JSValue _socket_listen(JSContext* context, JSValueConst this_val, int argc, JSVa void _socket_onNewConnection(uv_stream_t* server, int status) { socket_t* socket = server->data; + JSContext* context = tf_task_get_context(socket->_task); + JSValue ref = JS_DupValue(context, socket->_object); if (!JS_IsUndefined(socket->_onConnect)) { - JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onConnect, socket->_object, 0, NULL); - if (JS_IsException(result)) - { - printf("Socket error on connection.\n"); - js_std_dump_error(tf_task_get_context(socket->_task)); - } + JSValue result = JS_Call(context, socket->_onConnect, socket->_object, 0, NULL); + tf_util_report_error(context, result); + JS_FreeValue(context, result); } + JS_FreeValue(context, ref); } JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) @@ -629,32 +647,38 @@ void _socket_onTlsShutdown(uv_write_t* request, int status) JSValue _socket_onError(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { socket_t* socket = JS_GetOpaque(this_val, _classId); - if (!JS_IsUndefined(socket->_onError)) - { - JS_FreeValue(context, socket->_onError); - socket->_onError = JS_UNDEFINED; - } - socket->_onError = JS_DupValue(context, argv[0]); + _socket_set_handler(socket, &socket->_onError, argv[0]); return JS_NULL; } JSValue _socket_read(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { socket_t* socket = JS_GetOpaque(this_val, _classId); - socket->_onRead = JS_DupValue(context, argv[0]); - int result = uv_read_start((uv_stream_t*)&socket->_socket, _socket_allocateBuffer, _socket_onRead); + JSValue ref = JS_DupValue(context, socket->_object); + _socket_set_handler(socket, &socket->_onRead, argv[0]); + promiseid_t promise = -1; JSValue read_result = tf_task_allocate_promise(socket->_task, &promise); - if (result != 0) + if (!socket->_reading) { - char error[256]; - snprintf(error, sizeof(error), "uv_read_start: %s", uv_strerror(result)); - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, error)); + int result = uv_read_start((uv_stream_t*)&socket->_socket, _socket_allocateBuffer, _socket_onRead); + if (result != 0) + { + char error[256]; + snprintf(error, sizeof(error), "uv_read_start: %s", uv_strerror(result)); + tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, error)); + } + else + { + socket->_reading = true; + tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED); + } } else { tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED); } + JS_FreeValue(context, ref); return read_result; } @@ -666,19 +690,17 @@ void _socket_allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer) { socket_t* socket = stream->data; + JSContext* context = tf_task_get_context(socket->_task); + JSValue ref = JS_DupValue(context, socket->_object); if (readSize <= 0) { socket->_connected = false; if (!JS_IsUndefined(socket->_onRead)) { JSValue args[] = { JS_UNDEFINED }; - JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onRead, socket->_object, 1, args); - if (JS_IsException(result)) - { - printf("Socket error on read.\n"); - js_std_dump_error(tf_task_get_context(socket->_task)); - } - JS_FreeValue(tf_task_get_context(socket->_task), result); + JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args); + tf_util_report_error(context, result); + JS_FreeValue(context, result); } _socket_close_internal(socket); } @@ -704,7 +726,7 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe char buffer[8192]; if (tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer))) { - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), buffer)); + tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, buffer)); } else { @@ -713,7 +735,7 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe } } - while (true) + while (socket->_tls) { char plain[8192]; int result = tf_tls_session_read_plain(socket->_tls, plain, sizeof(plain)); @@ -732,13 +754,9 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe if (!JS_IsUndefined(socket->_onRead)) { JSValue args[] = { JS_UNDEFINED }; - JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onRead, socket->_object, 1, args); - if (JS_IsException(result)) - { - printf("Socket error on read plain.\n"); - js_std_dump_error(tf_task_get_context(socket->_task)); - } - JS_FreeValue(tf_task_get_context(socket->_task), result); + JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args); + tf_util_report_error(context, result); + JS_FreeValue(context, result); } break; } @@ -747,6 +765,7 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe break; } } + if (socket->_tls) { _socket_processOutgoingTls(socket); @@ -758,6 +777,7 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe } } free(buffer->base); + JS_FreeValue(context, ref); } static JSValue _newUint8Array(JSContext* context, const void* data, size_t length) @@ -774,21 +794,20 @@ static JSValue _newUint8Array(JSContext* context, const void* data, size_t lengt void _socket_notifyDataRead(socket_t* socket, const char* data, size_t length) { - if (!JS_IsUndefined(socket->_onRead)) + if (data && length > 0) { - if (data && length > 0) + JSContext* context = tf_task_get_context(socket->_task); + JSValue ref = JS_DupValue(context, socket->_object); + JSValue typedArray = _newUint8Array(context, data, length); + JSValue args[] = { typedArray }; + if (!JS_IsUndefined(socket->_onRead)) { - JSValue typedArray = _newUint8Array(tf_task_get_context(socket->_task), data, length); - JSValue args[] = { typedArray }; - JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onRead, socket->_object, 1, args); - if (JS_IsException(result)) - { - printf("Socket error on data read.\n"); - js_std_dump_error(tf_task_get_context(socket->_task)); - } - JS_FreeValue(tf_task_get_context(socket->_task), typedArray); - JS_FreeValue(tf_task_get_context(socket->_task), result); + JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args); + tf_util_report_error(context, result); + JS_FreeValue(context, result); } + JS_FreeValue(context, typedArray); + JS_FreeValue(context, ref); } } diff --git a/src/task.c b/src/task.c index 460d880a..86cf9b73 100644 --- a/src/task.c +++ b/src/task.c @@ -732,6 +732,8 @@ static JSValue _tf_task_getStats(JSContext* context, JSValueConst this_val, int } JS_SetPropertyStr(context, result, "sqlite3_memory_percent", JS_NewFloat64(context, 100.0f * sqlite3_memory_used() / total_memory)); + JS_SetPropertyStr(context, result, "socket_count", JS_NewInt32(context, tf_socket_get_count())); + JS_SetPropertyStr(context, result, "socket_open_count", JS_NewInt32(context, tf_socket_get_open_count())); if (task->_ssb) {