Table of Contents
include/nilorea/n_http_router.h
HTTP router: static route tables, typed placeholders, specificity. More...
Structures
| Name | |
|---|---|
| struct | N_ROUTE_MATCH a successful match: the route and its decoded placeholder values |
| struct | N_ROUTE one route of a table |
Detailed Description
HTTP router: static route tables, typed placeholders, specificity.
Author: Castagnier Mickael
Version: 1.0
Date: 2026
Routes are declared in static tables, one line each:
static const N_ROUTE PUBLIC[] = {
{N_ROUTE_GET, "/", h_home, NULL},
{N_ROUTE_GET, "/{y:d4}/{m:d2}/{d:d2}/{slug:slug}/", h_post, NULL},
{N_ROUTE_GET, "/media/{id:hex16}/{w:int4}/{name:seg}", h_variant, NULL},
};
A pattern is a '/'-separated list of segments, each either a literal ("feed", matched exactly and case-sensitively against the raw path) or one placeholder "{name:type}" ("{name}" is "{name:seg}"). A trailing '/' in the pattern requires one in the path. Types are checked on the RAW segment, before decoding, so a percent-encoded byte never satisfies a digit, hex or slug type:
| type | raw segment |
|---|---|
| dN | exactly N digits (N 1..18) |
| hexN | exactly N lowercase hex digits (N 1..64) |
| intN | 1 to N digits (N 1..18) |
| slug | 1 to 120 of a-z 0-9 '_' '-' |
| seg | 1 to 255 bytes, no '/' |
| rest | the remainder of the path, '/' included, last only |
Captured values are strictly percent-decoded into the match (a malformed escape, a decoded NUL or ill-formed UTF-8 in ANY segment of the path makes the whole request a 400, before any route is tried).
When several routes match, the most specific wins, segment by segment from the left: a literal beats dN/hexN, which beat intN, then slug, seg, rest. So "/feed/" wins over "/{slug}/" whatever the table order, and n_router_check() refuses only real ambiguities (two routes the rule cannot order, like a duplicate or d4 against hex4). A route allowing GET also answers HEAD.
The router does no allocation per request and keeps pointers to the route tables: they must outlive it (static tables do).
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __NILOREA_HTTP_ROUTER_HEADER
#define __NILOREA_HTTP_ROUTER_HEADER
#ifdef __cplusplus
extern "C" {
#endif
#include "nilorea/n_http_req.h"
#include <stddef.h>
#define N_ROUTE_GET N_HTTP_REQ_METHOD_BIT(N_HTTP_REQ_GET)
#define N_ROUTE_POST N_HTTP_REQ_METHOD_BIT(N_HTTP_REQ_POST)
#define N_ROUTE_HEAD N_HTTP_REQ_METHOD_BIT(N_HTTP_REQ_HEAD)
#define N_ROUTE_PARAMS_MAX 8
#define N_ROUTE_NAME_MAX 31
struct N_ROUTE_MATCH;
typedef int (*n_route_handler)(void* ctx, const struct N_ROUTE_MATCH* match);
typedef struct N_ROUTE {
unsigned methods;
const char* pattern;
n_route_handler handler;
const void* data;
} N_ROUTE;
typedef struct N_ROUTE_MATCH {
const N_ROUTE* route;
size_t nb_params;
const char* names[N_ROUTE_PARAMS_MAX];
const char* values[N_ROUTE_PARAMS_MAX];
size_t lens[N_ROUTE_PARAMS_MAX];
char buf[N_HTTP_REQ_PATH_MAX + N_ROUTE_PARAMS_MAX + 1];
} N_ROUTE_MATCH;
typedef enum {
N_ROUTER_FOUND = 0,
N_ROUTER_NOT_FOUND = 1,
N_ROUTER_METHOD_NOT_ALLOWED = 2,
N_ROUTER_BAD_REQUEST = 3
} N_ROUTER_RESULT;
typedef struct N_ROUTER N_ROUTER;
N_ROUTER* n_router_new(void);
void n_router_free(N_ROUTER** r);
int n_router_add(N_ROUTER* r, const N_ROUTE* routes, size_t count);
int n_router_check(const N_ROUTER* r, char* err, size_t errsz);
size_t n_router_count(const N_ROUTER* r);
const N_ROUTE* n_router_route(const N_ROUTER* r, size_t i);
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);
const char* n_route_param(const N_ROUTE_MATCH* m, const char* name, size_t* len);
int n_router_allow_header(unsigned allowed, char* out, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* __NILOREA_HTTP_ROUTER_HEADER */
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB