refactor(routing): moved some stuff, added some comments
parent
cf4451740f
commit
115baecde8
|
@ -89,4 +89,12 @@ uint64_t lnm_atoi(const char *s, size_t len);
|
||||||
*/
|
*/
|
||||||
uint64_t lnm_digits(uint64_t num);
|
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
|
#endif
|
||||||
|
|
|
@ -45,6 +45,10 @@ lnm_err lnm_http_router_init(lnm_http_router **out);
|
||||||
|
|
||||||
void lnm_http_router_free(lnm_http_router *router);
|
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_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router,
|
||||||
lnm_http_method method, const char *path);
|
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,
|
lnm_err lnm_http_router_nest(lnm_http_router *parent, lnm_http_router *child,
|
||||||
const char *prefix);
|
const char *prefix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route the given path & method.
|
||||||
|
*/
|
||||||
lnm_http_route_err lnm_http_router_route(lnm_http_route_match *out,
|
lnm_http_route_err lnm_http_router_route(lnm_http_route_match *out,
|
||||||
const lnm_http_router *router,
|
const lnm_http_router *router,
|
||||||
lnm_http_method method,
|
lnm_http_method method,
|
||||||
const char *path);
|
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 *
|
const lnm_http_route_match_segment *
|
||||||
lnm_http_route_match_get(lnm_http_route_match *match, const char *key);
|
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,
|
lnm_err lnm_http_route_step_append(lnm_http_route *route, lnm_http_step_fn fn,
|
||||||
bool blocking);
|
bool blocking);
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,31 @@ lnm_err lnm_http_route_key_segment_insert(lnm_http_route *route,
|
||||||
return lnm_err_ok;
|
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_err lnm_http_step_init(lnm_http_step **out) {
|
||||||
lnm_http_step *step = calloc(1, sizeof(lnm_http_step));
|
lnm_http_step *step = calloc(1, sizeof(lnm_http_step));
|
||||||
|
|
||||||
|
|
|
@ -34,21 +34,9 @@ void lnm_http_router_free(lnm_http_router *router) {
|
||||||
free(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_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router,
|
||||||
lnm_http_method method, const char *path) {
|
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;
|
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) {
|
switch (c) {
|
||||||
case ':': {
|
case ':': {
|
||||||
|
// Match the segment content as the variable name
|
||||||
const char *next_slash_ptr = strchr(path + 1, '/');
|
const char *next_slash_ptr = strchr(path + 1, '/');
|
||||||
|
|
||||||
|
// Account for segment being the final part of the route
|
||||||
const char *new_path =
|
const char *new_path =
|
||||||
next_slash_ptr == NULL ? strchr(path + 1, '\0') : next_slash_ptr;
|
next_slash_ptr == NULL ? strchr(path + 1, '\0') : next_slash_ptr;
|
||||||
size_t key_len = new_path - (path + 1);
|
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;
|
http_router->multi_segment_routes[method] = route;
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
if (http_router->exact_children[c] == NULL) {
|
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);
|
matched_key_segments + 1);
|
||||||
|
|
||||||
if (sub_res == lnm_http_route_err_match) {
|
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) {
|
if (out != NULL) {
|
||||||
out->key_segments[matched_key_segments].start = path_index;
|
out->key_segments[matched_key_segments].start = path_index;
|
||||||
out->key_segments[matched_key_segments].len = segment_len;
|
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,
|
const lnm_http_router *router,
|
||||||
lnm_http_method method,
|
lnm_http_method method,
|
||||||
const char *path) {
|
const char *path) {
|
||||||
if (!is_ascii(path)) {
|
if (!lnm_is_ascii(path)) {
|
||||||
return lnm_http_route_err_unknown_route;
|
return lnm_http_route_err_unknown_route;
|
||||||
}
|
}
|
||||||
|
|
||||||
return __lnm_http_router_route(out, router, method, path, 0, 0);
|
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,
|
bool lnm_http_router_conflicts(const lnm_http_router *r1,
|
||||||
const lnm_http_router *r2) {
|
const lnm_http_router *r2) {
|
||||||
// First check the literal routes for the routers
|
// 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,
|
lnm_err lnm_http_router_nest(lnm_http_router *parent, lnm_http_router *child,
|
||||||
const char *prefix) {
|
const char *prefix) {
|
||||||
if (!is_ascii(prefix) || prefix[0] != '/') {
|
if (!lnm_is_ascii(prefix) || prefix[0] != '/') {
|
||||||
return lnm_err_invalid_route;
|
return lnm_err_invalid_route;
|
||||||
}
|
}
|
||||||
|
|
||||||
lnm_http_router *router = parent;
|
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') {
|
while (*prefix != '\0') {
|
||||||
unsigned char c = *prefix;
|
unsigned char c = *prefix;
|
||||||
|
|
||||||
|
|
|
@ -55,3 +55,15 @@ uint64_t lnm_digits(uint64_t num) {
|
||||||
|
|
||||||
return digits;
|
return digits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool lnm_is_ascii(const char *s) {
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
|
while (valid && *s != '\0') {
|
||||||
|
valid = *s < 0;
|
||||||
|
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
|
@ -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(lnm_http_router_route(&match, rtr1, lnm_http_method_get, "/test2") == lnm_http_route_err_match);
|
||||||
TEST_CHECK(match.route == rt2);
|
TEST_CHECK(match.route == rt2);
|
||||||
|
|
||||||
|
lnm_http_router_free(rtr1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_routing_nest() {
|
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);
|
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 = {
|
TEST_LIST = {
|
||||||
|
|
Loading…
Reference in New Issue