From c2f62cd8e081d91f251b572d91c8f90f8fe37f9e Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Wed, 20 Dec 2023 23:58:28 +0000 Subject: [PATCH] Now we're uploading some data. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4682 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- src/http.c | 157 ++++++++++++++++++++++++++++++++++++++++------------ src/http.h | 1 + src/tests.c | 27 ++++++++- 3 files changed, 147 insertions(+), 38 deletions(-) diff --git a/src/http.c b/src/http.c index feaa93b7..33b752a5 100644 --- a/src/http.c +++ b/src/http.c @@ -2,11 +2,13 @@ #include "log.h" #include "mem.h" +#include "util.js.h" #include "picohttpparser.h" #include "uv.h" #include +#include #include #if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(_WIN32) @@ -18,12 +20,26 @@ typedef struct _tf_http_connection_t tf_http_t* http; uv_tcp_t tcp; + const char* method; + const char* path; + + char headers_buffer[8192]; + size_t headers_buffer_length; + int parsed_length; + char buffer[8192]; size_t buffer_length; - int parsed_length; + + struct phr_header headers[32]; + int headers_length; + bool headers_done; tf_http_callback_t* callback; void* user_data; + + void* body; + size_t body_length; + size_t content_length; } tf_http_connection_t; typedef struct _tf_http_handler_t @@ -49,6 +65,7 @@ typedef struct _tf_http_t } tf_http_t; static void _http_connection_destroy(tf_http_connection_t* connection); +static const char* _http_status_text(int status); tf_http_t* tf_http_create(uv_loop_t* loop) { @@ -63,14 +80,21 @@ tf_http_t* tf_http_create(uv_loop_t* loop) void _http_allocate_buffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf) { tf_http_connection_t* connection = handle->data; - *buf = uv_buf_init(connection->buffer + connection->buffer_length, sizeof(connection->buffer) - connection->buffer_length); + if (!connection->headers_done) + { + *buf = uv_buf_init(connection->headers_buffer + connection->headers_buffer_length, sizeof(connection->headers_buffer) - connection->headers_buffer_length); + } + else + { + *buf = uv_buf_init(connection->buffer + connection->buffer_length, sizeof(connection->buffer) - connection->buffer_length); + } } 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].pattern) + if (!http->handlers[i].pattern || strcmp(path, http->handlers[i].pattern) == 0) { *out_callback = http->handlers[i].callback; *out_user_data = http->handlers[i].user_data; @@ -108,55 +132,102 @@ static void _http_connection_destroy(tf_http_connection_t* connection) http->connections[i] = http->connections[--http->connections_count]; } } + if (connection->body) + { + tf_free(connection->body); + connection->body = NULL; + } tf_free(connection); } } -void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t* buffer) +static void _http_builtin_404_handler(tf_http_request_t* request) +{ + const char* k_payload = _http_status_text(404); + tf_http_respond(request, 404, NULL, 0, k_payload, strlen(k_payload)); +} + +static void _http_add_body_bytes(tf_http_connection_t* connection, const void* data, size_t size) +{ + size_t fit = tf_min(connection->content_length - connection->body_length, size); + if (fit > 0) + { + memcpy((char*)connection->body + connection->body_length, data, fit); + connection->body_length += fit; + } + + if (connection->body_length == connection->content_length) + { + tf_http_request_t request = + { + .connection = connection, + .phase = k_http_callback_phase_headers_received, + .method = connection->method, + .path = connection->path, + .headers = connection->headers, + .headers_count = connection->headers_length, + .user_data = connection->user_data, + }; + connection->callback(&request); + } +} + +static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t* buffer) { tf_http_connection_t* connection = stream->data; if (read_size > 0) { - connection->buffer_length += read_size; - - const char* method = NULL; - size_t method_length = 0; - const char* path = NULL; - size_t path_length = 0; - int minor_version = 0; - struct phr_header headers[100]; - size_t header_count = sizeof(headers) / sizeof(*headers); - - int parse_result = phr_parse_request(connection->buffer, connection->buffer_length, &method, &method_length, &path, &path_length, &minor_version, headers, &header_count, connection->parsed_length); - connection->parsed_length = connection->buffer_length; - if (parse_result > 0) + if (!connection->headers_done) { - if (_http_find_handler(connection->http, path, &connection->callback, &connection->user_data) && connection->callback) + connection->headers_buffer_length += read_size; + + const char* method = NULL; + size_t method_length = 0; + const char* path = NULL; + size_t path_length = 0; + int minor_version = 0; + size_t header_count = sizeof(connection->headers) / sizeof(*connection->headers); + + int parse_result = phr_parse_request(connection->headers_buffer, connection->headers_buffer_length, &method, &method_length, &path, &path_length, &minor_version, connection->headers, &header_count, connection->parsed_length); + connection->parsed_length = connection->headers_buffer_length; + if (parse_result > 0) { - char* method_copy = alloca(method_length + 1); - memcpy(method_copy, method, method_length); - method_copy[method_length] = '\0'; + connection->headers_done = true; + connection->method = method; + ((char*)connection->method)[method_length] = '\0'; + connection->path = path; + ((char*)connection->path)[path_length] = '\0'; - char* path_copy = alloca(path_length + 1); - memcpy(path_copy, path, path_length); - path_copy[path_length] = '\0'; - - tf_http_request_t request = + for (int i = 0; i < (int)header_count; i++) { - .connection = connection, - .phase = k_http_callback_phase_headers_received, - .method = method_copy, - .path = path_copy, - .headers = headers, - .headers_count = (int)header_count, - .user_data = connection->user_data, - }; - connection->callback(&request); + if (connection->headers[i].name_len == strlen("content-length") && + strncasecmp(connection->headers[i].name, "content-length", connection->headers[i].name_len) == 0) + { + connection->content_length = strtoull(connection->headers[i].value, NULL, 10); + } + } + + if (connection->content_length) + { + connection->body = tf_malloc(connection->content_length); + } + + if (!_http_find_handler(connection->http, connection->path, &connection->callback, &connection->user_data) || !connection->callback) + { + connection->callback = _http_builtin_404_handler; + } + + _http_add_body_bytes(connection, connection->headers_buffer + parse_result, connection->headers_buffer_length - parse_result); + } + else + { + tf_printf("phr_parse_request: %d\n", parse_result); } } else { - tf_printf("phr_parse_request: %d\n", parse_result); + connection->buffer_length += read_size; + _http_add_body_bytes(connection, connection->buffer, connection->buffer_length); } } else @@ -268,6 +339,14 @@ void tf_http_destroy(tf_http_t* http) http->listeners = NULL; http->listeners_count = 0; + for (int i = 0; i < http->handlers_count; i++) + { + if (http->handlers[i].pattern) + { + tf_free((void*)http->handlers[i].pattern); + http->handlers[i].pattern = NULL; + } + } tf_free(http->handlers); http->handlers_count = 0; @@ -347,3 +426,9 @@ void tf_http_respond(tf_http_request_t* request, int status, const char** header *shutdown_request = (uv_shutdown_t) { .data = request }; uv_shutdown(shutdown_request, (uv_stream_t*)&request->connection->tcp, _http_on_shutdown); } + +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; +} diff --git a/src/http.h b/src/http.h index a4d98602..ee634107 100644 --- a/src/http.h +++ b/src/http.h @@ -29,4 +29,5 @@ 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, 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); diff --git a/src/tests.c b/src/tests.c index 7256ee98..dbc555b8 100644 --- a/src/tests.c +++ b/src/tests.c @@ -685,7 +685,15 @@ static void _test_http_thread(void* data) { #if defined(__linux__) test_http_t* test = data; - int r = system("curl -v http://localhost:23456/"); + int r = system("curl -v http://localhost:23456/404"); + assert(WEXITSTATUS(r) == 0); + tf_printf("curl returned %d\n", WEXITSTATUS(r)); + + r = system("curl -v http://localhost:23456/hello"); + assert(WEXITSTATUS(r) == 0); + tf_printf("curl returned %d\n", WEXITSTATUS(r)); + + r = system("curl -v --data 'hello world' http://localhost:23456/post"); assert(WEXITSTATUS(r) == 0); tf_printf("curl returned %d\n", WEXITSTATUS(r)); @@ -706,12 +714,27 @@ static void _test_http_handler(tf_http_request_t* request) tf_http_respond(request, 200, headers, 1, k_payload, strlen(k_payload)); } +static void _test_http_handler_post(tf_http_request_t* request) +{ + const void* body = NULL; + size_t size = tf_http_get_body(request, &body); + tf_printf("size = %zd body=%.*s\n", size, (int)size, (const char*)body); + const char* headers[] = + { + "Connection", "close", + }; + const char* k_payload = "Hello, world!\n"; + tf_http_respond(request, 200, headers, 1, k_payload, strlen(k_payload)); +} + static void _test_http(const tf_test_options_t* options) { + tf_printf("Starting http.\n"); uv_loop_t loop = { 0 }; uv_loop_init(&loop); tf_http_t* http = tf_http_create(&loop); - tf_http_add_handler(http, NULL, _test_http_handler, 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 };