diff --git a/core/core.js b/core/core.js index 0b736d18f..3927d606b 100644 --- a/core/core.js +++ b/core/core.js @@ -954,6 +954,7 @@ function stringResponse(response, data) { loadSettings().then(function() { let httpd_impl = (tildefriends.args.httpdc ? httpdc : httpd); httpd_impl.all("/login", auth.handler); + httpd_impl.registerSocketHandler("/app/socket", app.socket); httpd_impl.all("", function(request, response) { let match; if (request.uri === "/" || request.uri === "") { @@ -1004,7 +1005,6 @@ loadSettings().then(function() { return response.end(data); } }); - httpd_impl.registerSocketHandler("/app/socket", app.socket); httpd_impl.start(tildefriends.http_port); }).catch(function(error) { print('Failed to load settings.'); diff --git a/src/http.c b/src/http.c index 435477892..91b84d3b9 100644 --- a/src/http.c +++ b/src/http.c @@ -40,7 +40,6 @@ typedef struct _tf_http_connection_t int headers_length; bool headers_done; - int flags; tf_http_callback_t* callback; tf_http_request_t* request; void* user_data; @@ -56,7 +55,6 @@ typedef struct _tf_http_connection_t typedef struct _tf_http_handler_t { const char* pattern; - int flags; tf_http_callback_t* callback; void* user_data; } tf_http_handler_t; @@ -103,14 +101,13 @@ void _http_allocate_buffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* } } -bool _http_find_handler(tf_http_t* http, const char* path, int flags, tf_http_callback_t** out_callback, void** out_user_data) +bool _http_find_handler(tf_http_t* http, const char* path, tf_http_callback_t** out_callback, void** out_user_data) { for (int i = 0; i < http->handlers_count; i++) { - if (http->handlers[i].flags == flags && - (!http->handlers[i].pattern || + if (!http->handlers[i].pattern || strcmp(path, http->handlers[i].pattern) == 0 || - (strncmp(path, http->handlers[i].pattern, strlen(http->handlers[i].pattern)) == 0 && path[strlen(http->handlers[i].pattern)] == '/'))) + (strncmp(path, http->handlers[i].pattern, strlen(http->handlers[i].pattern)) == 0 && path[strlen(http->handlers[i].pattern)] == '/')) { *out_callback = http->handlers[i].callback; *out_user_data = http->handlers[i].user_data; @@ -271,10 +268,6 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d if (connection->body_length == connection->content_length) { - if (connection->flags & k_tf_http_handler_flag_websocket) - { - connection->is_websocket = true; - } tf_http_request_t* request = tf_malloc(sizeof(tf_http_request_t)); *request = (tf_http_request_t) { @@ -282,7 +275,6 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d .phase = k_http_callback_phase_headers_received, .method = connection->method, .path = connection->path, - .flags = connection->flags, .query = connection->query, .body = connection->body, .content_length = connection->content_length, @@ -367,9 +359,7 @@ static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t connection->body = tf_malloc(connection->content_length); } - int flags = _http_connection_get_header(connection, "upgrade") ? k_tf_http_handler_flag_websocket : 0; - connection->flags = flags; - if (!_http_find_handler(connection->http, connection->path, flags, &connection->callback, &connection->user_data) || !connection->callback) + if (!_http_find_handler(connection->http, connection->path, &connection->callback, &connection->user_data) || !connection->callback) { connection->callback = _http_builtin_404_handler; } @@ -475,13 +465,12 @@ void tf_http_listen(tf_http_t* http, int port) } } -void tf_http_add_handler(tf_http_t* http, const char* pattern, int flags, tf_http_callback_t* callback, void* user_data) +void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data) { http->handlers = tf_realloc(http->handlers, sizeof(tf_http_handler_t) * (http->handlers_count + 1)); http->handlers[http->handlers_count++] = (tf_http_handler_t) { .pattern = tf_strdup(pattern), - .flags = flags, .callback = callback, .user_data = user_data, }; @@ -664,3 +653,8 @@ const char* tf_http_request_get_header(tf_http_request_t* request, const char* n { return _http_connection_get_header(request->connection, name); } + +void tf_http_request_websocket_upgrade(tf_http_request_t* request) +{ + request->connection->is_websocket = true; +} diff --git a/src/http.h b/src/http.h index 8b82ba72a..39fb37693 100644 --- a/src/http.h +++ b/src/http.h @@ -21,7 +21,6 @@ typedef struct _tf_http_request_t tf_http_connection_t* connection; const char* method; const char* path; - int flags; const char* query; void* body; size_t content_length; @@ -33,17 +32,11 @@ typedef struct _tf_http_request_t int ref_count; } tf_http_request_t; -typedef enum _tf_http_handler_flags_t -{ - k_tf_http_handler_flag_none = 0, - k_tf_http_handler_flag_websocket = 1, -} tf_http_handler_flags_t; - typedef void (tf_http_callback_t)(tf_http_request_t* request); tf_http_t* tf_http_create(uv_loop_t* loop); void tf_http_listen(tf_http_t* http, int port); -void tf_http_add_handler(tf_http_t* http, const char* pattern, int flags, tf_http_callback_t* callback, void* user_data); +void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data); 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); @@ -52,3 +45,4 @@ void tf_http_request_ref(tf_http_request_t* request); void tf_http_request_release(tf_http_request_t* request); const char* tf_http_request_get_header(tf_http_request_t* request, const char* name); void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size); +void tf_http_request_websocket_upgrade(tf_http_request_t* request); diff --git a/src/httpd.js.c b/src/httpd.js.c index 24a19a8da..60c6dbe53 100644 --- a/src/httpd.js.c +++ b/src/httpd.js.c @@ -162,44 +162,8 @@ static void _httpd_message_callback(tf_http_request_t* request, int op_code, con JS_FreeValue(context, on_message); } -static void _httpd_callback(tf_http_request_t* request) +static void _httpd_callback_internal(tf_http_request_t* request, bool is_websocket) { - if (request->flags & k_tf_http_handler_flag_websocket) - { - const char* header_connection = tf_http_request_get_header(request, "connection"); - const char* header_upgrade = tf_http_request_get_header(request, "upgrade"); - const char* header_sec_websocket_key = tf_http_request_get_header(request, "sec-websocket-key"); - if (header_connection && - header_upgrade && - header_sec_websocket_key && - strstr(header_connection, "Upgrade") && - strcasecmp(header_upgrade, "websocket") == 0) - { - static const char* k_magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - size_t key_length = strlen(header_sec_websocket_key); - size_t size = key_length + 36; - uint8_t* key_magic = alloca(size); - memcpy(key_magic, header_sec_websocket_key, key_length); - memcpy(key_magic + key_length, k_magic, 36); - uint8_t digest[20]; - SHA1(key_magic, size, digest); - char key[41] = { 0 }; - tf_base64_encode(digest, sizeof(digest), key, sizeof(key)); - enum { k_headers_count = 4 }; - const char* headers[k_headers_count * 2] = - { - "Upgrade", "websocket", - "Connection", "Upgrade", - "Sec-WebSocket-Accept", key, - "Sec-WebSocket-Version", "13", - }; - bool send_version = - !tf_http_request_get_header(request, "sec-websocket-version") || - strcmp(tf_http_request_get_header(request, "sec-websocket-version"), "13") != 0; - tf_http_respond(request, 101, headers, send_version ? k_headers_count : (k_headers_count - 1), NULL, 0); - } - } - http_handler_data_t* data = request->user_data; JSContext* context = data->context; JSValue request_object = JS_NewObject(context); @@ -238,11 +202,66 @@ static void _httpd_callback(tf_http_request_t* request) JSValue response = JS_Call(context, data->callback, JS_UNDEFINED, 2, args); tf_util_report_error(context, response); JS_FreeValue(context, request_object); - //JS_FreeValue(context, response_object); JS_FreeValue(context, response); - request->on_message = _httpd_message_callback; - request->context = context; - request->user_data = JS_VALUE_GET_PTR(response_object); + + if (is_websocket) + { + request->on_message = _httpd_message_callback; + request->context = context; + request->user_data = JS_VALUE_GET_PTR(response_object); + } + else + { + JS_FreeValue(context, response_object); + } +} + +static void _httpd_callback(tf_http_request_t* request) +{ + _httpd_callback_internal(request, false); +} + +static void _httpd_websocket_callback(tf_http_request_t* request) +{ + const char* header_connection = tf_http_request_get_header(request, "connection"); + const char* header_upgrade = tf_http_request_get_header(request, "upgrade"); + const char* header_sec_websocket_key = tf_http_request_get_header(request, "sec-websocket-key"); + if (header_connection && + header_upgrade && + header_sec_websocket_key && + strstr(header_connection, "Upgrade") && + strcasecmp(header_upgrade, "websocket") == 0) + { + static const char* k_magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + size_t key_length = strlen(header_sec_websocket_key); + size_t size = key_length + 36; + uint8_t* key_magic = alloca(size); + memcpy(key_magic, header_sec_websocket_key, key_length); + memcpy(key_magic + key_length, k_magic, 36); + uint8_t digest[20]; + SHA1(key_magic, size, digest); + char key[41] = { 0 }; + tf_base64_encode(digest, sizeof(digest), key, sizeof(key)); + enum { k_headers_count = 4 }; + const char* headers[k_headers_count * 2] = + { + "Upgrade", "websocket", + "Connection", "Upgrade", + "Sec-WebSocket-Accept", key, + "Sec-WebSocket-Version", "13", + }; + bool send_version = + !tf_http_request_get_header(request, "sec-websocket-version") || + strcmp(tf_http_request_get_header(request, "sec-websocket-version"), "13") != 0; + tf_http_request_websocket_upgrade(request); + tf_http_respond(request, 101, headers, send_version ? k_headers_count : (k_headers_count - 1), NULL, 0); + } + else + { + tf_http_respond(request, 400, NULL, 0, NULL, 0); + } + + _httpd_callback_internal(request, true); } static JSValue _httpd_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) @@ -251,7 +270,7 @@ static JSValue _httpd_all(JSContext* context, JSValueConst this_val, int argc, J 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]) }; - tf_http_add_handler(http, pattern, k_tf_http_handler_flag_none, _httpd_callback, data); + tf_http_add_handler(http, pattern, _httpd_callback, data); JS_FreeCString(context, pattern); return JS_UNDEFINED; } @@ -262,7 +281,7 @@ static JSValue _httpd_register_socket_handler(JSContext* context, JSValueConst t 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]) }; - tf_http_add_handler(http, pattern, k_tf_http_handler_flag_websocket, _httpd_callback, data); + tf_http_add_handler(http, pattern, _httpd_websocket_callback, data); JS_FreeCString(context, pattern); return JS_UNDEFINED; } diff --git a/src/tests.c b/src/tests.c index c79c93e6c..0763c6fcc 100644 --- a/src/tests.c +++ b/src/tests.c @@ -737,8 +737,8 @@ static void _test_http(const tf_test_options_t* options) uv_loop_t loop = { 0 }; uv_loop_init(&loop); tf_http_t* http = tf_http_create(&loop); - tf_http_add_handler(http, "/hello", k_tf_http_handler_flag_none, _test_http_handler, NULL); - tf_http_add_handler(http, "/post", k_tf_http_handler_flag_none, _test_http_handler_post, NULL); + tf_http_add_handler(http, "/hello", _test_http_handler, NULL); + tf_http_add_handler(http, "/post", _test_http_handler_post, NULL); tf_http_listen(http, 23456); test_http_t test = { .loop = &loop };