Middleware
Middleware are intermediate handlers that execute before the main route handler. They are used for authentication, validation, logging, and other tasks.
How It Works
Middleware is a function that takes a request context and returns:
1— continue chain execution0— stop execution (main handler will not be called)
c
typedef int (*middleware_fn_p)(void*);Global Middleware
Global middleware are configured in config.json and apply to all server routes:
json
{
"servers": {
"s1": {
"http": {
"middlewares": [
"middleware_http_test_header",
"middleware_http_auth"
],
"routes": { ... }
}
}
}
}Registering Global Middleware
Global middleware are registered in the middlewarelist.c file:
c
// app/middlewares/middlewarelist.c
#include "middleware_registry.h"
#include "httpmiddlewares.h"
middleware_global_fn_t middlewarelist[] = {
{ "middleware_http_test_header", middleware_http_test_header },
{ "middleware_http_auth", middleware_http_auth },
{ "", NULL } // End of list
};Creating Middleware
Simple Middleware
c
// app/middlewares/httpmiddlewares.h
#ifndef __HTTPMIDDLEWARES__
#define __HTTPMIDDLEWARES__
#include "http.h"
int middleware_http_test_header(httpctx_t* ctx);
int middleware_http_auth(httpctx_t* ctx);
#endifc
// app/middlewares/httpmiddlewares.c
#include "httpmiddlewares.h"
int middleware_http_test_header(httpctx_t* ctx) {
// Add header to all responses
ctx->response->add_header(ctx->response, "X-Powered-By", "C Web Framework");
return 1; // Continue execution
}Authorization Middleware
c
#include "httpmiddlewares.h"
#include "session.h"
int middleware_http_auth(httpctx_t* ctx) {
const char* session_id = ctx->request->get_cookie(ctx->request, "session_id");
if (session_id == NULL) {
ctx->response->status_code = 401;
ctx->response->send_data(ctx->response, "Unauthorized");
return 0; // Stop execution
}
char* session_data = session_get(session_id);
if (session_data == NULL) {
ctx->response->status_code = 401;
ctx->response->send_data(ctx->response, "Session expired");
return 0;
}
// Parse session data and set user
json_doc_t* doc = json_parse(session_data);
free(session_data);
if (doc == NULL) {
ctx->response->status_code = 401;
ctx->response->send_data(ctx->response, "Invalid session");
return 0;
}
json_token_t* root = json_root(doc);
json_token_t* user_id_token = json_object_get(root, "user_id");
int ok = 0;
int user_id = json_int(user_id_token, &ok);
json_free(doc);
if (!ok || user_id < 1) {
ctx->response->status_code = 401;
ctx->response->send_data(ctx->response, "Invalid user");
return 0;
}
// Load user and set in context
array_t* params = array_create();
mparams_fill_array(params, mparam_int(id, user_id));
user_t* user = user_get(params);
array_free(params);
if (user == NULL) {
ctx->response->status_code = 401;
ctx->response->send_data(ctx->response, "User not found");
return 0;
}
httpctx_set_user(ctx, user);
return 1; // Continue execution
}Middleware with 403 Response
c
int middleware_http_forbidden(httpctx_t* ctx) {
ctx->response->send_default(ctx->response, 403);
return 0;
}Local Middleware (in Handler)
To apply middleware to a specific route, use the middleware() macro:
c
#include "middleware.h"
void secret_page(httpctx_t* ctx) {
// Check authorization before execution
middleware(
middleware_http_auth(ctx),
middleware_check_role(ctx, "admin")
);
// Code will only execute if all middleware returned 1
ctx->response->send_data(ctx->response, "Secret content");
}The middleware() macro accepts any number of functions. If any of them returns 0, handler execution will stop.
Role Check Example
c
int middleware_check_role(httpctx_t* ctx, const char* required_role) {
user_t* user = httpctx_get_user(ctx);
if (user == NULL) {
ctx->response->send_default(ctx->response, 401);
return 0;
}
// Check user role
if (!user_has_role(user, required_role)) {
ctx->response->send_default(ctx->response, 403);
return 0;
}
return 1;
}Middleware for Parameter Validation
c
int middleware_http_query_param_required(httpctx_t* ctx, char** keys, int size) {
char message[256] = {0};
int ok = 0;
for (int i = 0; i < size; i++) {
const char* param = query_param_char(ctx->request, keys[i], &ok);
if (!ok || param == NULL || param[0] == 0) {
snprintf(message, sizeof(message), "Parameter \"%s\" is required", keys[i]);
ctx->response->status_code = 400;
ctx->response->send_data(ctx->response, message);
return 0;
}
}
return 1;
}
// Usage
void search(httpctx_t* ctx) {
char* required[] = {"query", "page"};
middleware(
middleware_http_query_param_required(ctx, required, 2)
);
// Parameters are guaranteed to be present
int ok;
const char* query = query_param_char(ctx->request, "query", &ok);
int page = query_param_int(ctx->request, "page", &ok);
// ...
}Rate Limiting via Middleware
Rate limiting is configured in the configuration and applied automatically:
json
{
"servers": {
"s1": {
"ratelimits": {
"strict": { "burst": 5, "rate": 1 },
"normal": { "burst": 100, "rate": 50 }
},
"http": {
"ratelimit": "normal",
"routes": {
"/api/login": {
"POST": {
"file": "...",
"function": "login",
"ratelimit": "strict"
}
}
}
}
}
}
}WebSocket Middleware
Separate middleware are created for WebSocket connections:
c
// app/middlewares/wsmiddlewares.h
#ifndef __WSMIDDLEWARES__
#define __WSMIDDLEWARES__
#include "websockets.h"
int middleware_ws_auth(wsctx_t* ctx);
#endifc
// app/middlewares/wsmiddlewares.c
#include "wsmiddlewares.h"
#include "session.h"
int middleware_ws_auth(wsctx_t* ctx) {
// Authorization check for WebSocket
// ...
return 1;
}