From 9ef909c9a14971bb474d9e293a27cf6a4b925a8e Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Tue, 2 Jan 2024 15:02:47 +0000 Subject: [PATCH] Reimplement http -> https redirects. Remove request phases. With just a little extra storage, it wasn't needed. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4723 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- core/core.js | 3 +++ src/http.c | 27 +++++++++++++++++++++++- src/http.h | 14 ++++++------- src/httpd.js.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 8 deletions(-) diff --git a/core/core.js b/core/core.js index 13b9563a7..0c70bf00e 100644 --- a/core/core.js +++ b/core/core.js @@ -951,6 +951,9 @@ function stringResponse(response, data) { } loadSettings().then(function() { + if (tildefriends.https_port && gGlobalSettings.http_redirect) { + httpd.set_http_redirect(gGlobalSettings.http_redirect); + } httpd.all("/login", auth.handler); httpd.registerSocketHandler("/app/socket", app.socket); httpd.all("", function(request, response) { diff --git a/src/http.c b/src/http.c index aafd54838..a2270b62f 100644 --- a/src/http.c +++ b/src/http.c @@ -89,6 +89,9 @@ typedef struct _tf_http_t int pending_closes; uv_loop_t* loop; + + void* user_data; + tf_http_cleanup_t* user_data_cleanup; } tf_http_t; static const char* _http_connection_get_header(const tf_http_connection_t* connection, const char* name); @@ -337,8 +340,9 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d tf_http_request_t* request = tf_malloc(sizeof(tf_http_request_t)); *request = (tf_http_request_t) { + .http = connection->http, .connection = connection, - .phase = k_http_callback_phase_headers_received, + .is_tls = connection->tls != NULL, .method = connection->method, .path = connection->path, .query = connection->query, @@ -671,6 +675,12 @@ void tf_http_destroy(tf_http_t* http) tf_free(http->connections); http->connections_count = 0; + if (http->user_data_cleanup) + { + http->user_data_cleanup(http->user_data); + http->user_data = NULL; + } + tf_free(http); } @@ -897,3 +907,18 @@ void tf_http_request_websocket_upgrade(tf_http_request_t* request) { request->connection->is_websocket = true; } + +void tf_http_set_user_data(tf_http_t* http, void* user_data, tf_http_cleanup_t* cleanup) +{ + if (http->user_data && http->user_data_cleanup) + { + http->user_data_cleanup(http->user_data); + } + http->user_data = user_data; + http->user_data_cleanup = cleanup; +} + +void* tf_http_get_user_data(tf_http_t* http) +{ + return http->user_data; +} diff --git a/src/http.h b/src/http.h index 624016365..61a5b146d 100644 --- a/src/http.h +++ b/src/http.h @@ -1,5 +1,6 @@ #pragma once +#include #include typedef struct _tf_http_connection_t tf_http_connection_t; @@ -8,18 +9,13 @@ typedef struct _tf_http_t tf_http_t; typedef struct _tf_tls_context_t tf_tls_context_t; typedef struct uv_loop_s uv_loop_t; -typedef enum _tf_http_callback_phase_t -{ - k_http_callback_phase_headers_received, - k_http_callback_phase_request_done, -} tf_http_callback_phase_t; - typedef void (tf_http_message_callback)(tf_http_request_t* request, int op_code, const void* data, size_t size); typedef struct _tf_http_request_t { - tf_http_callback_phase_t phase; + tf_http_t* http; tf_http_connection_t* connection; + bool is_tls; const char* method; const char* path; const char* query; @@ -34,6 +30,7 @@ typedef struct _tf_http_request_t } tf_http_request_t; typedef void (tf_http_callback_t)(tf_http_request_t* request); +typedef void (tf_http_cleanup_t)(void* user_data); tf_http_t* tf_http_create(uv_loop_t* loop); int tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls); @@ -42,6 +39,9 @@ void tf_http_respond(tf_http_request_t* request, int status, const char** header 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_set_user_data(tf_http_t* http, void* user_data, tf_http_cleanup_t* cleanup); +void* tf_http_get_user_data(tf_http_t* http); + 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); diff --git a/src/httpd.js.c b/src/httpd.js.c index 2c44c2d24..a765c1115 100644 --- a/src/httpd.js.c +++ b/src/httpd.js.c @@ -21,6 +21,11 @@ static JSClassID _httpd_class_id; static JSClassID _httpd_request_class_id; +typedef struct _http_user_data_t +{ + char redirect[1024]; +} http_user_data_t; + typedef struct _http_handler_data_t { JSContext* context; @@ -217,13 +222,42 @@ static void _httpd_callback_internal(tf_http_request_t* request, bool is_websock } } +static bool _httpd_redirect(tf_http_request_t* request) +{ + if (request->is_tls) + { + return false; + } + + http_user_data_t* user_data = tf_http_get_user_data(request->http); + if (!user_data || !*user_data->redirect) + { + return false; + } + + char redirect[1024]; + snprintf(redirect, sizeof(redirect), "%s%s", user_data->redirect, request->path); + tf_http_respond(request, 303, (const char*[]) { "Location", redirect }, 1, NULL, 0); + return true; +} + static void _httpd_callback(tf_http_request_t* request) { + if (_httpd_redirect(request)) + { + return; + } + _httpd_callback_internal(request, false); } static void _httpd_websocket_callback(tf_http_request_t* request) { + if (_httpd_redirect(request)) + { + return; + } + 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"); @@ -297,6 +331,28 @@ static JSValue _httpd_start(JSContext* context, JSValueConst this_val, int argc, return JS_NewInt32(context, assigned_port); } +static void _httpd_free_user_data(void* user_data) +{ + tf_free(user_data); +} + +static JSValue _httpd_set_http_redirect(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id); + http_user_data_t* user_data = tf_http_get_user_data(http); + if (!user_data) + { + user_data = tf_malloc(sizeof(http_user_data_t)); + memset(user_data, 0, sizeof(http_user_data_t)); + tf_http_set_user_data(http, user_data, _httpd_free_user_data); + } + + const char* redirect = JS_ToCString(context, argv[0]); + snprintf(user_data->redirect, sizeof(user_data->redirect), "%s", redirect ? redirect : ""); + JS_FreeCString(context, redirect); + return JS_UNDEFINED; +} + void _httpd_finalizer(JSRuntime* runtime, JSValue value) { tf_http_t* http = JS_GetOpaque(value, _httpd_class_id); @@ -343,6 +399,7 @@ void tf_httpd_register(JSContext* context) JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_all, "all", 2)); JS_SetPropertyStr(context, httpd, "registerSocketHandler", JS_NewCFunction(context, _httpd_register_socket_handler, "register_socket_handler", 2)); JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_start, "start", 2)); + JS_SetPropertyStr(context, httpd, "set_http_redirect", JS_NewCFunction(context, _httpd_set_http_redirect, "set_http_redirect", 1)); JS_SetPropertyStr(context, global, "httpd", httpd); JS_FreeValue(context, global); }