feat: abstract http body
							parent
							
								
									a6257a923d
								
							
						
					
					
						commit
						bbfea5876e
					
				|  | @ -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; | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ | |||
| #define LANDER_HTTP_TYPES | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <stdio.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| // 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 | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -0,0 +1,35 @@ | |||
| #include <stdlib.h> | ||||
| 
 | ||||
| #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); | ||||
| } | ||||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|     } | ||||
|   } | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue