diff --git a/include/lnm/common.h b/include/lnm/common.h index 8cc982e..3ada2a4 100644 --- a/include/lnm/common.h +++ b/include/lnm/common.h @@ -32,6 +32,8 @@ typedef enum lnm_err { lnm_err_not_setup, lnm_err_bad_regex, lnm_err_not_found, + lnm_err_invalid_route, + lnm_err_overlapping_route, } lnm_err; typedef struct lnm_loop lnm_http_loop; diff --git a/src/_include/lnm/http/router_internal.h b/src/_include/lnm/http/router_internal.h new file mode 100644 index 0000000..e4f6bda --- /dev/null +++ b/src/_include/lnm/http/router_internal.h @@ -0,0 +1,36 @@ +#ifndef LNM_HTTP_ROUTER_INTERNAL +#define LNM_HTTP_ROUTER_INTERNAL + +#include "lnm/common.h" + +typedef struct lnm_http_route { +} lnm_http_route; + +typedef struct lnm_http_router { + struct lnm_http_router *exact_children[128]; + struct lnm_http_router *single_segment_child; + lnm_http_route *route; +} lnm_http_router; + +/** + * 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_route_init(lnm_http_route **out); + +void lnm_http_route_free(lnm_http_route *route); + +lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router, + 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); + +#endif diff --git a/src/http/lnm_http_router.c b/src/http/lnm_http_router.c new file mode 100644 index 0000000..f5001f8 --- /dev/null +++ b/src/http/lnm_http_router.c @@ -0,0 +1,73 @@ +#include + +#include "lnm/common.h" +#include "lnm/http/router_internal.h" + +lnm_err lnm_http_router_init(lnm_http_router **out) { + lnm_http_router *router = calloc(1, sizeof(lnm_http_router)); + + if (router == NULL) { + return lnm_err_failed_alloc; + } + + *out = router; + + 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; +} + +static bool is_ascii(const char *s) { + while (*s != '0') { + if (*s > 127) { + return false; + } + + s++; + } + + return true; +} + +lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router, + const char *path) { + if (path[0] != '/' || !is_ascii(path)) { + return lnm_err_invalid_route; + } + + while (*path != '\0') { + unsigned char c = *path; + + switch (c) { + case ':': + LNM_RES(lnm_http_router_init(&http_router->single_segment_child)); + + // All other characters in the segment are ignored + const char *next_slash_ptr = strchr(path, '/'); + path = next_slash_ptr == NULL ? strchr(path, '\0') : next_slash_ptr; + break; + case '*': + // TODO multi-segment wildcard + break; + default: + LNM_RES(lnm_http_router_init(&http_router->exact_children[c])); + http_router = http_router->exact_children[c]; + path++; + break; + } + } + + LNM_RES(lnm_http_route_init(out)); + + return lnm_err_ok; +}