From e29e02ff85365294047f7d83d9ca5fdaabbe1cde Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Fri, 23 Feb 2024 12:15:43 +0100 Subject: [PATCH] feat(routing): integrate new router into framework --- example/blocking.c | 12 ++- include/lnm/http/loop.h | 100 +++++++++++++----------- include/lnm/http/router.h | 55 ------------- src/_include/lnm/http/loop_internal.h | 54 +++++++------ src/_include/lnm/http/router_internal.h | 37 --------- src/http/lnm_http_loop.c | 89 +-------------------- src/http/lnm_http_loop_process.c | 55 +++---------- src/http/lnm_http_route.c | 94 ++++++++++++++++++++++ src/http/lnm_http_router.c | 69 +--------------- 9 files changed, 201 insertions(+), 364 deletions(-) delete mode 100644 include/lnm/http/router.h delete mode 100644 src/_include/lnm/http/router_internal.h create mode 100644 src/http/lnm_http_route.c diff --git a/example/blocking.c b/example/blocking.c index a145ed5..e4324e3 100644 --- a/example/blocking.c +++ b/example/blocking.c @@ -21,15 +21,19 @@ lnm_http_step_err slow_step(lnm_http_conn *conn) { int main() { lnm_http_loop *hl; lnm_http_step *step = NULL; - lnm_http_route *route; lnm_http_loop_init(&hl, NULL, ctx_init, ctx_reset, ctx_free); - lnm_http_step_append(&step, slow_step, true); - lnm_http_route_init_literal(&route, lnm_http_method_get, "/", step); - lnm_http_loop_route_add(hl, route); + lnm_http_router *router; + lnm_http_router_init(&router); + + lnm_http_route *route; + lnm_http_router_add(&route, router, lnm_http_method_get, "/"); + lnm_http_route_step_append(route, slow_step, true); + + lnm_http_loop_router_set(hl, router); lnm_log_init_global(); lnm_log_register_stdout(lnm_log_level_debug); diff --git a/include/lnm/http/loop.h b/include/lnm/http/loop.h index fefdf6a..892723a 100644 --- a/include/lnm/http/loop.h +++ b/include/lnm/http/loop.h @@ -7,6 +7,8 @@ #include "lnm/http/req.h" #include "lnm/http/res.h" +#define LNM_HTTP_MAX_KEY_SEGMENTS 4 + typedef enum lnm_http_step_err { lnm_http_step_err_done = 0, lnm_http_step_err_io_needed, @@ -22,6 +24,55 @@ typedef void (*lnm_http_ctx_reset_fn)(void *c_ctx); typedef void (*lnm_http_ctx_free_fn)(void *c_ctx); +typedef struct lnm_http_route lnm_http_route; + +typedef struct lnm_http_route_match_segment { + size_t start; + size_t len; +} lnm_http_route_match_segment; + +typedef struct lnm_http_route_match { + const lnm_http_route *route; + lnm_http_method method; + lnm_http_route_match_segment key_segments[LNM_HTTP_MAX_KEY_SEGMENTS]; +} lnm_http_route_match; + +typedef struct lnm_http_router lnm_http_router; + +typedef enum lnm_http_route_err { + lnm_http_route_err_match = 0, + lnm_http_route_err_unknown_route = 1, + lnm_http_route_err_unknown_method = 2, +} lnm_http_route_err; + +/** + * Allocate and initialize a new http_router. + */ +lnm_err lnm_http_router_init(lnm_http_router **out); + +void lnm_http_router_free(lnm_http_router *router); + +lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router, + lnm_http_method method, const char *path); + +/** + * Add all of the child router's routes to the parent router, under the given + * route prefix. + */ +lnm_err lnm_http_router_nest(lnm_http_router *parent, + const lnm_http_router *child, const char *prefix); + +lnm_http_route_err lnm_http_router_route(lnm_http_route_match *out, + const lnm_http_router *router, + lnm_http_method method, + const char *path); + +const lnm_http_route_match_segment * +lnm_http_route_match_get(lnm_http_route_match *match, const char *key); + +lnm_err lnm_http_route_step_append(lnm_http_route *route, lnm_http_step_fn fn, + bool blocking); + /** * Initialize a new `lnm_http_loop`. * @@ -32,47 +83,7 @@ lnm_err lnm_http_loop_init(lnm_http_loop **out, void *c_gctx, lnm_http_ctx_reset_fn ctx_reset, lnm_http_ctx_free_fn ctx_free); -/** - * Append the given step fn to the step. - * - * @param out both the previous step to append the new step to, and the output - * variable to which the new step is appended - * @param fn step function - * @param blocking whether the step is blocking or not - */ -lnm_err lnm_http_step_append(lnm_http_step **out, lnm_http_step_fn fn, - bool blocking); - -/** - * Initialize a new route of type literal. - * - * @param out where to store pointer to new `lnm_http_route` - * @param path literal path to match - * @param step step to process request with - */ -lnm_err lnm_http_route_init_literal(lnm_http_route **out, - lnm_http_method method, const char *path, - lnm_http_step *step); - -/** - * Initialize a new route of type regex. - * - * @param out where to store pointer to new `lnm_http_route` - * @param pattern regex pattern - * @param regex_group_count how many regex groups are contained in the pattern - * @param step step to process request with - */ -lnm_err lnm_http_route_init_regex(lnm_http_route **out, lnm_http_method method, - const char *pattern, int regex_group_count, - lnm_http_step *step); - -/** - * Add a new route to the HTTP route. - * - * @param hl HTTP loop to modify - * @param route route to add - */ -lnm_err lnm_http_loop_route_add(lnm_http_loop *hl, lnm_http_route *route); +void lnm_http_loop_router_set(lnm_http_loop *hl, lnm_http_router *router); lnm_err lnm_http_loop_run(lnm_http_loop *hl, uint16_t port, size_t epoll_threads, size_t worker_threads); @@ -106,10 +117,7 @@ typedef enum lnm_http_loop_state { } lnm_http_loop_state; typedef struct lnm_http_loop_gctx { - struct { - lnm_http_route **arr; - size_t len; - } routes; + lnm_http_router *router; lnm_http_ctx_init_fn ctx_init; lnm_http_ctx_reset_fn ctx_reset; lnm_http_ctx_free_fn ctx_free; @@ -122,7 +130,7 @@ typedef struct lnm_http_loop_ctx { lnm_http_loop_state state; lnm_http_req req; lnm_http_res res; - lnm_http_route *route; + const lnm_http_route *route; lnm_http_step *cur_step; lnm_http_loop_gctx *g; void *c; diff --git a/include/lnm/http/router.h b/include/lnm/http/router.h deleted file mode 100644 index f2ed321..0000000 --- a/include/lnm/http/router.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef LNM_HTTP_ROUTER -#define LNM_HTTP_ROUTER - -#define LNM_HTTP_MAX_KEY_SEGMENTS 4 - -#include "lnm/common.h" -#include "lnm/http/consts.h" - -typedef struct lnm_http_route lnm_http_route; - -typedef struct lnm_http_route_match_segment { - size_t start; - size_t len; -} lnm_http_route_match_segment; - -typedef struct lnm_http_route_match { - const lnm_http_route *route; - lnm_http_method method; - lnm_http_route_match_segment key_segments[LNM_HTTP_MAX_KEY_SEGMENTS]; -} lnm_http_route_match; - -typedef struct lnm_http_router lnm_http_router; - -typedef enum lnm_http_route_err { - lnm_http_route_err_match = 0, - lnm_http_route_err_unknown_route = 1, - lnm_http_route_err_unknown_method = 2, -} lnm_http_route_err; - -/** - * Allocate and initialize a new http_router. - */ -lnm_err lnm_http_router_init(lnm_http_router **out); - -void lnm_http_router_free(lnm_http_router *router); - -lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router, - lnm_http_method method, const char *path); - -/** - * Add all of the child router's routes to the parent router, under the given - * route prefix. - */ -lnm_err lnm_http_router_nest(lnm_http_router *parent, - const lnm_http_router *child, const char *prefix); - -lnm_http_route_err lnm_http_router_route(lnm_http_route_match *out, - const lnm_http_router *router, - lnm_http_method method, - const char *path); - -const lnm_http_route_match_segment * -lnm_http_route_match_get(lnm_http_route_match *match, const char *key); - -#endif diff --git a/src/_include/lnm/http/loop_internal.h b/src/_include/lnm/http/loop_internal.h index a343de6..2a8c0ec 100644 --- a/src/_include/lnm/http/loop_internal.h +++ b/src/_include/lnm/http/loop_internal.h @@ -5,42 +5,48 @@ #include "lnm/http/loop.h" +struct lnm_http_router { + struct lnm_http_router *exact_children[128]; + struct lnm_http_router *single_segment_child; + lnm_http_route *routes[lnm_http_method_total]; + bool represents_route; +}; + +typedef struct lnm_http_route_segment_trie { + struct lnm_http_route_segment_trie *children[128]; + size_t index; + bool represents_segment; +} lnm_http_route_segment_trie; + +lnm_err lnm_http_route_segment_trie_init(lnm_http_route_segment_trie **out); + +void lnm_http_route_segment_trie_free(lnm_http_route_segment_trie *trie); + +lnm_err lnm_http_route_key_segment_insert(lnm_http_route *route, + const char *key, size_t key_len, + size_t index); typedef struct lnm_http_step { lnm_http_step_fn fn; struct lnm_http_step *next; bool blocking; } lnm_http_step; -typedef enum lnm_http_route_type { - lnm_http_route_type_literal = 0, - lnm_http_route_type_regex, -} lnm_http_route_type; - -typedef struct lnm_http_route { - union { - regex_t *regex; - const char *s; - } route; - lnm_http_method method; - lnm_http_route_type type; - int regex_group_count; - lnm_http_step *step; -} lnm_http_route; - -/** - * Initialize a new empty route. - * - * @param out where to store pointer to new `lnm_http_route` - */ -lnm_err lnm_http_route_init(lnm_http_route **out); - /** * Initialize a first step. * * @param out where to store pointer to new `lnm_http_step` * @param fn step function associated with the step */ -lnm_err lnm_http_step_init(lnm_http_step **out, lnm_http_step_fn fn); +lnm_err lnm_http_step_init(lnm_http_step **out); + +struct lnm_http_route { + lnm_http_route_segment_trie *key_segments; + lnm_http_step *step; +}; + +lnm_err lnm_http_route_init(lnm_http_route **out); + +void lnm_http_route_free(lnm_http_route *route); /** * Initialize a new global context object. diff --git a/src/_include/lnm/http/router_internal.h b/src/_include/lnm/http/router_internal.h deleted file mode 100644 index 6c402d3..0000000 --- a/src/_include/lnm/http/router_internal.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef LNM_HTTP_ROUTER_INTERNAL -#define LNM_HTTP_ROUTER_INTERNAL - -#include "lnm/common.h" -#include "lnm/http/consts.h" -#include "lnm/http/router.h" - -typedef struct lnm_http_route_segment_trie { - struct lnm_http_route_segment_trie *children[128]; - size_t index; - bool represents_segment; -} lnm_http_route_segment_trie; - -lnm_err lnm_http_route_segment_trie_init(lnm_http_route_segment_trie **out); - -void lnm_http_route_segment_trie_free(lnm_http_route_segment_trie *trie); - -lnm_err lnm_http_route_key_segment_insert(lnm_http_route *route, - const char *key, size_t key_len, - size_t index); - -struct lnm_http_route { - lnm_http_route_segment_trie *key_segments; -}; - -struct lnm_http_router { - struct lnm_http_router *exact_children[128]; - struct lnm_http_router *single_segment_child; - lnm_http_route *routes[lnm_http_method_total]; - bool represents_route; -}; - -lnm_err lnm_http_route_init(lnm_http_route **out); - -void lnm_http_route_free(lnm_http_route *route); - -#endif diff --git a/src/http/lnm_http_loop.c b/src/http/lnm_http_loop.c index bd83526..7b31e80 100644 --- a/src/http/lnm_http_loop.c +++ b/src/http/lnm_http_loop.c @@ -28,94 +28,9 @@ lnm_err lnm_http_loop_init(lnm_http_loop **out, void *c_gctx, return lnm_err_ok; } -lnm_err lnm_http_step_append(lnm_http_step **out, lnm_http_step_fn fn, - bool blocking) { - lnm_http_step *step = calloc(1, sizeof(lnm_http_step)); - - if (step == NULL) { - return lnm_err_failed_alloc; - } - - step->fn = fn; - step->blocking = blocking; - - if ((*out) != NULL) { - (*out)->next = step; - } - - *out = step; - - return lnm_err_ok; -} - -lnm_err lnm_http_route_init(lnm_http_route **out) { - lnm_http_route *route = calloc(1, sizeof(lnm_http_route)); - - if (route == NULL) { - return lnm_err_failed_alloc; - } - - *out = route; - - return lnm_err_ok; -} - -lnm_err lnm_http_route_init_literal(lnm_http_route **out, - lnm_http_method method, const char *path, - lnm_http_step *step) { - LNM_RES(lnm_http_route_init(out)); - - (*out)->type = lnm_http_route_type_literal; - (*out)->method = method; - (*out)->route.s = path; - (*out)->step = step; - - return lnm_err_ok; -} - -lnm_err lnm_http_route_init_regex(lnm_http_route **out, lnm_http_method method, - const char *pattern, int regex_group_count, - lnm_http_step *step) { - regex_t *regex = calloc(1, sizeof(regex_t)); - - if (regex == NULL) { - return lnm_err_failed_alloc; - } - - if (regcomp(regex, pattern, REG_EXTENDED) != 0) { - free(regex); - return lnm_err_bad_regex; - } - - LNM_RES2(lnm_http_route_init(out), free(regex)); - - (*out)->method = method; - (*out)->type = lnm_http_route_type_regex; - (*out)->route.regex = regex; - (*out)->regex_group_count = regex_group_count; - (*out)->step = step; - - return lnm_err_ok; -} - -lnm_err lnm_http_loop_route_add(lnm_http_loop *hl, lnm_http_route *route) { +void lnm_http_loop_router_set(lnm_http_loop *hl, lnm_http_router *router) { lnm_http_loop_gctx *gctx = hl->gctx; - - lnm_http_route **new_routes = - gctx->routes.len > 0 - ? realloc(gctx->routes.arr, - (gctx->routes.len + 1) * sizeof(lnm_http_route *)) - : malloc(sizeof(lnm_http_route *)); - - if (new_routes == NULL) { - return lnm_err_failed_alloc; - } - - new_routes[gctx->routes.len] = route; - gctx->routes.arr = new_routes; - gctx->routes.len++; - - return lnm_err_ok; + gctx->router = router; } lnm_err lnm_http_loop_run(lnm_http_loop *hl, uint16_t port, diff --git a/src/http/lnm_http_loop_process.c b/src/http/lnm_http_loop_process.c index 1e465cf..e6a405e 100644 --- a/src/http/lnm_http_loop_process.c +++ b/src/http/lnm_http_loop_process.c @@ -56,55 +56,24 @@ void lnm_http_loop_process_parse_req(lnm_http_conn *conn) { void lnm_http_loop_process_route(lnm_http_conn *conn) { lnm_http_loop_ctx *ctx = conn->ctx; - lnm_http_loop_gctx *gctx = ctx->g; + const lnm_http_loop_gctx *gctx = ctx->g; - // 0: no match - // 1: matched route, but not method - // 2: fully matched route - int match_level = 0; - lnm_http_route *route; + lnm_http_route_match match; - for (size_t i = 0; i < gctx->routes.len && match_level < 3; i++) { - route = gctx->routes.arr[i]; - bool matched_path = false; - - switch (route->type) { - case lnm_http_route_type_literal: - matched_path = strncmp(route->route.s, ctx->req.buf.s + ctx->req.path.o, - ctx->req.path.len) == 0; - break; - case lnm_http_route_type_regex: - matched_path = - regexec(route->route.regex, ctx->req.buf.s + ctx->req.path.o, - LNM_HTTP_MAX_REGEX_GROUPS, ctx->req.path.groups, 0) == 0; - break; - } - - // GET routes also automatically route HEAD requests - bool matched_method = route->method == ctx->req.method || - (route->method == lnm_http_method_get && - ctx->req.method == lnm_http_method_head); - int new_match_level = 2 * matched_path + matched_method; - - // Remember the previous match levels so we can return the correct status - // message - match_level = match_level < new_match_level ? new_match_level : match_level; - } - - switch (match_level) { - case 0: - case 1: - ctx->res.status = lnm_http_status_not_found; - ctx->state = lnm_http_loop_state_first_res; + switch (lnm_http_router_route(&match, gctx->router, ctx->req.method, + ctx->req.buf.s + ctx->req.path.o)) { + case lnm_http_route_err_match: + ctx->route = match.route; + ctx->cur_step = match.route->step; + ctx->state = lnm_http_loop_state_parse_headers; break; - case 2: + case lnm_http_route_err_unknown_method: ctx->res.status = lnm_http_status_method_not_allowed; ctx->state = lnm_http_loop_state_first_res; break; - case 3: - ctx->route = route; - ctx->cur_step = route->step; - ctx->state = lnm_http_loop_state_parse_headers; + case lnm_http_route_err_unknown_route: + ctx->res.status = lnm_http_status_not_found; + ctx->state = lnm_http_loop_state_first_res; break; } } diff --git a/src/http/lnm_http_route.c b/src/http/lnm_http_route.c new file mode 100644 index 0000000..231a89d --- /dev/null +++ b/src/http/lnm_http_route.c @@ -0,0 +1,94 @@ +#include "lnm/http/loop_internal.h" + +lnm_err lnm_http_route_init(lnm_http_route **out) { + lnm_http_route *route = calloc(1, sizeof(lnm_http_route)); + + if (route == NULL) { + return lnm_err_failed_alloc; + } + + *out = route; + + return lnm_err_ok; +} + +lnm_err lnm_http_route_segment_trie_init(lnm_http_route_segment_trie **out) { + lnm_http_route_segment_trie *trie = + calloc(1, sizeof(lnm_http_route_segment_trie)); + + if (trie == NULL) { + return lnm_err_failed_alloc; + } + + *out = trie; + + return lnm_err_ok; +} + +void lnm_http_route_segment_trie_free(lnm_http_route_segment_trie *trie) { + if (trie == NULL) { + return; + } + + for (size_t i = 0; i < 128; i++) { + lnm_http_route_segment_trie_free(trie->children[i]); + } + + free(trie); +} + +lnm_err lnm_http_route_key_segment_insert(lnm_http_route *route, + const char *key, size_t key_len, + size_t index) { + if (route->key_segments == NULL) { + LNM_RES(lnm_http_route_segment_trie_init(&route->key_segments)); + } + + lnm_http_route_segment_trie *trie = route->key_segments; + + for (size_t key_index = 0; key_index < key_len; key_index++) { + unsigned char c = key[key_index]; + + if (trie->children[c] == NULL) { + LNM_RES(lnm_http_route_segment_trie_init(&trie->children[c])); + } + + trie = trie->children[c]; + } + + if (trie->represents_segment) { + return lnm_err_already_present; + } + + trie->represents_segment = true; + trie->index = index; + + return lnm_err_ok; +} + +lnm_err lnm_http_step_init(lnm_http_step **out) { + lnm_http_step *step = calloc(1, sizeof(lnm_http_step)); + + if (step == NULL) { + return lnm_err_failed_alloc; + } + + *out = step; + + return lnm_err_ok; +} + +lnm_err lnm_http_route_step_append(lnm_http_route *route, lnm_http_step_fn fn, + bool blocking) { + lnm_http_step **step_ptr = &route->step; + + while (*step_ptr != NULL) { + step_ptr = &(*step_ptr)->next; + } + + LNM_RES(lnm_http_step_init(step_ptr)); + (*step_ptr)->fn = fn; + (*step_ptr)->blocking = blocking; + + return lnm_err_ok; +} diff --git a/src/http/lnm_http_router.c b/src/http/lnm_http_router.c index 3dcc857..dddf882 100644 --- a/src/http/lnm_http_router.c +++ b/src/http/lnm_http_router.c @@ -1,8 +1,7 @@ #include #include "lnm/common.h" -#include "lnm/http/router.h" -#include "lnm/http/router_internal.h" +#include "lnm/http/loop_internal.h" lnm_err lnm_http_router_init(lnm_http_router **out) { lnm_http_router *router = calloc(1, sizeof(lnm_http_router)); @@ -16,18 +15,6 @@ lnm_err lnm_http_router_init(lnm_http_router **out) { return lnm_err_ok; } -lnm_err lnm_http_route_init(lnm_http_route **out) { - lnm_http_route *route = calloc(1, sizeof(lnm_http_route)); - - if (route == NULL) { - return lnm_err_failed_alloc; - } - - *out = route; - - return lnm_err_ok; -} - void lnm_http_route_free(lnm_http_route *route) { if (route == NULL) { return; @@ -37,60 +24,6 @@ void lnm_http_route_free(lnm_http_route *route) { free(route); } -lnm_err lnm_http_route_segment_trie_init(lnm_http_route_segment_trie **out) { - lnm_http_route_segment_trie *trie = - calloc(1, sizeof(lnm_http_route_segment_trie)); - - if (trie == NULL) { - return lnm_err_failed_alloc; - } - - *out = trie; - - return lnm_err_ok; -} - -void lnm_http_route_segment_trie_free(lnm_http_route_segment_trie *trie) { - if (trie == NULL) { - return; - } - - for (size_t i = 0; i < 128; i++) { - lnm_http_route_segment_trie_free(trie->children[i]); - } - - free(trie); -} - -lnm_err lnm_http_route_key_segment_insert(lnm_http_route *route, - const char *key, size_t key_len, - size_t index) { - if (route->key_segments == NULL) { - LNM_RES(lnm_http_route_segment_trie_init(&route->key_segments)); - } - - lnm_http_route_segment_trie *trie = route->key_segments; - - for (size_t key_index = 0; key_index < key_len; key_index++) { - unsigned char c = key[key_index]; - - if (trie->children[c] == NULL) { - LNM_RES(lnm_http_route_segment_trie_init(&trie->children[c])); - } - - trie = trie->children[c]; - } - - if (trie->represents_segment) { - return lnm_err_already_present; - } - - trie->represents_segment = true; - trie->index = index; - - return lnm_err_ok; -} - static bool is_ascii(const char *s) { while (*s != '\0') { if (*s < 0) {