From 7b3a9e0f638c725818b4a00730075af794da9f15 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sun, 17 Dec 2023 17:44:54 +0000 Subject: [PATCH] Send a valid HTTP response and shutdown the connection. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4679 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- src/http.c | 64 ++++++++++++++++++++++++++++++++++++++++++++--------- src/http.h | 3 +++ src/tests.c | 17 +++++++++++--- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/http.c b/src/http.c index d7d57a98..187675f6 100644 --- a/src/http.c +++ b/src/http.c @@ -6,6 +6,7 @@ #include "picohttpparser.h" #include "uv.h" +#include #include #if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(_WIN32) @@ -121,16 +122,6 @@ void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t* buffe .user_data = connection->user_data, }; connection->callback(&request); - - const char* payload = "HTTP/1.0 200 OK\r\nContent-Length: 13\r\n\r\nHello, world!"; - uv_write_t* write = tf_malloc(sizeof(uv_write_t) + strlen(payload)); - *write = (uv_write_t) { 0 }; - memcpy(write + 1, payload, strlen(payload)); - int r = uv_write(write, stream, &(uv_buf_t) { .base = (char*)(write + 1), .len = strlen(payload) }, 1, _http_on_write); - if (r) - { - tf_printf("uv_write: %s\n", uv_strerror(r)); - } } } else @@ -231,3 +222,56 @@ void tf_http_destroy(tf_http_t* http) { tf_free(http); } + +static const char* _http_status_text(int status) +{ + switch (status) + { + case 200: return "OK"; + case 404: return "File not found"; + default: return "Unknown"; + } +} + +static void _http_on_shutdown(uv_shutdown_t* request, int status) +{ + tf_free(request); +} + +void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, void* body, size_t content_length) +{ + const char* status_text = _http_status_text(status); + /* HTTP/1.x 200 OK\r\n */ + int headers_length = 8 + 1 + 3 + 1 + strlen(status_text) + 2; + for (int i = 0; i < headers_count; i += 2) + { + /* Key: Value\r\n */ + headers_length += strlen(headers[i]) + 2 + strlen(headers[i + 1]) + 2; + } + /* \r\n */ + headers_length += 2; + + uv_write_t* write = tf_malloc(sizeof(uv_write_t) + headers_length + content_length + 1); + *write = (uv_write_t) { .data = request->connection }; + char* buffer = (char*)(write + 1); + int offset = snprintf(buffer, headers_length + 1, "HTTP/1.0 %03d %s\r\n", status, status_text); + for (int i = 0; i < headers_count; i += 2) + { + offset += snprintf(buffer + offset, headers_length + 1 - offset, "%s: %s\r\n", headers[i], headers[i + 1]); + } + offset += snprintf(buffer + offset, headers_length + 1 - offset, "\r\n"); + assert(offset == headers_length); + if (content_length) + { + memcpy(buffer + offset, body, content_length); + } + int r = uv_write(write, (uv_stream_t*)&request->connection->tcp, &(uv_buf_t) { .base = buffer, .len = headers_length + content_length }, 1, _http_on_write); + if (r) + { + tf_printf("uv_write: %s\n", uv_strerror(r)); + } + + uv_shutdown_t* shutdown_request = tf_malloc(sizeof(uv_shutdown_t)); + *shutdown_request = (uv_shutdown_t) { .data = request }; + uv_shutdown(shutdown_request, (uv_stream_t*)&request->connection->tcp, _http_on_shutdown); +} diff --git a/src/http.h b/src/http.h index 2a73a9af..38ed3ca8 100644 --- a/src/http.h +++ b/src/http.h @@ -1,5 +1,7 @@ #pragma once +#include + typedef struct uv_loop_s uv_loop_t; typedef struct _tf_http_t tf_http_t; typedef struct _tf_http_connection_t tf_http_connection_t; @@ -26,4 +28,5 @@ 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, 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, void* body, size_t content_length); void tf_http_destroy(tf_http_t* http); diff --git a/src/tests.c b/src/tests.c index d269bcda..410f0c56 100644 --- a/src/tests.c +++ b/src/tests.c @@ -674,14 +674,20 @@ static void _test_http_thread(void* data) { #if defined(__linux__) int r = system("curl -v http://localhost:23456/"); - tf_printf("curl returned %d\n", WEXITSTATUS(r)); + *(int*)data = WEXITSTATUS(r); assert(WEXITSTATUS(r) == 0); + tf_printf("curl returned %d\n", WEXITSTATUS(r)); #endif } static void _test_http_handler(tf_http_request_t* request) { tf_printf("HANDLER %d\n", request->phase); + const char* headers[] = + { + "Connection", "close", + }; + tf_http_respond(request, 200, headers, 1, "Hello, world!", strlen("Hello, world!")); } static void _test_http(const tf_test_options_t* options) @@ -692,9 +698,14 @@ static void _test_http(const tf_test_options_t* options) tf_http_add_handler(http, NULL, _test_http_handler, NULL); tf_http_listen(http, 23456); + int result = -1; uv_thread_t thread = { 0 }; - uv_thread_create(&thread, _test_http_thread, NULL); - uv_run(&loop, UV_RUN_DEFAULT); + uv_thread_create(&thread, _test_http_thread, &result); + while (result == -1) + { + uv_run(&loop, UV_RUN_ONCE); + } + tf_printf("Done running.\n"); tf_http_destroy(http); uv_loop_close(&loop);