Csilk 0.2.1
A lightweight, high-performance C HTTP web framework
Loading...
Searching...
No Matches
group.c File Reference

Route group implementation — prefix nesting with inherited middleware. More...

#include <stdlib.h>
#include <string.h>
#include "csilk/core/internal.h"
#include "csilk/csilk.h"
Include dependency graph for group.c:

Data Structures

struct  csilk_group_t
 Route group structure. More...
 

Macros

#define CSILK_GROUP_MW_INIT_CAP   4
 Route group — holds a URL prefix, middleware chain, and optional parent group for nesting.
 

Functions

static char * join_path (const char *p1, const char *p2)
 Internal: join two URL path components with a single '/' separator.
 
csilk_group_t * csilk_group_new (csilk_router_t *router, const char *prefix)
 Create a new root route group with the given URL prefix.
 
csilk_group_t * csilk_group_group (csilk_group_t *parent, const char *prefix)
 Create a child subgroup nested under a parent group.
 
void csilk_group_use (csilk_group_t *group, csilk_handler_t handler)
 Register a middleware handler that applies to all routes in this group.
 
static int gather_handlers (csilk_group_t *group, csilk_handler_t **handlers, size_t *count)
 Internal: recursively collect all middleware handlers from a group and its ancestors into a flat array.
 
void csilk_group_add_route (csilk_group_t *group, const char *method, const char *path, csilk_handler_t handler)
 Register a route on this group with a single handler.
 
void csilk_group_add_route_extended (csilk_group_t *group, const char *method, const char *path, csilk_handler_t handler, const char *input_type, const char *output_type, const char *summary, const char *description)
 Register a route with OpenAPI metadata (input/output types, summary, description).
 
void csilk_group_add_route_extended_perm (csilk_group_t *group, const char *method, const char *path, csilk_handler_t handler, const char *input_type, const char *output_type, const char *summary, const char *description, const char *perm_required, const char *perm_resource)
 Add a route with OpenAPI/reflection metadata to a group.
 
void csilk_group_add_handlers (csilk_group_t *group, const char *method, const char *path, csilk_handler_t *handlers, size_t count)
 Register a route with a custom chain of handlers (middleware + route handler).
 
void csilk_group_free (csilk_group_t *group)
 Free all resources associated with a route group.
 

Detailed Description

Route group implementation — prefix nesting with inherited middleware.

Architecture

Route groups let users attach middleware + route handlers under a common URL prefix. Groups form a tree: a root group (prefix "") has zero or more child subgroups, each with their own prefix segment.

Middleware Chain Assembly

When a route is registered on a group, the framework:

  1. Recursively collects middleware from the group and all its ancestors (see gather_handlers()). Parent middleware comes first.
  2. Appends the route-specific handler chain.
  3. Registers the combined chain with the router.

This means parent-group middleware runs before child-group middleware for every request matching that prefix.

Prefix Joining

Path components are joined via join_path(), which normalizes slashes: e.g., parent="/api", child="/v1" → "/api/v1".


Data Structure Documentation

◆ csilk_group_s

struct csilk_group_s

Route group structure.

Collaboration diagram for csilk_group_t:
Data Fields
size_t middleware_capacity

Allocated capacity (always ≥ count).

size_t middleware_count

Current number of middleware handlers.

csilk_handler_t * middlewares

Dynamically-grown array of middleware handlers for this group. Grown by doubling (initial cap = 4). Freed in csilk_group_free().

csilk_group_t * parent

Parent group in the nesting tree, or NULL for root groups. Used by gather_handlers() to walk up the tree. Not owned.

char * prefix

URL prefix for routes in this group. Combined with parent prefix at creation via join_path(). Freed in csilk_group_free(). e.g., parent="/api", child="/v1" → "/api/v1".

csilk_router_t * router

Router where routes are registered. Inherited from parent or set explicitly in csilk_group_new(). Not owned by the group.

Macro Definition Documentation

◆ CSILK_GROUP_MW_INIT_CAP

#define CSILK_GROUP_MW_INIT_CAP   4

Route group — holds a URL prefix, middleware chain, and optional parent group for nesting.

Groups form a tree via the parent pointer. The full middleware chain for any route is the concatenation of parent middleware (recursively) followed by this group's middleware, followed by the route handlers.

