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)
* Fully decoupled HTTP loop functionnality
## [0.1.0](https://git.rustybever.be/Chewing_Bever/lander/src/tag/0.1.0)
### Added

View File

@ -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

View File

@ -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);

View File

@ -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");

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 *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);
}

View File

@ -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); }

View File

@ -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

View File

@ -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;

View File

@ -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);
}