From f1fd63335653843a80f51e7759329fa21219388a Mon Sep 17 00:00:00 2001 From: Francesco Cozzuto Date: Sat, 29 Nov 2025 21:28:56 +0100 Subject: [PATCH] Add Content-Length to HTTP requests --- chttp.c | 30 +++++++++++++++++++++++++++--- chttp.h | 10 ++++++++++ src/client.c | 29 ++++++++++++++++++++++++++--- src/client.h | 10 ++++++++++ 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/chttp.c b/chttp.c index b897a5b..6b62d3b 100644 --- a/chttp.c +++ b/chttp.c @@ -3945,6 +3945,22 @@ void http_request_builder_target(HTTP_RequestBuilder builder, } } + HTTP_String s; + + s = HTTP_STR("Connection: Close\r\n"); + byte_queue_write(&conn->output, s.ptr, s.len); + + s = HTTP_STR("Content-Length: "); + byte_queue_write(&conn->output, s.ptr, s.len); + + conn->content_length_value_offset = byte_queue_offset(&conn->output); + + #define TEN_SPACES " " + _Static_assert(sizeof(TEN_SPACES) == 10+1); + + s = HTTP_STR(TEN_SPACES "\r\n"); + byte_queue_write(&conn->output, s.ptr, s.len); + conn->state = HTTP_CLIENT_CONN_WAIT_HEADER; } @@ -3984,9 +4000,8 @@ void http_request_builder_body(HTTP_RequestBuilder builder, // Transition from WAIT_HEADER to WAIT_BODY if needed if (conn->state == HTTP_CLIENT_CONN_WAIT_HEADER) { - // End headers section - // TODO: add Content-Length header byte_queue_write(&conn->output, "\r\n", 2); + conn->content_length_offset = byte_queue_offset(&conn->output); conn->state = HTTP_CLIENT_CONN_WAIT_BODY; } @@ -4040,8 +4055,8 @@ int http_request_builder_send(HTTP_RequestBuilder builder) goto error; // Early completion due to an error if (conn->state == HTTP_CLIENT_CONN_WAIT_HEADER) { - // No body, just end headers byte_queue_write(&conn->output, "\r\n", 2); + conn->content_length_offset = byte_queue_offset(&conn->output); conn->state = HTTP_CLIENT_CONN_WAIT_BODY; } @@ -4051,6 +4066,14 @@ int http_request_builder_send(HTTP_RequestBuilder builder) if (byte_queue_error(&conn->output)) goto error; + int content_length = byte_queue_size_from_offset(&conn->output, conn->content_length_offset); + + char tmp[11]; + int len = snprintf(tmp, sizeof(tmp), "%d", content_length); + assert(len > 0 && len < 11); + + byte_queue_patch(&conn->output, conn->content_length_value_offset, tmp, len); + ConnectTarget target = url_to_connect_target(conn->url); bool secure = http_streq(conn->url.scheme, HTTP_STR("https")); if (socket_connect(&client->sockets, 1, &target, secure, conn->dont_verify_cert, conn) < 0) @@ -4263,6 +4286,7 @@ void http_client_process_events(HTTP_Client *client, // Decouple from the socket socket_set_user(&client->sockets, events[i].handle, NULL); + socket_close(&client->sockets, events[i].handle); // Push to the ready queue assert(client->num_ready < HTTP_CLIENT_CAPACITY); diff --git a/chttp.h b/chttp.h index a8cf645..e754153 100644 --- a/chttp.h +++ b/chttp.h @@ -943,6 +943,16 @@ typedef struct { // Parsed response once complete HTTP_Response response; + + // This offset points to the first byte that comes + // after the string "Content-Length: ". + ByteQueueOffset content_length_value_offset; + + // This one points to the first byte of the body. + // This allows calculating the length of the request + // content byte subtracting it from the offset reached + // when the request is marked as done. + ByteQueueOffset content_length_offset; } HTTP_ClientConn; // Fields of this struct are private diff --git a/src/client.c b/src/client.c index 389f89a..9513f4e 100644 --- a/src/client.c +++ b/src/client.c @@ -316,6 +316,22 @@ void http_request_builder_target(HTTP_RequestBuilder builder, } } + HTTP_String s; + + s = HTTP_STR("Connection: Close\r\n"); + byte_queue_write(&conn->output, s.ptr, s.len); + + s = HTTP_STR("Content-Length: "); + byte_queue_write(&conn->output, s.ptr, s.len); + + conn->content_length_value_offset = byte_queue_offset(&conn->output); + + #define TEN_SPACES " " + _Static_assert(sizeof(TEN_SPACES) == 10+1); + + s = HTTP_STR(TEN_SPACES "\r\n"); + byte_queue_write(&conn->output, s.ptr, s.len); + conn->state = HTTP_CLIENT_CONN_WAIT_HEADER; } @@ -355,9 +371,8 @@ void http_request_builder_body(HTTP_RequestBuilder builder, // Transition from WAIT_HEADER to WAIT_BODY if needed if (conn->state == HTTP_CLIENT_CONN_WAIT_HEADER) { - // End headers section - // TODO: add Content-Length header byte_queue_write(&conn->output, "\r\n", 2); + conn->content_length_offset = byte_queue_offset(&conn->output); conn->state = HTTP_CLIENT_CONN_WAIT_BODY; } @@ -411,8 +426,8 @@ int http_request_builder_send(HTTP_RequestBuilder builder) goto error; // Early completion due to an error if (conn->state == HTTP_CLIENT_CONN_WAIT_HEADER) { - // No body, just end headers byte_queue_write(&conn->output, "\r\n", 2); + conn->content_length_offset = byte_queue_offset(&conn->output); conn->state = HTTP_CLIENT_CONN_WAIT_BODY; } @@ -422,6 +437,14 @@ int http_request_builder_send(HTTP_RequestBuilder builder) if (byte_queue_error(&conn->output)) goto error; + int content_length = byte_queue_size_from_offset(&conn->output, conn->content_length_offset); + + char tmp[11]; + int len = snprintf(tmp, sizeof(tmp), "%d", content_length); + assert(len > 0 && len < 11); + + byte_queue_patch(&conn->output, conn->content_length_value_offset, tmp, len); + ConnectTarget target = url_to_connect_target(conn->url); bool secure = http_streq(conn->url.scheme, HTTP_STR("https")); if (socket_connect(&client->sockets, 1, &target, secure, conn->dont_verify_cert, conn) < 0) diff --git a/src/client.h b/src/client.h index b3b142e..71f2567 100644 --- a/src/client.h +++ b/src/client.h @@ -105,6 +105,16 @@ typedef struct { // Parsed response once complete HTTP_Response response; + + // This offset points to the first byte that comes + // after the string "Content-Length: ". + ByteQueueOffset content_length_value_offset; + + // This one points to the first byte of the body. + // This allows calculating the length of the request + // content byte subtracting it from the offset reached + // when the request is marked as done. + ByteQueueOffset content_length_offset; } HTTP_ClientConn; // Fields of this struct are private