63 while ((opt = getopt(argc, argv,
"V:")) != -1) {
90 {
N_ROUTE_GET,
"/category/{slug:slug}/page/{n:int6}/",
h,
"category-paged"},
92 {
N_ROUTE_GET,
"/{y:d4}/{m:d2}/{d:d2}/{slug:slug}/",
h,
"post"},
95 {
N_ROUTE_GET,
"/media/{id:hex16}/{name:seg}",
h,
"media"},
96 {
N_ROUTE_GET,
"/media/{id:hex16}/{w:int4}/{name:seg}",
h,
"variant"},
97 {
N_ROUTE_GET,
"/wp-content/uploads/{path:rest}",
h,
"legacy-upload"},
105 {
N_ROUTE_POST,
"/admin/posts/{id:int9}/trash",
h,
"post-trash"},
118 n_log(
LOG_ERR,
"FAIL: %s -> result %d, route %s, wanted %s", path, (
int)res,
132 return v && len == strlen(want) && strcmp(v, want) == 0;
138 unsigned allowed = 0;
199 for (i = 0; i < 40; i++) {
218 expect_true(
"same shape twice is ambiguous",
n_router_check(r, err,
sizeof(err)) == FALSE && strstr(err,
"/a/{x:slug}/") != NULL);
239 static const char* bad[] = {
240 "noslash",
"/a//b",
"/{x",
"/{x}y",
"/{}",
"/{X:seg}",
"/{x:what}",
"/{x:d0}",
"/{x:d19}",
"/{x:hex65}",
241 "/{x:int}",
"/{x:seg}/{x:seg}",
"/{r:rest}/more",
"/{r:rest}/",
"/a%20b",
"/a b",
"/a?b",
242 "/{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}/{i}",
"/{abcdefghijklmnopqrstuvwxyz012345}"};
244 for (i = 0; i <
sizeof(bad) /
sizeof(bad[0]); i++) {
255 N_ROUTE nomethod = {0,
"/x",
h, NULL};
257 N_ROUTE badbit = {1u << 9,
"/x",
h, NULL};
273int main(
int argc,
char** argv) {
278 fprintf(stderr,
"usage: %s [-V LOG_LEVEL]\n", argv[0]);
void process_args(int argc, char **argv)
static void expect_true(const char *label, int cond)
static void test_checks(void)
the start-up checks
static int param_is(const N_ROUTE_MATCH *m, const char *name, const char *want)
param name of the last match equals want
static void test_match(const N_ROUTER *r)
matching on the blog's tables
static int h(void *ctx, const N_ROUTE_MATCH *m)
a handler that does nothing (the test only routes)
static const N_ROUTE PUBLIC[]
the public routes of the Nilorea blog, in a deliberately unhelpful order: the catch-all "/{slug}...
static const N_ROUTE ADMIN[]
a few admin routes
static void expect_route(const N_ROUTER *r, n_http_req_method method, const char *path, const char *want, N_ROUTE_MATCH *m)
route path with method and expect a route data tag (NULL: not found)
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_NOTICE
normal but significant condition
#define LOG_INFO
informational
n_http_req_method
The methods accepted.
size_t nb_params
number of placeholders
const N_ROUTE * route
the route that matched
const void * data
caller data for this route (a policy: auth, CSRF, limits...)
#define N_ROUTE_POST
route accepts POST
N_ROUTER_RESULT
result of n_router_match()
N_ROUTER * n_router_new(void)
new, empty router
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
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
Common headers and low-level functions & define.
a compiled router (opaque)
HTTP router: static route tables, typed placeholders, specificity.