diff --git a/include/lnm/common.h b/include/lnm/common.h index eba90a8..a21a439 100644 --- a/include/lnm/common.h +++ b/include/lnm/common.h @@ -89,4 +89,12 @@ uint64_t lnm_atoi(const char *s, size_t len); */ uint64_t lnm_digits(uint64_t num); +/** + * Check whether the given nul-terminated string solely consists of ASCII + * characters. + * + * @param s nul-terminated string to check + */ +bool lnm_is_ascii(const char *s); + #endif diff --git a/include/lnm/http/route.h b/include/lnm/http/route.h index 2d87a1b..4e2d40f 100644 --- a/include/lnm/http/route.h +++ b/include/lnm/http/route.h @@ -45,6 +45,10 @@ lnm_err lnm_http_router_init(lnm_http_router **out); void lnm_http_router_free(lnm_http_router *router); +/** + * Insert a new path & method in the given router, returning a handle to the + * newly created route struct. + */ lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router, lnm_http_method method, const char *path); @@ -67,14 +71,26 @@ lnm_err lnm_http_router_merge(lnm_http_router *r1, lnm_http_router *r2); lnm_err lnm_http_router_nest(lnm_http_router *parent, lnm_http_router *child, const char *prefix); +/** + * Route the given path & method. + */ 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); +/** + * Retrieve a path segment using its name. + * + * @return NULL if not found, otherwise pointer to the match segment struct + * representing the key + */ const lnm_http_route_match_segment * lnm_http_route_match_get(lnm_http_route_match *match, const char *key); +/** + * Append the given step function to the route's step list. + */ lnm_err lnm_http_route_step_append(lnm_http_route *route, lnm_http_step_fn fn, bool blocking); diff --git a/src/http/lnm_http_route.c b/src/http/lnm_http_route.c index 9901b79..45de13e 100644 --- a/src/http/lnm_http_route.c +++ b/src/http/lnm_http_route.c @@ -75,6 +75,31 @@ lnm_err lnm_http_route_key_segment_insert(lnm_http_route *route, return lnm_err_ok; } +const lnm_http_route_match_segment * +lnm_http_route_match_get(lnm_http_route_match *match, const char *key) { + if (match->route->key_segments == NULL) { + return NULL; + } + + lnm_http_route_segment_trie *trie = match->route->key_segments; + + while (*key != '\0') { + trie = trie->children[(unsigned char)*key]; + + if (trie == NULL) { + return NULL; + } + + key++; + } + + if (!trie->represents_segment) { + return NULL; + } + + return &match->key_segments[trie->index]; +} + lnm_err lnm_http_step_init(lnm_http_step **out) { lnm_http_step *step = calloc(1, sizeof(lnm_http_step)); diff --git a/src/http/lnm_http_router.c b/src/http/lnm_http_router.c index fec6c2e..88c7f65 100644 --- a/src/http/lnm_http_router.c +++ b/src/http/lnm_http_router.c @@ -34,21 +34,9 @@ void lnm_http_router_free(lnm_http_router *router) { free(router); } -static bool is_ascii(const char *s) { - while (*s != '\0') { - if (*s < 0) { - return false; - } - - s++; - } - - return true; -} - lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router, lnm_http_method method, const char *path) { - if (path[0] != '/' || !is_ascii(path)) { + if (path[0] != '/' || !lnm_is_ascii(path)) { return lnm_err_invalid_route; } @@ -63,7 +51,10 @@ lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router, switch (c) { case ':': { + // Match the segment content as the variable name const char *next_slash_ptr = strchr(path + 1, '/'); + + // Account for segment being the final part of the route const char *new_path = next_slash_ptr == NULL ? strchr(path + 1, '\0') : next_slash_ptr; size_t key_len = new_path - (path + 1); @@ -128,7 +119,6 @@ lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router, http_router->multi_segment_routes[method] = route; goto end; - } break; default: if (http_router->exact_children[c] == NULL) { @@ -218,6 +208,8 @@ static lnm_http_route_err __lnm_http_router_route(lnm_http_route_match *out, matched_key_segments + 1); if (sub_res == lnm_http_route_err_match) { + // If match succeeds down the recursion, we can correctly set the + // matched segments when going back up the stack if (out != NULL) { out->key_segments[matched_key_segments].start = path_index; out->key_segments[matched_key_segments].len = segment_len; @@ -257,38 +249,13 @@ 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) { - if (!is_ascii(path)) { + if (!lnm_is_ascii(path)) { return lnm_http_route_err_unknown_route; } return __lnm_http_router_route(out, router, method, path, 0, 0); } -const lnm_http_route_match_segment * -lnm_http_route_match_get(lnm_http_route_match *match, const char *key) { - if (match->route->key_segments == NULL) { - return NULL; - } - - lnm_http_route_segment_trie *trie = match->route->key_segments; - - while (*key != '\0') { - trie = trie->children[(unsigned char)*key]; - - if (trie == NULL) { - return NULL; - } - - key++; - } - - if (!trie->represents_segment) { - return NULL; - } - - return &match->key_segments[trie->index]; -} - bool lnm_http_router_conflicts(const lnm_http_router *r1, const lnm_http_router *r2) { // First check the literal routes for the routers @@ -359,14 +326,12 @@ lnm_err lnm_http_router_merge(lnm_http_router *r1, lnm_http_router *r2) { lnm_err lnm_http_router_nest(lnm_http_router *parent, lnm_http_router *child, const char *prefix) { - if (!is_ascii(prefix) || prefix[0] != '/') { + if (!lnm_is_ascii(prefix) || prefix[0] != '/') { return lnm_err_invalid_route; } lnm_http_router *router = parent; - // The child router's routes are also mounted on '/', so we stop one earlier - // as we need to merge it with the router representing the '/' while (*prefix != '\0') { unsigned char c = *prefix; diff --git a/src/lnm_utils.c b/src/lnm_utils.c index 85094bd..849c31f 100644 --- a/src/lnm_utils.c +++ b/src/lnm_utils.c @@ -55,3 +55,15 @@ uint64_t lnm_digits(uint64_t num) { return digits; } + +bool lnm_is_ascii(const char *s) { + bool valid = true; + + while (valid && *s != '\0') { + valid = *s < 0; + + s++; + } + + return valid; +} diff --git a/test/routing.c b/test/routing.c index 805a856..827d4c5 100644 --- a/test/routing.c +++ b/test/routing.c @@ -77,6 +77,8 @@ void test_routing_merge() { TEST_CHECK(lnm_http_router_route(&match, rtr1, lnm_http_method_get, "/test2") == lnm_http_route_err_match); TEST_CHECK(match.route == rt2); + + lnm_http_router_free(rtr1); } void test_routing_nest() { @@ -97,6 +99,7 @@ void test_routing_nest() { TEST_CHECK(lnm_http_router_route(&match, r2, lnm_http_method_get, "/test/test2") == lnm_http_route_err_match); + lnm_http_router_free(r2); } TEST_LIST = {