Function Documentation

◆ csilk_group_add_handlers()

void csilk_group_add_handlers ( csilk_group_t *  group,
const char *  method,
const char *  path,
csilk_handler_t handlers,
size_t  count 
)

Register a route with a custom chain of handlers (middleware + route handler).

Add a route with an explicit array of handlers.

Assembly pipeline

  1. join_path(group->prefix, path) → full_path (e.g., "/api/v1/users").
  2. gather_handlers(group, ...) → flat array of all inherited middleware (parent first, then child), grown via realloc.
  3. realloc to append caller's handlers[] to the end.
  4. csilk_router_add(group->router, method, full_path, combined, count).

The final handler chain stored in the router looks like: [parent_mw..., group_mw..., handler_1, ..., handler_n]

Parameters
groupThe route group.
methodHTTP method.
pathPath relative to the group prefix.
handlersArray of handler functions (the chain).
countNumber of handlers in the array.
Note
The handlers array is combined with group middleware — group middleware always runs first, followed by the provided handlers.

◆ csilk_group_add_route()

void csilk_group_add_route ( csilk_group_t *  group,
const char *  method,
const char *  path,
csilk_handler_t  handler 
)

Register a route on this group with a single handler.

Add a route to the group.

The route's path is automatically prefixed with the group's prefix. The handler is wrapped in a 1-element array and passed to csilk_group_add_handlers() which combines group middleware.

Parameters
groupThe route group.
methodHTTP method (e.g., "GET", "POST").
pathPath relative to the group prefix (e.g., "/users").
handlerThe route handler function.

◆ csilk_group_add_route_extended()

void csilk_group_add_route_extended ( csilk_group_t *  group,
const char *  method,
const char *  path,
csilk_handler_t  handler,
const char *  input_type,
const char *  output_type,
const char *  summary,
const char *  description 
)

Register a route with OpenAPI metadata (input/output types, summary, description).

Add a route with OpenAPI/reflection metadata to a group.

Like csilk_group_add_route() but enriches the route with metadata used by the OpenAPI spec generator. The metadata is stored in the method handler for later retrieval by csilk_generate_openapi_json().

Parameters
groupThe route group.
methodHTTP method.
pathPath relative to the group prefix.
handlerThe route handler function.
input_typeRegistered reflection type name for the request body (e.g., "CreateUserRequest"), or NULL.
output_typeRegistered reflection type name for the response body, or NULL.
summaryShort description for OpenAPI operation summary.
descriptionDetailed description for OpenAPI operation.

◆ csilk_group_add_route_extended_perm()

void csilk_group_add_route_extended_perm ( csilk_group_t *  group,
const char *  method,
const char *  path,
csilk_handler_t  handler,
const char *  input_type,
const char *  output_type,
const char *  summary,
const char *  description,
const char *  perm_required,
const char *  perm_resource 
)

Add a route with OpenAPI/reflection metadata to a group.

Add a route with full OpenAPI metadata and permission requirements to a group.

Extended version that also records input/output types and documentation for automatic OpenAPI spec generation.

Parameters
groupThe route group.
methodHTTP method.
pathPath relative to the group prefix.
handlerThe route handler function.
input_typeRegistered type name for request-body binding (NULL if none).
output_typeRegistered type name for response serialisation (NULL if none).
summaryShort operation summary for OpenAPI (NULL to omit).
descriptionDetailed operation description for OpenAPI (NULL to omit).

Add a route with OpenAPI/reflection metadata to a group.

Like csilk_group_add_route() but enriches the route with metadata used by the OpenAPI spec generator. The metadata is stored in the method handler for later retrieval by csilk_generate_openapi_json().

Parameters
groupThe route group.
methodHTTP method.
pathPath relative to the group prefix.
handlerThe route handler function.
input_typeRegistered reflection type name for the request body (e.g., "CreateUserRequest"), or NULL.
output_typeRegistered reflection type name for the response body, or NULL.
summaryShort description for OpenAPI operation summary.
descriptionDetailed description for OpenAPI operation.
perm_requiredPermission required for this route, or NULL.
perm_resourceResource pattern for permission check, or NULL.

