Compare commits
No commits in common. "e29e02ff85365294047f7d83d9ca5fdaabbe1cde" and "9dbdb0b089554165123d04560d6c7e0d2a763288" have entirely different histories.
e29e02ff85
...
9dbdb0b089
|
@ -21,19 +21,15 @@ 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_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_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_log_init_global();
|
||||
lnm_log_register_stdout(lnm_log_level_debug);
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#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,
|
||||
|
@ -24,55 +22,6 @@ 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`.
|
||||
*
|
||||
|
@ -83,7 +32,47 @@ 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);
|
||||
|
||||
void lnm_http_loop_router_set(lnm_http_loop *hl, lnm_http_router *router);
|
||||
/**
|
||||
* 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);
|
||||
|
||||
lnm_err lnm_http_loop_run(lnm_http_loop *hl, uint16_t port,
|
||||
size_t epoll_threads, size_t worker_threads);
|
||||
|
@ -117,7 +106,10 @@ typedef enum lnm_http_loop_state {
|
|||
} lnm_http_loop_state;
|
||||
|
||||
typedef struct lnm_http_loop_gctx {
|
||||
lnm_http_router *router;
|
||||
struct {
|
||||
lnm_http_route **arr;
|
||||
size_t len;
|
||||
} routes;
|
||||
lnm_http_ctx_init_fn ctx_init;
|
||||
lnm_http_ctx_reset_fn ctx_reset;
|
||||
lnm_http_ctx_free_fn ctx_free;
|
||||
|
@ -130,7 +122,7 @@ typedef struct lnm_http_loop_ctx {
|
|||
lnm_http_loop_state state;
|
||||
lnm_http_req req;
|
||||
lnm_http_res res;
|
||||
const lnm_http_route *route;
|
||||
lnm_http_route *route;
|
||||
lnm_http_step *cur_step;
|
||||
lnm_http_loop_gctx *g;
|
||||
void *c;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
#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);
|
||||
|
||||
lnm_err lnm_http_route_match_get(lnm_http_route_match_segment **out,
|
||||
lnm_http_route_match *match, const char *key);
|
||||
|
||||
#endif
|
|
@ -5,48 +5,42 @@
|
|||
|
||||
#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);
|
||||
|
||||
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);
|
||||
lnm_err lnm_http_step_init(lnm_http_step **out, lnm_http_step_fn fn);
|
||||
|
||||
/**
|
||||
* Initialize a new global context object.
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#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
|
|
@ -28,9 +28,94 @@ lnm_err lnm_http_loop_init(lnm_http_loop **out, void *c_gctx,
|
|||
return lnm_err_ok;
|
||||
}
|
||||
|
||||
void lnm_http_loop_router_set(lnm_http_loop *hl, lnm_http_router *router) {
|
||||
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) {
|
||||
lnm_http_loop_gctx *gctx = hl->gctx;
|
||||
gctx->router = router;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
lnm_err lnm_http_loop_run(lnm_http_loop *hl, uint16_t port,
|
||||
|
|
|
@ -56,24 +56,55 @@ 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;
|
||||
const lnm_http_loop_gctx *gctx = ctx->g;
|
||||
lnm_http_loop_gctx *gctx = ctx->g;
|
||||
|
||||
lnm_http_route_match match;
|
||||
// 0: no match
|
||||
// 1: matched route, but not method
|
||||
// 2: fully matched route
|
||||
int match_level = 0;
|
||||
lnm_http_route *route;
|
||||
|
||||
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;
|
||||
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;
|
||||
break;
|
||||
case lnm_http_route_err_unknown_method:
|
||||
case 2:
|
||||
ctx->res.status = lnm_http_status_method_not_allowed;
|
||||
ctx->state = lnm_http_loop_state_first_res;
|
||||
break;
|
||||
case lnm_http_route_err_unknown_route:
|
||||
ctx->res.status = lnm_http_status_not_found;
|
||||
ctx->state = lnm_http_loop_state_first_res;
|
||||
case 3:
|
||||
ctx->route = route;
|
||||
ctx->cur_step = route->step;
|
||||
ctx->state = lnm_http_loop_state_parse_headers;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "lnm/common.h"
|
||||
#include "lnm/http/loop_internal.h"
|
||||
#include "lnm/http/router.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));
|
||||
|
@ -15,6 +16,18 @@ 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;
|
||||
|
@ -24,9 +37,65 @@ 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++) {
|
||||
if (trie->children[i] != NULL) {
|
||||
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) {
|
||||
while (*s != '0') {
|
||||
if (*s > 127) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -200,10 +269,10 @@ lnm_http_route_err lnm_http_router_route(lnm_http_route_match *out,
|
|||
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) {
|
||||
lnm_err lnm_http_route_match_get(lnm_http_route_match_segment **out,
|
||||
lnm_http_route_match *match, const char *key) {
|
||||
if (match->route->key_segments == NULL) {
|
||||
return NULL;
|
||||
return lnm_err_not_found;
|
||||
}
|
||||
|
||||
lnm_http_route_segment_trie *trie = match->route->key_segments;
|
||||
|
@ -212,15 +281,17 @@ lnm_http_route_match_get(lnm_http_route_match *match, const char *key) {
|
|||
trie = trie->children[(unsigned char)*key];
|
||||
|
||||
if (trie == NULL) {
|
||||
return NULL;
|
||||
return lnm_err_not_found;
|
||||
}
|
||||
|
||||
key++;
|
||||
}
|
||||
|
||||
if (!trie->represents_segment) {
|
||||
return NULL;
|
||||
return lnm_err_not_found;
|
||||
}
|
||||
|
||||
return &match->key_segments[trie->index];
|
||||
*out = &match->key_segments[trie->index];
|
||||
|
||||
return lnm_err_ok;
|
||||
}
|
||||
|
|
|
@ -29,12 +29,12 @@ void test_routing_simple() {
|
|||
TEST_CHECK(match.key_segments[1].start == 15);
|
||||
TEST_CHECK(match.key_segments[1].len == 9);
|
||||
|
||||
const lnm_http_route_match_segment *segment;
|
||||
TEST_CHECK((segment = lnm_http_route_match_get(&match, "second")) != NULL);
|
||||
lnm_http_route_match_segment *segment;
|
||||
TEST_CHECK(lnm_http_route_match_get(&segment, &match, "second") == lnm_err_ok);
|
||||
TEST_CHECK(segment->start == 15);
|
||||
TEST_CHECK(segment->len == 9);
|
||||
TEST_CHECK((segment = lnm_http_route_match_get(&match, "yuhh")) == NULL);
|
||||
TEST_CHECK((segment = lnm_http_route_match_get(&match, "hello")) != NULL);
|
||||
TEST_CHECK(lnm_http_route_match_get(&segment, &match, "yuhh") == lnm_err_not_found);
|
||||
TEST_CHECK(lnm_http_route_match_get(&segment, &match, "hello") == lnm_err_ok);
|
||||
TEST_CHECK(segment->start == 6);
|
||||
TEST_CHECK(segment->len == 8);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue