39#define N_ROUTER_SEGS_MAX 32
125static int _parse_n(
const char* s,
size_t len,
size_t lo,
size_t hi,
size_t* n) {
128 if (len == 0 || len > 2)
130 for (i = 0; i < len; i++) {
131 if (s[i] <
'0' || s[i] >
'9')
133 *n = *n * 10 + (size_t)(s[i] -
'0');
135 return *n >= lo && *n <= hi;
139 return c > 0x20 && c < 0x7f && c !=
'%' && c !=
'{' && c !=
'}' && c !=
'?' && c !=
'#';
144 const char* p = route->
pattern;
146 memset(c, 0,
sizeof(*c));
150 *why =
"no method or no handler";
154 *why =
"unknown method bit";
157 if (!p || p[0] !=
'/') {
158 *why =
"a pattern starts with '/'";
163 *why =
"pattern too long";
173 while (i < len && p[i] !=
'/')
177 *why =
"empty segment ('//')";
181 *why =
"too many segments";
185 *why =
"{...:rest} must be the last segment";
191 size_t colon = s + 1, nlen, k, tlen;
193 if (p[e - 1] !=
'}') {
194 *why =
"a placeholder fills its whole segment";
197 while (colon < e - 1 && p[colon] !=
':')
199 nlen = colon - s - 1;
201 *why =
"placeholder name empty or too long";
204 for (k = s + 1; k < colon; k++)
205 if (!((p[k] >=
'a' && p[k] <=
'z') || (p[k] >=
'0' && p[k] <=
'9') || p[k] ==
'_')) {
206 *why =
"placeholder name is not [a-z0-9_]";
210 *why =
"too many placeholders";
214 if (strlen(c->
names[k]) == nlen && memcmp(c->
names[k], p + s + 1, nlen) == 0) {
215 *why =
"duplicate placeholder name";
220 t = colon < e - 1 ? p + colon + 1 :
"seg}";
221 tlen = colon < e - 1 ? (size_t)(e - 1 - colon - 1) : 3;
222 if (tlen == 4 && memcmp(t,
"slug", 4) == 0)
224 else if (tlen == 3 && memcmp(t,
"seg", 3) == 0)
226 else if (tlen == 4 && memcmp(t,
"rest", 4) == 0)
228 else if (tlen > 1 && t[0] ==
'd' &&
_parse_n(t + 1, tlen - 1, 1, 18, &g->
n))
230 else if (tlen > 3 && memcmp(t,
"hex", 3) == 0 &&
_parse_n(t + 3, tlen - 3, 1, 64, &g->
n))
232 else if (tlen > 3 && memcmp(t,
"int", 3) == 0 &&
_parse_n(t + 3, tlen - 3, 1, 18, &g->
n))
235 *why =
"unknown placeholder type";
242 for (k = s; k < e; k++)
244 *why =
"literal holds a byte outside visible ASCII, or % { } ? #";
259 *why =
"{...:rest} takes the trailing '/' itself";
283 for (i = 0; i < count; i++) {
284 const char* why =
"";
303 return r ? r->
nb : 0;
313 if (!r || i >= r->
nb)
345 for (i = 0; i < r->
nb; i++) {
346 for (j = i + 1; j < r->
nb; j++) {
354 for (k = 0; k < a->
nb_segs && ambiguous; k++) {
367 snprintf(err, errsz,
"'%s' and '%s' match the same paths with the same specificity", a->
route->
pattern, b->
route->
pattern);
380 return n == g->
lit_len && memcmp(s, g->
lit, n) == 0;
385 for (k = 0; k < n; k++)
386 if (s[k] <
'0' || s[k] >
'9')
392 for (k = 0; k < n; k++)
393 if (!((s[k] >=
'0' && s[k] <=
'9') || (s[k] >=
'a' && s[k] <=
'f')))
397 if (n == 0 || n > 120)
399 for (k = 0; k < n; k++)
400 if (!((s[k] >=
'a' && s[k] <=
'z') || (s[k] >=
'0' && s[k] <=
'9') || s[k] ==
'_' || s[k] ==
'-'))
404 return n >= 1 && n <= 255;
411 if (c >=
'0' && c <=
'9')
413 if (c >=
'a' && c <=
'f')
415 if (c >=
'A' && c <=
'F')
422static size_t _decode(
const char* s,
size_t n,
char* dst) {
424 for (i = 0; i < n; i++) {
428 if (n - i < 3 || (hi =
_hexval(s[i + 1])) < 0 || (lo =
_hexval(s[i + 2])) < 0)
430 c = (char)((hi << 4) | lo);
446 for (k = 0; k < n; k++) {
455static int _split(
const char* path,
size_t len,
const char** seg,
size_t* seglen,
int* trailing) {
463 while (i < len && path[i] !=
'/')
483 if ((
size_t)nseg < c->nb_segs)
488 for (k = 0; k < c->
nb_segs; k++) {
513 int nseg, trailing, k;
520 nseg =
_split(path, len, seg, seglen, &trailing);
524 for (k = 0; k < nseg; k++)
525 if (
_decode(seg[k], seglen[k], scratch) == (
size_t)-1)
527 for (i = 0; i < r->
nb; i++) {
540 *allowed = path_methods;
546 memset(match, 0,
sizeof(*match));
550 for (i = 0; i < best->
nb_segs; i++) {
552 const char* s = seg[i];
553 size_t n = seglen[i], d;
557 n = (size_t)(path + len - s);
583 if (m->
names[i] && strcmp(m->
names[i], name) == 0) {
604 strcat(buf, buf[0] ?
", HEAD" :
"HEAD");
606 strcat(buf, buf[0] ?
", POST" :
"POST");
607 if (!out || size <= strlen(buf))
609 memcpy(out, buf, strlen(buf) + 1);
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define Free(__ptr)
Free Handler to get errors.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
#define N_HTTP_REQ_METHOD_BIT(m)
Bit of a method in n_http_req_limits.methods.
n_http_req_method
The methods accepted.
#define N_HTTP_REQ_PATH_MAX
Longest origin-form request target ('/'-prefixed, query included).
const char * names[8]
placeholder names, in pattern order
const char * pattern
the pattern, see the file documentation
size_t lens[8]
decoded value lengths
const char * values[8]
decoded values (NUL-terminated, inside buf)
n_route_handler handler
what the caller runs on a match
size_t nb_params
number of placeholders
char buf[2048u+8+1]
storage for the decoded values
unsigned methods
accepted methods, an OR of N_ROUTE_GET / N_ROUTE_POST / N_ROUTE_HEAD
const N_ROUTE * route
the route that matched
#define N_ROUTE_POST
route accepts POST
N_ROUTER_RESULT
result of n_router_match()
N_ROUTER * n_router_new(void)
new, empty router
#define N_ROUTE_NAME_MAX
longest placeholder name
int n_router_allow_header(unsigned allowed, char *out, size_t size)
write an Allow header value for a method mask (GET implies HEAD)
N_ROUTER_RESULT n_router_match(const N_ROUTER *r, n_http_req_method method, const char *path, size_t len, N_ROUTE_MATCH *match, unsigned *allowed)
find the route for a method and a path
#define N_ROUTE_HEAD
route accepts HEAD only (rare: GET routes answer HEAD already)
#define N_ROUTE_GET
route accepts GET (and so HEAD)
int n_router_check(const N_ROUTER *r, char *err, size_t errsz)
refuse two routes sharing a method that can match the same path with the same specificity
void n_router_free(N_ROUTER **r)
free a router and set *r to NULL (the route tables are not touched)
size_t n_router_count(const N_ROUTER *r)
number of routes added
const N_ROUTE * n_router_route(const N_ROUTER *r, size_t i)
the i-th route added
int n_router_add(N_ROUTER *r, const N_ROUTE *routes, size_t count)
compile and add a route table; nothing is added when a route is refused
#define N_ROUTE_PARAMS_MAX
most placeholders in one route
const char * n_route_param(const N_ROUTE_MATCH *m, const char *name, size_t *len)
decoded value of a placeholder
@ N_ROUTER_BAD_REQUEST
the path itself is invalid: 400
@ N_ROUTER_FOUND
a route matched path and method
@ N_ROUTER_NOT_FOUND
no route matches the path: 404 or a fallback
@ N_ROUTER_METHOD_NOT_ALLOWED
the path matches, the method does not: 405
a successful match: the route and its decoded placeholder values
int n_str_utf8_valid(const char *s, size_t len)
Test that [s, s+len) is well-formed UTF-8 (RFC 3629): no overlong form, no surrogate,...
Common headers and low-level functions & define.
static int _hexval(char c)
static int _literal_byte_ok(unsigned char c)
static int _compile(const N_ROUTE *route, _compiled *c, const char **why)
static int _split(const char *path, size_t len, const char **seg, size_t *seglen, int *trailing)
static unsigned _methods(unsigned m)
static int _rank(_seg_type t)
static int _route_fits(const _compiled *c, const char **seg, const size_t *seglen, int nseg, int trailing)
static int _more_specific(const _compiled *a, const _compiled *b)
static int _parse_n(const char *s, size_t len, size_t lo, size_t hi, size_t *n)
#define N_ROUTER_SEGS_MAX
static int _seg_fits(const _seg *g, const char *s, size_t n)
static int _types_overlap(const _seg *a, const _seg *b)
static size_t _decode(const char *s, size_t n, char *dst)
a compiled router (opaque)
HTTP router: static route tables, typed placeholders, specificity.
N_STR and string function declaration.