Permission metadata is forwarded to the router which stores it alongside the route for authorization middleware to inspect at request time.

◆ csilk_group_free()

void csilk_group_free ( csilk_group_t *  group)

Free all resources associated with a route group.

Destroy a route group and release its resources.

Releases the group's prefix string, middleware handlers array, and the group struct itself. Does NOT free child groups or the router.

Parameters
groupThe group to free (may be NULL).
Note
Child groups created with csilk_group_group() must be freed separately. The router is not owned by the group.

◆ csilk_group_group()

csilk_group_t * csilk_group_group ( csilk_group_t *  parent,
const char *  prefix 
)

Create a child subgroup nested under a parent group.

Create a nested sub-group within an existing group.

The child inherits the parent's router and its prefix is joined with the parent's prefix (e.g., parent="/api", child="/v1" -> combined prefix "/api/v1").

Parameters
parentThe parent group (cannot be NULL).
prefixURL prefix for this subgroup (e.g., "/v1").
Returns
A new csilk_group_t, or NULL on failure.
Note
The child must be freed separately with csilk_group_free() — freeing the parent does NOT free its children.

◆ csilk_group_new()

csilk_group_t * csilk_group_new ( csilk_router_t router,
const char *  prefix 
)

Create a new root route group with the given URL prefix.

Create a new route group with a URL prefix.

Root groups are attached directly to a router. All routes added to this group will be prefixed with prefix.

Parameters
routerThe router instance this group belongs to.
prefixURL prefix for all routes in this group (e.g., "/api/v1"). Pass NULL or "/" for no prefix.
Returns
A new csilk_group_t, or NULL on allocation failure.
Note
The group must be freed with csilk_group_free().

◆ csilk_group_use()

void csilk_group_use ( csilk_group_t *  group,
csilk_handler_t  handler 
)

Register a middleware handler that applies to all routes in this group.

Add middleware to a group.

Middleware handlers are executed before route handlers in the order they are registered. The internal middleware array grows dynamically (doubling capacity) as needed.

Parameters
groupThe route group.
handlerMiddleware handler function. Receives the request context and should call csilk_next() to pass control forward.
Note
Middleware from parent groups is automatically inherited by child groups and prepended before child middleware.

◆ gather_handlers()

static int gather_handlers ( csilk_group_t *  group,
csilk_handler_t **  handlers,
size_t *  count 
)
static

Internal: recursively collect all middleware handlers from a group and its ancestors into a flat array.

How middleware inheritance works

The recursion goes depth-first to the root, then appends middleware on the way back up:

gather_handlers(child, &arr, &n) → gather_handlers(parent, &arr, &n) // recurse to root first → gather_handlers(grandparent, &arr, &n) // root's parent is NULL → (no parent — return) → memcpy grandparent's middlewares into arr[n..] → n += grandparent.middleware_count → memcpy parent's middlewares into arr[n..] → n += parent.middleware_count → memcpy child's middlewares into arr[n..] → n += child.middleware_count

Result: [grandparent_mw..., parent_mw..., child_mw...] This guarantees parent middleware executes before child middleware.

Parameters
groupThe leaf group.
handlers[in/out] Pointer to the handlers array (realloc'd as needed).
count[in/out] Number of handlers collected so far.
Returns
0 on success, -1 on realloc failure.
Note
The caller must free the returned handlers array with free().

◆ join_path()

static char * join_path ( const char *  p1,
const char *  p2 
)
static

Internal: join two URL path components with a single '/' separator.

Algorithm

  1. Handle trivial cases: if either component is NULL or empty, return a strdup of the other (or "/" if both are empty).
  2. Strip trailing slashes from p1 by decrementing l1.
  3. Strip leading slashes from p2 by advancing p2_start.
  4. Allocate l1 + l2 + 1 (slash) + 1 (null) bytes.
  5. Memcpy p1, then '/', then p2_start.

Examples: join_path("/api/", "/v1") → "/api/v1" join_path("api", "v1") → "api/v1" join_path(NULL, "/users") → "/users" join_path("", "") → "/"

Parameters
p1First path component (may be empty or NULL).
p2Second path component (may be empty or NULL).
Returns
A newly heap-allocated joined path string. Caller must free().
Note
Returns strdup("/") if both components are NULL or empty.