feat(http): fully decouple HTTP loop functionality
parent
fc4187e6ce
commit
6d74c8c550
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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); }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
18
src/main.c
18
src/main.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue