feat(routing): multi-segment wildcard matches

This commit is contained in:
Jef Roosens 2024-02-25 22:59:58 +01:00
parent 3ae1b62dd0
commit 115bf74456
Signed by: Jef Roosens
GPG key ID: B75D4F293C7052DB
3 changed files with 94 additions and 15 deletions

View file

@ -5,19 +5,25 @@
#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;
struct lnm_http_route {
lnm_http_route_segment_trie *key_segments;
lnm_http_step *step;
};
struct lnm_http_router {
struct lnm_http_router *exact_children[128];
struct lnm_http_router *single_segment_child;
lnm_http_route *multi_segment_routes[lnm_http_method_total];
lnm_http_route *routes[lnm_http_method_total];
bool represents_route;
};
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);
@ -39,11 +45,6 @@ typedef struct lnm_http_step {
*/
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);

View file

@ -93,9 +93,42 @@ lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router,
http_router = http_router->single_segment_child;
path = new_path;
} break;
case '*':
// TODO multi-segment wildcard
break;
case '*': {
const char *next_slash_ptr = strchr(path + 1, '/');
// Star match should be at end of route
if (next_slash_ptr != NULL) {
res = lnm_err_invalid_route;
goto end;
}
const char *end = strchr(path + 1, '\0');
size_t key_len = end - (path + 1);
if (key_len == 0) {
res = lnm_err_invalid_route;
goto end;
}
res = lnm_http_route_key_segment_insert(route, path + 1, key_len,
key_segments_count);
if (res != lnm_err_ok) {
goto end;
}
key_segments_count++;
if (http_router->multi_segment_routes[method] != NULL) {
res = lnm_err_overlapping_route;
}
http_router->multi_segment_routes[method] = route;
goto end;
} break;
default:
if (http_router->exact_children[c] == NULL) {
res = lnm_http_router_init(&http_router->exact_children[c]);
@ -107,6 +140,7 @@ lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router,
http_router = http_router->exact_children[c];
path++;
break;
}
}
@ -195,6 +229,26 @@ static lnm_http_route_err __lnm_http_router_route(lnm_http_route_match *out,
}
}
const lnm_http_route *multi_segment_route =
router->multi_segment_routes[method];
if (multi_segment_route != NULL) {
const char *end_ptr = strchr(path + path_index, '\0');
size_t segment_len = end_ptr - (path + path_index);
// TODO do 405's make sense for star matches?
if (segment_len > 0) {
if (out != NULL) {
out->route = multi_segment_route;
out->key_segments[matched_key_segments].start = path_index;
out->key_segments[matched_key_segments].len = segment_len;
}
return lnm_http_route_err_match;
}
}
return res;
}