From 38ab32dad9674a1ee0c960737ec83314234b70c6 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Thu, 21 Dec 2023 17:45:06 +0000 Subject: [PATCH] Fix some http request lifetime issues, and follow the same lowercase convention for headers. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4690 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- src/http.c | 34 ++++++++++++++++++++++++++++++++-- src/http.h | 4 ++++ src/httpd.js.c | 30 +++++++++++++++++++++++------- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/http.c b/src/http.c index afefdc9e..3d5b64c7 100644 --- a/src/http.c +++ b/src/http.c @@ -21,6 +21,8 @@ typedef struct _tf_http_connection_t uv_tcp_t tcp; uv_shutdown_t shutdown; + int ref_count; + const char* method; const char* path; int minor_version; @@ -127,7 +129,7 @@ static void _http_connection_destroy(tf_http_connection_t* connection) { uv_close((uv_handle_t*)&connection->tcp, _http_connection_on_close); } - else + else if (connection->ref_count == 0) { tf_http_t* http = connection->http; for (int i = 0; i < http->connections_count; i++) @@ -182,7 +184,9 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d .headers_count = connection->headers_length, .user_data = connection->user_data, }; + tf_http_request_ref(request); connection->callback(request); + tf_http_request_release(request); _http_reset_connection(connection); } } @@ -207,6 +211,7 @@ static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t if (parse_result > 0) { connection->headers_done = true; + connection->headers_length = header_count; connection->method = method; ((char*)connection->method)[method_length] = '\0'; connection->path = path; @@ -216,6 +221,14 @@ static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t for (int i = 0; i < (int)header_count; i++) { + for (size_t j = 0; j < connection->headers[i].name_len; j++) + { + if (connection->headers[i].name[j] >= 'A' && + connection->headers[i].name[j] <= 'Z') + { + ((char*)connection->headers[i].name)[j] += 'a' - 'A'; + } + } ((char*)connection->headers[i].name)[connection->headers[i].name_len] = '\0'; ((char*)connection->headers[i].value)[connection->headers[i].value_len] = '\0'; if (strcasecmp(connection->headers[i].name, "content-length") == 0) @@ -464,7 +477,6 @@ void tf_http_respond(tf_http_request_t* request, int status, const char** header request->connection->shutdown.data = request->connection; uv_shutdown(&request->connection->shutdown, (uv_stream_t*)&request->connection->tcp, _http_on_shutdown); } - tf_free(request); } size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data) @@ -472,3 +484,21 @@ size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data) *out_data = request->connection->body; return request->connection->content_length; } + +void tf_http_request_ref(tf_http_request_t* request) +{ + request->ref_count++; + request->connection->ref_count++; +} + +void tf_http_request_release(tf_http_request_t* request) +{ + if (--request->connection->ref_count == 0) + { + /* Reset the connection? */ + } + if (--request->ref_count == 0) + { + tf_free(request); + } +} diff --git a/src/http.h b/src/http.h index ee634107..556b5f41 100644 --- a/src/http.h +++ b/src/http.h @@ -21,6 +21,7 @@ typedef struct _tf_http_request_t struct phr_header* headers; int headers_count; void* user_data; + int ref_count; } tf_http_request_t; typedef void (tf_http_callback_t)(tf_http_request_t* request); @@ -31,3 +32,6 @@ void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_ void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length); size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data); void tf_http_destroy(tf_http_t* http); + +void tf_http_request_ref(tf_http_request_t* request); +void tf_http_request_release(tf_http_request_t* request); diff --git a/src/httpd.js.c b/src/httpd.js.c index 72a9d418..a8ba6cc2 100644 --- a/src/httpd.js.c +++ b/src/httpd.js.c @@ -15,7 +15,7 @@ #endif static JSClassID _httpd_class_id; -static JSClassID _httpd_response_class_id; +static JSClassID _httpd_request_class_id; typedef struct _http_handler_data_t { @@ -32,7 +32,7 @@ static JSValue _httpd_response_write_head(JSContext* context, JSValueConst this_ static JSValue _httpd_response_end(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { - tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_response_class_id); + tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id); size_t length = 0; const void* data = NULL; JSValue buffer; @@ -104,17 +104,18 @@ static void _httpd_callback(tf_http_request_t* request) JSValue request_object = JS_NewObject(context); JS_SetPropertyStr(context, request_object, "uri", JS_NewString(context, request->path)); JSValue headers = JS_NewObject(context); - JS_SetPropertyStr(context, request_object, "headers", headers); for (int i = 0; i < request->headers_count; i++) { JS_SetPropertyStr(context, headers, request->headers[i].name, JS_NewString(context, request->headers[i].value)); } + JS_SetPropertyStr(context, request_object, "headers", headers); JSValue client = JS_NewObject(context); JS_SetPropertyStr(context, client, "tls", JS_FALSE); JS_SetPropertyStr(context, request_object, "client", client); - JSValue response_object = JS_NewObjectClass(context, _httpd_response_class_id); + JSValue response_object = JS_NewObjectClass(context, _httpd_request_class_id); + tf_http_request_ref(request); JS_SetOpaque(response_object, request); JS_SetPropertyStr(context, response_object, "writeHead", JS_NewCFunction(context, _httpd_response_write_head, "writeHead", 2)); JS_SetPropertyStr(context, response_object, "end", JS_NewCFunction(context, _httpd_response_end, "end", 1)); @@ -161,19 +162,34 @@ void _httpd_finalizer(JSRuntime* runtime, JSValue value) tf_http_destroy(http); } +void _httpd_request_finalizer(JSRuntime* runtime, JSValue value) +{ + tf_http_request_t* request = JS_GetOpaque(value, _httpd_request_class_id); + tf_http_request_release(request); +} + void tf_httpd_register(JSContext* context) { JS_NewClassID(&_httpd_class_id); - JS_NewClassID(&_httpd_response_class_id); - JSClassDef def = + JS_NewClassID(&_httpd_request_class_id); + JSClassDef httpd_def = { .class_name = "Httpd", .finalizer = &_httpd_finalizer, }; - if (JS_NewClass(JS_GetRuntime(context), _httpd_class_id, &def) != 0) + if (JS_NewClass(JS_GetRuntime(context), _httpd_class_id, &httpd_def) != 0) { fprintf(stderr, "Failed to register Httpd.\n"); } + JSClassDef request_def = + { + .class_name = "Request", + .finalizer = &_httpd_request_finalizer, + }; + if (JS_NewClass(JS_GetRuntime(context), _httpd_request_class_id, &request_def) != 0) + { + fprintf(stderr, "Failed to register Request.\n"); + } JSValue global = JS_GetGlobalObject(context); JSValue httpd = JS_NewObjectClass(context, _httpd_class_id);