From bbfea5876ecaf4b18dc24cf55d2db4d9c33b081a Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Wed, 31 May 2023 11:46:34 +0200 Subject: [PATCH] feat: abstract http body --- include/http/req.h | 9 +-------- include/http/res.h | 10 +-------- include/http/types.h | 24 ++++++++++++++++++++++ src/http/res.c | 10 ++++----- src/http/types.c | 35 ++++++++++++++++++++++++++++++++ src/http_loop/http_loop_ctx.c | 23 ++------------------- src/http_loop/http_loop_res.c | 18 ++++++++--------- src/http_loop/http_loop_steps.c | 36 ++++++++++++++++----------------- src/lander/lander_post.c | 10 ++++----- 9 files changed, 100 insertions(+), 75 deletions(-) create mode 100644 src/http/types.c diff --git a/include/http/req.h b/include/http/req.h index f0fad93..c7ead87 100644 --- a/include/http/req.h +++ b/include/http/req.h @@ -23,14 +23,7 @@ typedef struct http_request { size_t path_len; const char *query; size_t query_len; - http_body_type body_type; - union { - char *buf; - FILE *file; - } body; - size_t body_len; - size_t body_received; - char *body_file_name; + http_body body; regmatch_t regex_groups[HTTP_MAX_REGEX_GROUPS]; struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS]; size_t num_headers; diff --git a/include/http/res.h b/include/http/res.h index f7551c2..6552541 100644 --- a/include/http/res.h +++ b/include/http/res.h @@ -17,15 +17,7 @@ typedef struct http_response { const char *head; size_t head_len; size_t head_written; - http_body_type body_type; - union { - char *buf; - FILE *file; - } body; - size_t body_len; - size_t body_written; - // If false, the body won't be freed - bool owns_body; + http_body body; http_response_header headers[4]; size_t header_count; } http_response; diff --git a/include/http/types.h b/include/http/types.h index e1f4e64..27b3133 100644 --- a/include/http/types.h +++ b/include/http/types.h @@ -2,6 +2,8 @@ #define LANDER_HTTP_TYPES #include +#include +#include // Array mapping the http_request_method enum to strings extern const char *http_method_names[]; @@ -129,4 +131,26 @@ typedef enum http_body_type { http_body_file = 1 } http_body_type; +typedef struct http_body { + http_body_type type; + char *buf; + bool buf_owned; + FILE *file; + char *fname; + bool fname_owned; + // In the context of a request, expected_len is the content of the request's + // Content-Length header, and len is how many bytes have already been + // received. + // In the context of a response, expected_len is the actual length of the + // body, and len is how many have been written. + size_t expected_len; + size_t len; +} http_body; + +http_body *http_body_init(); + +void http_body_reset(http_body *body); + +void http_body_free(http_body *body); + #endif diff --git a/src/http/res.c b/src/http/res.c index 304a460..c3e8806 100644 --- a/src/http/res.c +++ b/src/http/res.c @@ -4,10 +4,10 @@ void http_res_set_body_buf(http_response *res, const char *body, size_t body_len, bool owned) { - res->body_type = http_body_buf; + res->body.type = http_body_buf; res->body.buf = (char *)body; - res->body_len = body_len; - res->owns_body = owned; + res->body.expected_len = body_len; + res->body.buf_owned = owned; } void http_res_set_body_file(http_response *res, const char *filename) { @@ -17,9 +17,9 @@ void http_res_set_body_file(http_response *res, const char *filename) { // TODO error handling FILE *f = fopen(filename, "r"); - res->body_type = http_body_file; + res->body.type = http_body_file; res->body.file = f; - res->body_len = st.st_size; + res->body.expected_len = st.st_size; } void http_res_add_header(http_response *res, http_header type, diff --git a/src/http/types.c b/src/http/types.c new file mode 100644 index 0000000..e2f3cfa --- /dev/null +++ b/src/http/types.c @@ -0,0 +1,35 @@ +#include + +#include "http/types.h" + +http_body *http_body_init() { + return calloc(sizeof(http_body), 1); +} + +void http_body_reset(http_body *body) { + if (body->type == http_body_file) { + fclose(body->file); + } + + if (body->buf_owned) { + free(body->buf); + } + + if (body->fname_owned) { + free(body->fname); + } + + body->type = 0; + body->buf = NULL; + body->buf_owned = false; + body->file = NULL; + body->fname = NULL; + body->fname_owned = false; + body->expected_len = 0; + body->len = 0; +} + +void http_body_free(http_body *body) { + http_body_reset(body); + free(body); +} diff --git a/src/http_loop/http_loop_ctx.c b/src/http_loop/http_loop_ctx.c index ea550e6..b820725 100644 --- a/src/http_loop/http_loop_ctx.c +++ b/src/http_loop/http_loop_ctx.c @@ -26,29 +26,13 @@ void http_loop_ctx_reset(http_loop_ctx *ctx) { ctx->route = NULL; ctx->current_step = 0; - if (ctx->req.body_type == http_body_buf && ctx->req.body.buf != NULL) { - free(ctx->req.body.buf); - ctx->req.body.buf = NULL; - } else if (ctx->req.body_type == http_body_file && - ctx->req.body.file != NULL) { - fclose(ctx->req.body.file); - ctx->req.body.file = NULL; - } - if (ctx->res.head != NULL) { free((void *)ctx->res.head); ctx->res.head = NULL; } - if (ctx->res.body_type == http_body_buf && ctx->res.body.buf != NULL && - ctx->res.owns_body) { - free(ctx->res.body.buf); - ctx->res.body.buf = NULL; - } else if (ctx->res.body_type == http_body_file && - ctx->res.body.file != NULL) { - fclose(ctx->res.body.file); - ctx->res.body.file = NULL; - } + http_body_reset(&ctx->req.body); + http_body_reset(&ctx->res.body); for (size_t i = 0; i < ctx->res.header_count; i++) { if (ctx->res.headers[i].owned) { @@ -61,7 +45,4 @@ void http_loop_ctx_reset(http_loop_ctx *ctx) { ctx->res.status = 0; ctx->res.head_len = 0; ctx->res.head_written = 0; - ctx->res.body_len = 0; - ctx->res.body_written = 0; - ctx->res.owns_body = false; } diff --git a/src/http_loop/http_loop_res.c b/src/http_loop/http_loop_res.c index 50d9c25..d2ae029 100644 --- a/src/http_loop/http_loop_res.c +++ b/src/http_loop/http_loop_res.c @@ -21,7 +21,7 @@ void http_loop_init_header(http_response *res) { // First we calculate the size of the start of the header int buf_size = snprintf(NULL, 0, http_response_format, res->status, - response_type_name, res->body_len); + response_type_name, res->body.expected_len); // We add each header's required size for (size_t i = 0; i < res->header_count; i++) { @@ -34,7 +34,7 @@ void http_loop_init_header(http_response *res) { // with the required final newline char *buf = malloc(buf_size + 1); buf_size = sprintf(buf, http_response_format, res->status, response_type_name, - res->body_len); + res->body.expected_len); for (size_t i = 0; i < res->header_count; i++) { buf_size += @@ -59,7 +59,7 @@ void http_loop_write_response(event_loop_conn *conn) { // The final iteration marks the end of the response, after which we reset the // context so a next request can be processed if (res->head_written == res->head_len && - res->body_written == res->body_len) { + res->body.expected_len == res->body.len) { http_loop_ctx_reset(conn->ctx); conn->state = event_loop_conn_state_req; return; @@ -75,23 +75,23 @@ void http_loop_write_response(event_loop_conn *conn) { res->head_written += bytes_to_write; } - if (res->body_written < res->body_len) { - size_t bytes_to_write = MIN(res->body_len - res->body_written, + if (res->body.len < res->body.expected_len) { + size_t bytes_to_write = MIN(res->body.expected_len - res->body.len, EVENT_LOOP_BUFFER_SIZE - conn->wbuf_size); size_t bytes_written; - switch (res->body_type) { + switch (res->body.type) { case http_body_buf: - memcpy(&conn->wbuf[conn->wbuf_size], &(res->body.buf)[res->body_written], + memcpy(&conn->wbuf[conn->wbuf_size], &(res->body.buf)[res->body.len], bytes_to_write); conn->wbuf_size += bytes_to_write; - res->body_written += bytes_to_write; + res->body.len += bytes_to_write; break; case http_body_file: bytes_written = fread(&conn->wbuf[conn->wbuf_size], sizeof(uint8_t), bytes_to_write, res->body.file); conn->wbuf_size += bytes_written; - res->body_written += bytes_written; + res->body.len += bytes_written; break; } } diff --git a/src/http_loop/http_loop_steps.c b/src/http_loop/http_loop_steps.c index f12dfea..e5d3436 100644 --- a/src/http_loop/http_loop_steps.c +++ b/src/http_loop/http_loop_steps.c @@ -36,7 +36,7 @@ static bool try_parse_content_length(event_loop_conn *conn) { if (strncmp(header->name, "Content-Length", header->name_len) == 0) { // If the content length header is present but contains an invalid // number, we return a bad request error - if (!string_to_num(&ctx->req.body_len, header->value, + if (!string_to_num(&ctx->req.body.expected_len, header->value, header->value_len)) { ctx->res.status = http_bad_request; conn->state = event_loop_conn_state_res; @@ -44,14 +44,14 @@ static bool try_parse_content_length(event_loop_conn *conn) { return false; } // The content length was actually 0, so we can instantly return here - else if (ctx->req.body_len == 0) { + else if (ctx->req.body.expected_len == 0) { return false; } } } // A zero here means there's no content length header - if (ctx->req.body_len == 0) { + if (ctx->req.body.expected_len == 0) { ctx->res.status = http_length_required; conn->state = event_loop_conn_state_res; @@ -64,47 +64,47 @@ static bool try_parse_content_length(event_loop_conn *conn) { bool http_loop_step_body_to_buf(event_loop_conn *conn) { http_loop_ctx *ctx = conn->ctx; - if (ctx->req.body_len == 0) { + if (ctx->req.body.expected_len == 0) { if (!try_parse_content_length(conn)) { return true; } - ctx->req.body_type = http_body_buf; - ctx->req.body.buf = malloc(ctx->req.body_len * sizeof(uint8_t)); - ctx->req.body_received = 0; + ctx->req.body.type = http_body_buf; + ctx->req.body.buf = malloc(ctx->req.body.expected_len * sizeof(uint8_t)); + ctx->req.body.len = 0; } size_t bytes_to_copy = MIN(conn->rbuf_size - conn->rbuf_read, - ctx->req.body_len - ctx->req.body_received); - memcpy(&ctx->req.body.buf[ctx->req.body_received], + ctx->req.body.expected_len - ctx->req.body.len); + memcpy(&ctx->req.body.buf[ctx->req.body.len], &conn->rbuf[conn->rbuf_read], bytes_to_copy); - ctx->req.body_received += bytes_to_copy; + ctx->req.body.len += bytes_to_copy; conn->rbuf_read += bytes_to_copy; - return ctx->req.body_received == ctx->req.body_len; + return ctx->req.body.len == ctx->req.body.expected_len; } bool http_loop_step_body_to_file(event_loop_conn *conn) { http_loop_ctx *ctx = conn->ctx; - if (ctx->req.body_len == 0) { + if (ctx->req.body.expected_len == 0) { if (!try_parse_content_length(conn)) { return true; } - ctx->req.body_type = http_body_file; - ctx->req.body.file = fopen(ctx->req.body_file_name, "wb"); - ctx->req.body_received = 0; + ctx->req.body.type = http_body_file; + ctx->req.body.file = fopen(ctx->req.body.fname, "wb"); + ctx->req.body.len = 0; } size_t bytes_to_write = MIN(conn->rbuf_size - conn->rbuf_read, - ctx->req.body_len - ctx->req.body_received); + ctx->req.body.expected_len - ctx->req.body.len); size_t bytes_written = fwrite(&conn->rbuf[conn->rbuf_read], sizeof(uint8_t), bytes_to_write, ctx->req.body.file); - ctx->req.body_received += bytes_written; + ctx->req.body.len += bytes_written; conn->rbuf_read += bytes_written; - return ctx->req.body_received == ctx->req.body_len; + return ctx->req.body.len == ctx->req.body.expected_len; } bool http_loop_step_auth(event_loop_conn *conn) { diff --git a/src/lander/lander_post.c b/src/lander/lander_post.c index a958982..bb00e14 100644 --- a/src/lander/lander_post.c +++ b/src/lander/lander_post.c @@ -63,9 +63,9 @@ bool lander_post_redirect(event_loop_conn *conn) { ctx->req.regex_groups[2].rm_eo == ctx->req.regex_groups[2].rm_so; // Allocate a new buffer to pass to the trie - char *url = malloc(ctx->req.body_len + 1); - memcpy(url, ctx->req.body.buf, ctx->req.body_len); - url[ctx->req.body_len] = '\0'; + char *url = malloc(ctx->req.body.len + 1); + memcpy(url, ctx->req.body.buf, ctx->req.body.len); + url[ctx->req.body.len] = '\0'; Entry *new_entry = entry_new(Redirect, url); @@ -101,11 +101,11 @@ bool lander_post_paste(event_loop_conn *conn) { return true; } - // TODO free this char *fname = malloc(8 + key_len); sprintf(fname, "pastes/%.*s", key_len, key); - ctx->req.body_file_name = fname; + ctx->req.body.fname = fname; + ctx->req.body.fname_owned = true; if (random) { free(key);