diff --git a/src/http.c b/src/http.c index 8a9ff2b6..85eecd3c 100644 --- a/src/http.c +++ b/src/http.c @@ -158,6 +158,15 @@ static void _http_connection_destroy(tf_http_connection_t* connection, const cha { connection->is_shutting_down = true; + if (connection->request && connection->request->on_close) + { + tf_http_close_callback* on_close = connection->request->on_close; + connection->request->on_close = NULL; + tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "websocket"); + on_close(connection->request); + tf_trace_end(connection->http->trace); + } + if (connection->tcp.data && !uv_is_closing((uv_handle_t*)&connection->tcp)) { uv_close((uv_handle_t*)&connection->tcp, _http_connection_on_close); @@ -896,6 +905,7 @@ void tf_http_request_release(tf_http_request_t* request) } if (--request->ref_count == 0) { + request->connection->request = NULL; tf_free(request); } } diff --git a/src/http.h b/src/http.h index 21d1352f..4a74a408 100644 --- a/src/http.h +++ b/src/http.h @@ -11,6 +11,7 @@ typedef struct _tf_trace_t tf_trace_t; typedef struct uv_loop_s uv_loop_t; typedef void (tf_http_message_callback)(tf_http_request_t* request, int op_code, const void* data, size_t size); +typedef void (tf_http_close_callback)(tf_http_request_t* request); typedef struct _tf_http_request_t { @@ -25,6 +26,7 @@ typedef struct _tf_http_request_t struct phr_header* headers; int headers_count; tf_http_message_callback* on_message; + tf_http_close_callback* on_close; void* context; void* user_data; int ref_count; diff --git a/src/httpd.js.c b/src/httpd.js.c index 685cb541..7b8cb323 100644 --- a/src/httpd.js.c +++ b/src/httpd.js.c @@ -163,12 +163,23 @@ static JSValue _httpd_response_send(JSContext* context, JSValueConst this_val, i return JS_UNDEFINED; } +static void _httpd_close_callback(tf_http_request_t* request) +{ + JSContext* context = request->context; + JSValue response_object = JS_MKPTR(JS_TAG_OBJECT, request->user_data); + JSValue on_close = JS_GetPropertyStr(context, response_object, "onClose"); + JSValue response = JS_Call(context, on_close, JS_UNDEFINED, 0, NULL); + tf_util_report_error(context, response); + JS_FreeValue(context, response); + JS_FreeValue(context, on_close); + tf_http_request_release(request); +} + static void _httpd_message_callback(tf_http_request_t* request, int op_code, const void* data, size_t size) { JSContext* context = request->context; JSValue response_object = JS_MKPTR(JS_TAG_OBJECT, request->user_data); JSValue on_message = JS_GetPropertyStr(context, response_object, "onMessage"); - JSValue event = JS_NewObject(context); JS_SetPropertyStr(context, event, "opCode", JS_NewInt32(context, op_code)); JS_SetPropertyStr(context, event, "data", JS_NewStringLen(context, data, size)); @@ -307,12 +318,14 @@ static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_va tf_http_respond(request, 101, headers, headers_count, NULL, 0); request->on_message = _httpd_message_callback; + request->on_close = _httpd_close_callback; request->context = context; request->user_data = JS_VALUE_GET_PTR(JS_DupValue(context, this_val)); } else { tf_http_respond(request, 400, NULL, 0, NULL, 0); + tf_http_request_release(request); } return JS_UNDEFINED; @@ -324,6 +337,7 @@ static JSValue _httpd_endpoint_all(JSContext* context, JSValueConst this_val, in const char* pattern = JS_ToCString(context, argv[0]); http_handler_data_t* data = tf_malloc(sizeof(http_handler_data_t)); *data = (http_handler_data_t) { .context = context, .callback = JS_DupValue(context, argv[1]) }; + /* TODO: This leaks the callback. */ tf_http_add_handler(http, pattern, _httpd_callback, data); JS_FreeCString(context, pattern); return JS_UNDEFINED; @@ -334,6 +348,7 @@ static JSValue _httpd_endpoint_start(JSContext* context, JSValueConst this_val, tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id); int port = 0; JS_ToInt32(context, &port, argv[0]); + /* TODO: This leaks the TLS context. */ tf_tls_context_t* tls = tf_tls_context_get(JS_DupValue(context, argv[1])); int assigned_port = tf_http_listen(http, port, tls); return JS_NewInt32(context, assigned_port);