From 6d4b94c55ee24697b01f111f6cf59d4b66c89276 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Thu, 25 May 2023 22:58:00 +0200 Subject: [PATCH] feat: inching our way towards http routing --- include/event_loop.h | 54 ++++++++++++++++++++++++ include/http.h | 27 ++++-------- include/http_req.h | 31 ++++++++++++++ src/event_loop/event_loop.c | 2 +- src/event_loop/event_loop_conn.c | 63 +++++++++------------------- src/event_loop/event_loop_internal.h | 57 ------------------------- src/http/http_parse.c | 22 ++++------ src/http/http_route.c | 5 +++ 8 files changed, 126 insertions(+), 135 deletions(-) create mode 100644 include/http_req.h delete mode 100644 src/event_loop/event_loop_internal.h create mode 100644 src/http/http_route.c diff --git a/include/event_loop.h b/include/event_loop.h index 4912e44..971addc 100644 --- a/include/event_loop.h +++ b/include/event_loop.h @@ -1,6 +1,12 @@ #ifndef LANDER_EVENT_LOOP #define LANDER_EVENT_LOOP +#include +#include +#include + +#include "http_req.h" + // Size of the read and write buffers for each connection, in bytes #define EVENT_LOOP_BUFFER_SIZE 1024 @@ -30,4 +36,52 @@ event_loop *event_loop_init(); */ void event_loop_run(event_loop *el, int port); +typedef struct event_loop_conn { + int fd; + event_loop_conn_state state; + // buffer for reading + size_t rbuf_size; + size_t rbuf_read; + uint8_t rbuf[EVENT_LOOP_BUFFER_SIZE]; + // buffer for writing + size_t wbuf_size; + size_t wbuf_sent; + uint8_t wbuf[EVENT_LOOP_BUFFER_SIZE]; + + // If true, the server will close the connection after the final write buffer + // has been written + bool close_after_write; + /* void (*process_func)(struct event_loop_conn *); */ + http_request req; +} event_loop_conn; + +/* + * Initialize a new event_loop_conn struct + */ +event_loop_conn *event_loop_conn_init(); + +typedef struct event_loop { + event_loop_conn **connections; + size_t connection_count; +} event_loop; + +/* + * Initialize a new event_loop struct + */ +event_loop *event_loop_init(); + +/* + * Place a new connection into the event loop's internal array. + * + * Returns -1 if the internal realloc failed + */ +int event_loop_put(event_loop *loop, event_loop_conn *conn); + +/** + * Accept a new connection for the given file descriptor. + */ +int event_loop_accept(event_loop *loop, int fd); + +void event_loop_conn_io(event_loop_conn *conn); + #endif diff --git a/include/http.h b/include/http.h index 06de7ca..bfe7a8c 100644 --- a/include/http.h +++ b/include/http.h @@ -3,31 +3,16 @@ #include +#include "picohttpparser.h" + +#include "event_loop.h" +#include "http_req.h" + extern const char http_404[]; extern const size_t http_404_len; extern const char http_500[]; extern const size_t http_500_len; -typedef enum http_request_entity { - event_loop_request_type_unknown = 0 -} http_request_entity; - -typedef enum http_request_method { - http_request_method_get = 0, - http_request_method_post = 1, - http_request_method_put = 2, - http_request_method_patch = 3, - http_request_method_delete = 4 -} http_request_method; - -/* - * Struct representing the specific type of request - */ -typedef struct http_request { - http_request_method type; - http_request_entity entity; -} http_request; - typedef enum http_parse_error { http_parse_error_ok = 0, http_parse_error_incomplete = 1, @@ -37,4 +22,6 @@ typedef enum http_parse_error { http_parse_error http_parse_request(http_request *req, const char *path, size_t path_len); +void http_route(event_loop_conn *conn); + #endif diff --git a/include/http_req.h b/include/http_req.h new file mode 100644 index 0000000..7069142 --- /dev/null +++ b/include/http_req.h @@ -0,0 +1,31 @@ +#ifndef HTTP_REQ +#define HTTP_REQ + +#include + +#include "picohttpparser.h" + +#define HTTP_MAX_ALLOWED_HEADERS 16 + +typedef enum http_request_method { + http_request_method_get = 0, + http_request_method_post = 1, + http_request_method_put = 2, + http_request_method_patch = 3, + http_request_method_delete = 4 +} http_request_method; + +/* + * Struct representing the specific type of request + */ +typedef struct http_request { + size_t len; + int minor_version; + const char *method; + size_t method_len; + const char *path; + size_t path_len; + struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS]; +} http_request; + +#endif diff --git a/src/event_loop/event_loop.c b/src/event_loop/event_loop.c index 3cf28ec..a58585a 100644 --- a/src/event_loop/event_loop.c +++ b/src/event_loop/event_loop.c @@ -10,7 +10,7 @@ #include #include -#include "event_loop_internal.h" +#include "event_loop.h" #include "log.h" #include "picohttpparser.h" diff --git a/src/event_loop/event_loop_conn.c b/src/event_loop/event_loop_conn.c index de5b6cb..745c496 100644 --- a/src/event_loop/event_loop_conn.c +++ b/src/event_loop/event_loop_conn.c @@ -11,9 +11,9 @@ #include #include "picohttpparser.h" -#include "http.h" -#include "event_loop_internal.h" +#include "event_loop.h" +#include "http.h" void event_loop_conn_io_res(event_loop_conn *conn) { while (1) { @@ -59,58 +59,34 @@ bool event_loop_handle_request(event_loop_conn *conn) { return false; } - /* if (conn->process_func != NULL) { */ - /* conn->process_func(conn); */ - /* } */ + http_parse_error res = + http_parse_request(&conn->req, (const char *)&conn->rbuf[conn->rbuf_read], + conn->rbuf_size - conn->rbuf_read); - const char *method, *path; - struct phr_header headers[16]; - size_t method_len, path_len, num_headers; - int minor_version; - - num_headers = sizeof(headers) / sizeof(headers[0]); - - int res = phr_parse_request((const char *)&conn->rbuf[conn->rbuf_read], conn->rbuf_size - conn->rbuf_read, - &method, &method_len, &path, &path_len, - &minor_version, headers, &num_headers, 0); - - if (res > 0) { - /* for (size_t i = 0; i < num_headers; i++) { */ - /* /1* printf("%.*s: ", headers[i].name_len, headers[i].name); *1/ */ - /* /1* printf("%.*s\n", headers[i].value_len, headers[i].value); *1/ */ - /* if (strncmp("Connection", headers[i].name, headers[i].name_len) == 0) { - */ - /* if (strncmp("close", headers[i].value, headers[i].value_len) == 0) { - */ - /* conn->close_after_write = true; */ - /* break; */ - /* } */ - /* } */ - /* } */ - - // Advance the read buffer - conn->rbuf_read += res; + if (res == http_parse_error_ok) { + conn->rbuf_read += conn->req.len; memcpy(conn->wbuf, http_404, http_404_len); - // Move the new request up to the front of the read buffer - /* memmove(conn->rbuf, &conn->rbuf[res], conn->rbuf_size - res); */ - /* conn->rbuf_size -= res; */ - conn->state = event_loop_conn_state_res; conn->wbuf_size = http_404_len; conn->wbuf_sent = 0; - event_loop_conn_io_res(conn); + /* event_loop_conn_io_res(conn); */ - /* // End the connection if the http request is invalid, or when the - * response */ - /* // is too large to fit into a single read buffer */ - } else if (res == -1 || - (res == -2 && conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) { + } + // Both in the case of an invalid HTTP request or one that's larger than the + // read buffer, we cannot determine when the next, possibly valid, HTTP + // request begins in the data stream. Therefore, we close the connection, + // even if additional pipelined requests are incoming. + else if (res == http_parse_error_invalid || + (res == http_parse_error_incomplete && + conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) { conn->state = event_loop_conn_state_end; } + // TODO in highly concurrent situations, it might actually be better to always + // return false here, as this allows cycling better through all connections return conn->state == event_loop_conn_state_req; } @@ -123,7 +99,8 @@ bool event_loop_handle_request(event_loop_conn *conn) { void event_loop_conn_io_req(event_loop_conn *conn) { do { // Move remaining data to start of buffer - memmove(conn->rbuf, &conn->rbuf[conn->rbuf_read], conn->rbuf_size - conn->rbuf_read); + memmove(conn->rbuf, &conn->rbuf[conn->rbuf_read], + conn->rbuf_size - conn->rbuf_read); conn->rbuf_size -= conn->rbuf_read; conn->rbuf_read = 0; diff --git a/src/event_loop/event_loop_internal.h b/src/event_loop/event_loop_internal.h deleted file mode 100644 index cf72eb3..0000000 --- a/src/event_loop/event_loop_internal.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef LANDER_EVENT_LOOP_INTERNAL -#define LANDER_EVENT_LOOP_INTERNAL - -#include -#include -#include - -#include "event_loop.h" - -typedef struct event_loop_conn { - int fd; - event_loop_conn_state state; - // buffer for reading - size_t rbuf_size; - size_t rbuf_read; - uint8_t rbuf[EVENT_LOOP_BUFFER_SIZE]; - // buffer for writing - size_t wbuf_size; - size_t wbuf_sent; - uint8_t wbuf[EVENT_LOOP_BUFFER_SIZE]; - - // If true, the server will close the connection after the final write buffer - // has been written - bool close_after_write; - /* void (*process_func)(struct event_loop_conn *); */ -} event_loop_conn; - -/* - * Initialize a new event_loop_conn struct - */ -event_loop_conn *event_loop_conn_init(); - -typedef struct event_loop { - event_loop_conn **connections; - size_t connection_count; -} event_loop; - -/* - * Initialize a new event_loop struct - */ -event_loop *event_loop_init(); - -/* - * Place a new connection into the event loop's internal array. - * - * Returns -1 if the internal realloc failed - */ -int event_loop_put(event_loop *loop, event_loop_conn *conn); - -/** - * Accept a new connection for the given file descriptor. - */ -int event_loop_accept(event_loop *loop, int fd); - -void event_loop_conn_io(event_loop_conn *conn); - -#endif diff --git a/src/http/http_parse.c b/src/http/http_parse.c index bdd1f46..a2b72d5 100644 --- a/src/http/http_parse.c +++ b/src/http/http_parse.c @@ -7,25 +7,19 @@ http_parse_error http_parse_request(http_request *req, const char *buf, size_t buf_size) { // First we try to parse the incoming HTTP request - const char *method, *path; - struct phr_header headers[16]; - size_t method_len, path_len, num_headers; - int minor_version; + size_t num_headers = HTTP_MAX_ALLOWED_HEADERS; - int res = - phr_parse_request(buf, buf_size, &method, &method_len, &path, &path_len, - &minor_version, headers, &num_headers, 0); + int res = phr_parse_request(buf, buf_size, &req->method, &req->method_len, + &req->path, &req->path_len, &req->minor_version, + req->headers, &num_headers, 0); - if (res == -2) { + if (res == -1) { + return http_parse_error_invalid; + } else if (res == -2) { return http_parse_error_incomplete; - } else if (res < 0) { - return http_parse_error_invalid; } - // Next, we parse the HTTP request as a lander-specific request - if (path_len == 0 || path[0] != '/') { - return http_parse_error_invalid; - } + req->len = res; return http_parse_error_ok; } diff --git a/src/http/http_route.c b/src/http/http_route.c new file mode 100644 index 0000000..b6765d9 --- /dev/null +++ b/src/http/http_route.c @@ -0,0 +1,5 @@ +#include "event_loop.h" + +typedef void (*routing_func)(event_loop_conn *); + +routing_func http_route();