feat(http): fully decouple HTTP loop functionality

lsm
Jef Roosens 2023-10-30 21:14:06 +01:00
parent fc4187e6ce
commit 6d74c8c550
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
9 changed files with 99 additions and 21 deletions

View File

@ -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) ## [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) ## [0.1.0](https://git.rustybever.be/Chewing_Bever/lander/src/tag/0.1.0)
### Added ### Added

View File

@ -52,9 +52,12 @@ typedef struct http_route {
typedef struct http_loop_gctx { typedef struct http_loop_gctx {
http_route *routes; http_route *routes;
size_t route_count; 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 *api_key;
const char *data_dir; // Custom global context
void *c;
} http_loop_gctx; } http_loop_gctx;
/** /**
@ -73,6 +76,7 @@ typedef struct http_loop_ctx {
http_route *route; http_route *route;
size_t current_step; size_t current_step;
http_loop_gctx *g; http_loop_gctx *g;
void *c;
} http_loop_ctx; } 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); 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 * Process incoming data as an HTTP request. This is the "handle_data" function
* for the event loop. * for the event loop.
@ -174,10 +183,27 @@ bool http_loop_step_switch_res(event_loop_conn *conn);
/** /**
* Initialize a new http loop. * 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 * @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. * 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 el the event loop
* @param port on what port to listen * @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 #endif

View File

@ -5,6 +5,23 @@
extern http_route lander_routes[4]; 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_index(event_loop_conn *conn);
bool lander_get_entry(event_loop_conn *conn); bool lander_get_entry(event_loop_conn *conn);

View File

@ -46,18 +46,32 @@ bool http_loop_handle_request(event_loop_conn *conn) {
return conn->state == event_loop_conn_state_req; 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(); event_loop *el = event_loop_init();
el->ctx_init = (void *(*)(void *))http_loop_ctx_init; el->ctx_init = (void *(*)(void *))http_loop_ctx_init;
el->ctx_free = (void (*)(void *))http_loop_ctx_free; el->ctx_free = (void (*)(void *))http_loop_ctx_free;
el->handle_data = http_loop_handle_request; el->handle_data = http_loop_handle_request;
el->write_data = http_loop_write_response; 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; el->gctx = gctx;
return el; 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) { void http_loop_run(event_loop *el, int port) {
debug("Compiling RegEx routes"); debug("Compiling RegEx routes");

View File

@ -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 *http_loop_ctx_init(http_loop_gctx *g) {
http_loop_ctx *ctx = calloc(sizeof(http_loop_ctx), 1); http_loop_ctx *ctx = calloc(sizeof(http_loop_ctx), 1);
ctx->g = g; ctx->g = g;
ctx->c = g->custom_ctx_init();
return ctx; return ctx;
} }
void http_loop_ctx_free(http_loop_ctx *ctx) { void http_loop_ctx_free(http_loop_ctx *ctx) {
http_loop_ctx_reset(ctx); http_loop_ctx_reset(ctx);
ctx->g->custom_ctx_free(ctx->c);
free(ctx); free(ctx);
} }
@ -45,4 +47,6 @@ void http_loop_ctx_reset(http_loop_ctx *ctx) {
ctx->res.status = 0; ctx->res.status = 0;
ctx->res.head_len = 0; ctx->res.head_len = 0;
ctx->res.head_written = 0; ctx->res.head_written = 0;
ctx->g->custom_ctx_reset(ctx->c);
} }

View File

@ -22,3 +22,11 @@ http_route lander_routes[] = {
.steps = {http_loop_step_auth, lander_post_paste, .steps = {http_loop_step_auth, lander_post_paste,
http_loop_step_body_to_file, http_loop_step_switch_res, NULL}}, 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); }

View File

@ -24,12 +24,13 @@ bool lander_get_index(event_loop_conn *conn) {
bool lander_get_entry(event_loop_conn *conn) { bool lander_get_entry(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx; 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]; 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; int key_len = ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so;
Entry *entry; 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) { if (res == NotFound) {
ctx->res.status = http_not_found; ctx->res.status = http_not_found;
@ -37,8 +38,8 @@ bool lander_get_entry(event_loop_conn *conn) {
ctx->res.status = http_moved_permanently; ctx->res.status = http_moved_permanently;
http_res_add_header(&ctx->res, http_header_location, entry->string, false); http_res_add_header(&ctx->res, http_header_location, entry->string, false);
} else if (entry->type == Paste) { } else if (entry->type == Paste) {
char fname[strlen(ctx->g->data_dir) + 8 + key_len + 1]; char fname[strlen(c_gctx->data_dir) + 8 + key_len + 1];
sprintf(fname, "%s/pastes/%.*s", ctx->g->data_dir, key_len, key); sprintf(fname, "%s/pastes/%.*s", c_gctx->data_dir, key_len, key);
http_res_set_body_file(&ctx->res, fname); http_res_set_body_file(&ctx->res, fname);
// TODO don't call everything a text file // TODO don't call everything a text file

View File

@ -5,6 +5,8 @@
// TODO entry leaks if key is already present // TODO entry leaks if key is already present
static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx, static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx,
Entry *entry, bool random) { Entry *entry, bool random) {
lander_gctx *c_gctx = ctx->g->c;
// The first match group matches the "long" path // The first match group matches the "long" path
bool secure = bool secure =
(ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so) == 1; (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; TrieExitCode res;
if (random) { 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) { if (res == Ok) {
key_len = strlen(key); 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 = (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; 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) { switch (res) {
@ -89,6 +91,8 @@ bool lander_post_redirect(event_loop_conn *conn) {
bool lander_post_paste(event_loop_conn *conn) { bool lander_post_paste(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx; http_loop_ctx *ctx = conn->ctx;
lander_gctx *c_gctx = ctx->g->c;
bool random = bool random =
ctx->req.regex_groups[2].rm_eo == ctx->req.regex_groups[2].rm_so; 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; return true;
} }
char *fname = malloc(strlen(ctx->g->data_dir) + 8 + key_len + 1); char *fname = malloc(strlen(c_gctx->data_dir) + 8 + key_len + 1);
sprintf(fname, "%s/pastes/%.*s", ctx->g->data_dir, key_len, key); sprintf(fname, "%s/pastes/%.*s", c_gctx->data_dir, key_len, key);
ctx->req.body.fname = fname; ctx->req.body.fname = fname;
ctx->req.body.fname_owned = true; ctx->req.body.fname_owned = true;

View File

@ -45,13 +45,15 @@ int main() {
info("Trie initialized and populated with %i entries", trie_size(trie)); info("Trie initialized and populated with %i entries", trie_size(trie));
http_loop_gctx *gctx = http_loop_gctx_init(); lander_gctx *c_gctx = lander_gctx_init();
gctx->trie = trie; c_gctx->data_dir = data_dir;
gctx->routes = lander_routes; c_gctx->trie = trie;
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);
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);
} }