From 6d74c8c5503bd197ae99f4cfdfda341a5e300863 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Mon, 30 Oct 2023 21:14:06 +0100 Subject: [PATCH] feat(http): fully decouple HTTP loop functionality --- CHANGELOG.md | 2 ++ include/http_loop.h | 36 ++++++++++++++++++++++++++++++----- include/lander.h | 17 +++++++++++++++++ src/http_loop/http_loop.c | 16 +++++++++++++++- src/http_loop/http_loop_ctx.c | 4 ++++ src/lander/lander.c | 8 ++++++++ src/lander/lander_get.c | 7 ++++--- src/lander/lander_post.c | 12 ++++++++---- src/main.c | 18 ++++++++++-------- 9 files changed, 99 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0709613..91784d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/Chewing_Bever/lander/src/branch/dev) +* Fully decoupled HTTP loop functionnality + ## [0.1.0](https://git.rustybever.be/Chewing_Bever/lander/src/tag/0.1.0) ### Added diff --git a/include/http_loop.h b/include/http_loop.h index b207d51..486ccbf 100644 --- a/include/http_loop.h +++ b/include/http_loop.h @@ -52,9 +52,12 @@ typedef struct http_route { typedef struct http_loop_gctx { http_route *routes; size_t route_count; - Trie *trie; + void *(*custom_ctx_init)(); + void (*custom_ctx_reset)(void *); + void (*custom_ctx_free)(void *); const char *api_key; - const char *data_dir; + // Custom global context + void *c; } http_loop_gctx; /** @@ -73,6 +76,7 @@ typedef struct http_loop_ctx { http_route *route; size_t current_step; http_loop_gctx *g; + void *c; } http_loop_ctx; /** @@ -98,6 +102,11 @@ void http_loop_ctx_reset(http_loop_ctx *ctx); */ void http_loop_ctx_free(http_loop_ctx *ctx); +/** + * Represents an HTTP loop + */ +typedef struct event_loop http_loop; + /** * Process incoming data as an HTTP request. This is the "handle_data" function * for the event loop. @@ -174,10 +183,27 @@ bool http_loop_step_switch_res(event_loop_conn *conn); /** * Initialize a new http loop. * - * @param gctx global context for the event loop + * @param routes array of routes that should be served + * @parma route_count how many elements are in `routes` + * @param custom_gctx the application's custom global context; can be NULL + * @param custom_ctx_init function to initialize a new custom context + * @param custom_ctx_reset function to reset a custom context + * @param custom_ctx_free function to free a custom context; will always be run + * after a reset * @return pointer to the newly allocated object */ -event_loop *http_loop_init(http_loop_gctx *gctx); +http_loop *http_loop_init(http_route *routes, size_t route_count, + void *custom_gctx, void *(*custom_ctx_init)(), + void(custom_ctx_reset)(void *), + void(custom_ctx_free)(void *)); + +/** + * Set the API key the authentication steps should use. + * + * @param hl HTTP loop to set key in + * @param api_key API key to use + */ +void http_loop_set_api_key(http_loop *hl, const char *api_key); /** * Run the HTTP loop. This function never returns. @@ -185,6 +211,6 @@ event_loop *http_loop_init(http_loop_gctx *gctx); * @param el the event loop * @param port on what port to listen */ -void http_loop_run(event_loop *el, int port); +void http_loop_run(http_loop *hl, int port); #endif diff --git a/include/lander.h b/include/lander.h index daf6fe7..f44870b 100644 --- a/include/lander.h +++ b/include/lander.h @@ -5,6 +5,23 @@ extern http_route lander_routes[4]; +typedef struct lander_gctx { + const char *data_dir; + Trie *trie; +} lander_gctx; + +typedef struct lander_ctx { + char *key; +} lander_ctx; + +void *lander_gctx_init(); + +void *lander_ctx_init(); + +void lander_ctx_reset(lander_ctx *ctx); + +void lander_ctx_free(lander_ctx *ctx); + bool lander_get_index(event_loop_conn *conn); bool lander_get_entry(event_loop_conn *conn); diff --git a/src/http_loop/http_loop.c b/src/http_loop/http_loop.c index e5c7d20..536f89d 100644 --- a/src/http_loop/http_loop.c +++ b/src/http_loop/http_loop.c @@ -46,18 +46,32 @@ bool http_loop_handle_request(event_loop_conn *conn) { return conn->state == event_loop_conn_state_req; } -event_loop *http_loop_init(http_loop_gctx *gctx) { +event_loop *http_loop_init(http_route *routes, size_t route_count, + void *custom_gctx, void *(*custom_ctx_init)(), + void(custom_ctx_reset)(), void(custom_ctx_free)()) { event_loop *el = event_loop_init(); el->ctx_init = (void *(*)(void *))http_loop_ctx_init; el->ctx_free = (void (*)(void *))http_loop_ctx_free; el->handle_data = http_loop_handle_request; el->write_data = http_loop_write_response; + + http_loop_gctx *gctx = http_loop_gctx_init(); + gctx->c = custom_gctx; + gctx->routes = routes; + gctx->route_count = route_count; + gctx->custom_ctx_init = custom_ctx_init; + gctx->custom_ctx_reset = custom_ctx_reset; + gctx->custom_ctx_free = custom_ctx_free; el->gctx = gctx; return el; } +void http_loop_set_api_key(http_loop *hl, const char *api_key) { + ((http_loop_gctx *)hl->gctx)->api_key = api_key; +} + void http_loop_run(event_loop *el, int port) { debug("Compiling RegEx routes"); diff --git a/src/http_loop/http_loop_ctx.c b/src/http_loop/http_loop_ctx.c index b820725..8d0db1e 100644 --- a/src/http_loop/http_loop_ctx.c +++ b/src/http_loop/http_loop_ctx.c @@ -12,12 +12,14 @@ http_loop_gctx *http_loop_gctx_init() { http_loop_ctx *http_loop_ctx_init(http_loop_gctx *g) { http_loop_ctx *ctx = calloc(sizeof(http_loop_ctx), 1); ctx->g = g; + ctx->c = g->custom_ctx_init(); return ctx; } void http_loop_ctx_free(http_loop_ctx *ctx) { http_loop_ctx_reset(ctx); + ctx->g->custom_ctx_free(ctx->c); free(ctx); } @@ -45,4 +47,6 @@ void http_loop_ctx_reset(http_loop_ctx *ctx) { ctx->res.status = 0; ctx->res.head_len = 0; ctx->res.head_written = 0; + + ctx->g->custom_ctx_reset(ctx->c); } diff --git a/src/lander/lander.c b/src/lander/lander.c index 60ee83d..9606326 100644 --- a/src/lander/lander.c +++ b/src/lander/lander.c @@ -22,3 +22,11 @@ http_route lander_routes[] = { .steps = {http_loop_step_auth, lander_post_paste, http_loop_step_body_to_file, http_loop_step_switch_res, NULL}}, }; + +void *lander_gctx_init() { return calloc(1, sizeof(lander_gctx)); } + +void *lander_ctx_init() { return calloc(1, sizeof(lander_ctx)); } + +void lander_ctx_reset(lander_ctx *ctx) {} + +void lander_ctx_free(lander_ctx *ctx) { free(ctx); } diff --git a/src/lander/lander_get.c b/src/lander/lander_get.c index e4ba39b..b139d09 100644 --- a/src/lander/lander_get.c +++ b/src/lander/lander_get.c @@ -24,12 +24,13 @@ bool lander_get_index(event_loop_conn *conn) { bool lander_get_entry(event_loop_conn *conn) { http_loop_ctx *ctx = conn->ctx; + lander_gctx *c_gctx = ctx->g->c; const char *key = &ctx->req.path[ctx->req.regex_groups[1].rm_so]; int key_len = ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so; Entry *entry; - TrieExitCode res = trie_search_len(ctx->g->trie, &entry, key, key_len); + TrieExitCode res = trie_search_len(c_gctx->trie, &entry, key, key_len); if (res == NotFound) { ctx->res.status = http_not_found; @@ -37,8 +38,8 @@ bool lander_get_entry(event_loop_conn *conn) { ctx->res.status = http_moved_permanently; http_res_add_header(&ctx->res, http_header_location, entry->string, false); } else if (entry->type == Paste) { - char fname[strlen(ctx->g->data_dir) + 8 + key_len + 1]; - sprintf(fname, "%s/pastes/%.*s", ctx->g->data_dir, key_len, key); + char fname[strlen(c_gctx->data_dir) + 8 + key_len + 1]; + sprintf(fname, "%s/pastes/%.*s", c_gctx->data_dir, key_len, key); http_res_set_body_file(&ctx->res, fname); // TODO don't call everything a text file diff --git a/src/lander/lander_post.c b/src/lander/lander_post.c index f1b686c..9288929 100644 --- a/src/lander/lander_post.c +++ b/src/lander/lander_post.c @@ -5,6 +5,8 @@ // TODO entry leaks if key is already present static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx, Entry *entry, bool random) { + lander_gctx *c_gctx = ctx->g->c; + // The first match group matches the "long" path bool secure = (ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so) == 1; @@ -14,7 +16,7 @@ static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx, TrieExitCode res; if (random) { - res = trie_add_random(ctx->g->trie, &key, entry, secure); + res = trie_add_random(c_gctx->trie, &key, entry, secure); if (res == Ok) { key_len = strlen(key); @@ -23,7 +25,7 @@ static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx, key = (char *)&ctx->req.path[ctx->req.regex_groups[2].rm_so]; key_len = ctx->req.regex_groups[2].rm_eo - ctx->req.regex_groups[2].rm_so; - res = trie_add_len(ctx->g->trie, key, key_len, entry); + res = trie_add_len(c_gctx->trie, key, key_len, entry); } switch (res) { @@ -89,6 +91,8 @@ bool lander_post_redirect(event_loop_conn *conn) { bool lander_post_paste(event_loop_conn *conn) { http_loop_ctx *ctx = conn->ctx; + lander_gctx *c_gctx = ctx->g->c; + bool random = ctx->req.regex_groups[2].rm_eo == ctx->req.regex_groups[2].rm_so; @@ -102,8 +106,8 @@ bool lander_post_paste(event_loop_conn *conn) { return true; } - char *fname = malloc(strlen(ctx->g->data_dir) + 8 + key_len + 1); - sprintf(fname, "%s/pastes/%.*s", ctx->g->data_dir, key_len, key); + char *fname = malloc(strlen(c_gctx->data_dir) + 8 + key_len + 1); + sprintf(fname, "%s/pastes/%.*s", c_gctx->data_dir, key_len, key); ctx->req.body.fname = fname; ctx->req.body.fname_owned = true; diff --git a/src/main.c b/src/main.c index f32f83f..cbefd01 100644 --- a/src/main.c +++ b/src/main.c @@ -45,13 +45,15 @@ int main() { info("Trie initialized and populated with %i entries", trie_size(trie)); - http_loop_gctx *gctx = http_loop_gctx_init(); - gctx->trie = trie; - gctx->routes = lander_routes; - gctx->route_count = sizeof(lander_routes) / sizeof(lander_routes[0]); - gctx->api_key = api_key; - gctx->data_dir = data_dir; - event_loop *el = http_loop_init(gctx); + lander_gctx *c_gctx = lander_gctx_init(); + c_gctx->data_dir = data_dir; + c_gctx->trie = trie; - http_loop_run(el, port); + http_loop *hl = http_loop_init( + lander_routes, sizeof(lander_routes) / sizeof(lander_routes[0]), c_gctx, + lander_ctx_init, (void (*)(void *))lander_ctx_reset, + (void (*)(void *))lander_ctx_free); + http_loop_set_api_key(hl, api_key); + + http_loop_run(hl, port); }