Table of Contents
- include/nilorea/n_http2_server.h
- Structures
- Types
- Functions
- Defines
- Detailed Description
- Types Documentation
- Functions Documentation
- function n_http2_server_run
- function n_http2_server_rst_stream
- function n_http2_server_respond
- function n_http2_server_request_count
- function n_http2_server_new
- function n_http2_server_handshake
- function n_http2_server_goaway
- function n_http2_server_free
- function n_http2_server_error
- Macros Documentation
- define N_H2_SRV_READ_CHUNK
- define N_H2_SRV_MAX_STREAMS
- define N_H2_SRV_MAX_HEADERS
- define N_H2_SRV_MAX_BODY
- define N_H2_ERR_STREAM_CLOSED
- define N_H2_ERR_SETTINGS_TIMEOUT
- define N_H2_ERR_REFUSED_STREAM
- define N_H2_ERR_PROTOCOL_ERROR
- define N_H2_ERR_NO_ERROR
- define N_H2_ERR_INTERNAL_ERROR
- define N_H2_ERR_INADEQUATE_SECURITY
- define N_H2_ERR_HTTP_1_1_REQUIRED
- define N_H2_ERR_FRAME_SIZE_ERROR
- define N_H2_ERR_FLOW_CONTROL_ERROR
- define N_H2_ERR_ENHANCE_YOUR_CALM
- define N_H2_ERR_CONNECT_ERROR
- define N_H2_ERR_COMPRESSION_ERROR
- define N_H2_ERR_CANCEL
- Source code
include/nilorea/n_http2_server.h
HTTP/2 (RFC 7540) server-side connection and stream state machine. More...
Structures
| Name | |
|---|---|
| struct | N_H2_REQUEST one complete request received on a stream; every pointer is owned by the server and valid only for the duration of the request callback |
Types
| Name | |
|---|---|
| typedef ssize_t(*)(void *io, const void *buf, size_t len) | n_h2_srv_write_fn write exactly len bytes from buf: returns len on success, -1 on error |
| typedef int(*)(void *user_data, N_H2_SERVER *srv, const N_H2_REQUEST *req) | n_h2_srv_request_cb called with each complete request; answer it with n_http2_server_respond() (or n_http2_server_rst_stream()). |
| typedef ssize_t(*)(void *io, void *buf, size_t len) | n_h2_srv_read_fn read up to len bytes into buf: returns the bytes read (>0), 0 on a clean end of stream, or -1 on error (a timeout included) |
Functions
| Name | |
|---|---|
| int | n_http2_server_run(N_H2_SERVER * srv, n_h2_srv_request_cb cb, void * user_data) serve the connection until it ends |
| int | n_http2_server_rst_stream(N_H2_SERVER * srv, uint32_t stream_id, uint32_t error_code) abort one stream with RST_STREAM |
| int | n_http2_server_respond(N_H2_SERVER * srv, uint32_t stream_id, const N_H2_HEADER * headers, size_t nheaders, const unsigned char * body, size_t body_len) write a complete response on a stream (HEADERS then DATA, END_STREAM) |
| size_t | n_http2_server_request_count(const N_H2_SERVER * srv) the number of requests delivered to the callback so far |
| N_H2_SERVER * | n_http2_server_new(void * io, n_h2_srv_read_fn rd, n_h2_srv_write_fn wr) create a server connection over the given I/O callbacks |
| int | n_http2_server_handshake(N_H2_SERVER * srv) read and validate the client connection preface, then send ours |
| int | n_http2_server_goaway(N_H2_SERVER * srv, uint32_t error_code) send GOAWAY, naming the last stream we processed |
| void | n_http2_server_free(N_H2_SERVER ** srv) free a server connection and NULL the caller's pointer |
| const char * | n_http2_server_error(const N_H2_SERVER * srv) the reason the connection failed, or NULL when none was recorded |
Defines
| Name | |
|---|---|
| N_H2_SRV_READ_CHUNK bytes pulled from the read callback per call |
|
| N_H2_SRV_MAX_STREAMS largest number of streams tracked at once on one connection |
|
| N_H2_SRV_MAX_HEADERS largest number of header fields decoded from one request |
|
| N_H2_SRV_MAX_BODY largest request body accumulated for one stream |
|
| N_H2_ERR_STREAM_CLOSED frame received for a closed stream |
|
| N_H2_ERR_SETTINGS_TIMEOUT settings not acknowledged |
|
| N_H2_ERR_REFUSED_STREAM stream not processed |
|
| N_H2_ERR_PROTOCOL_ERROR unspecific protocol error |
|
| N_H2_ERR_NO_ERROR graceful shutdown |
|
| N_H2_ERR_INTERNAL_ERROR implementation fault |
|
| N_H2_ERR_INADEQUATE_SECURITY negotiated TLS parameters not acceptable |
|
| N_H2_ERR_HTTP_1_1_REQUIRED use HTTP/1.1 for the request |
|
| N_H2_ERR_FRAME_SIZE_ERROR frame size incorrect |
|
| N_H2_ERR_FLOW_CONTROL_ERROR flow-control limits exceeded |
|
| N_H2_ERR_ENHANCE_YOUR_CALM processing capacity exceeded |
|
| N_H2_ERR_CONNECT_ERROR TCP connection error for CONNECT. |
|
| N_H2_ERR_COMPRESSION_ERROR HPACK compression state not updated. |
|
| N_H2_ERR_CANCEL stream cancelled |
Detailed Description
HTTP/2 (RFC 7540) server-side connection and stream state machine.
Author: Castagnier Mickael
Version: 1.0
Date: 2026
The server half of HTTP/2, built on the framing and HPACK codec of n_http2.h: it validates the client connection preface, exchanges SETTINGS, reassembles HEADERS plus CONTINUATION per stream, accumulates DATA until END_STREAM, hands each complete request to a callback, and writes responses back as HEADERS plus DATA. It answers PING, honours RST_STREAM, and closes with GOAWAY.
Both directions are flow controlled at the connection AND the stream level: the receive windows are replenished as request bodies are consumed, and a response body is never written past the peer's advertised window (when a window is exhausted the server pumps incoming frames until a WINDOW_UPDATE reopens it). A server that skips this works against small responses and deadlocks against large ones.
Byte I/O goes through injected read/write callbacks rather than a socket, so the same core runs over a plain connection (h2c prior knowledge, and the tests against a scripted peer) or a TLS connection that negotiated ALPN "h2"; the callbacks own the timeouts and the TLS.
Requests are delivered one at a time: the callback for a stream returns before the next stream's request is delivered, so a handler may block. Streams are still multiplexed on the wire (the client may open many at once, and their frames keep being read and buffered), they are simply served in the order they complete.
Types Documentation
typedef n_h2_srv_write_fn
typedef ssize_t(* n_h2_srv_write_fn) (void *io, const void *buf, size_t len);
write exactly len bytes from buf: returns len on success, -1 on error
typedef n_h2_srv_request_cb
typedef int(* n_h2_srv_request_cb) (void *user_data, N_H2_SERVER *srv, const N_H2_REQUEST *req);
called with each complete request; answer it with n_http2_server_respond() (or n_http2_server_rst_stream()).
Return 0 to keep serving the connection, -1 to stop n_http2_server_run().
typedef n_h2_srv_read_fn
typedef ssize_t(* n_h2_srv_read_fn) (void *io, void *buf, size_t len);
read up to len bytes into buf: returns the bytes read (>0), 0 on a clean end of stream, or -1 on error (a timeout included)
Functions Documentation
function n_http2_server_run
int n_http2_server_run(
N_H2_SERVER * srv,
n_h2_srv_request_cb cb,
void * user_data
)
serve the connection until it ends
Parameters:
- srv the handshaked server connection, not NULL
- cb the request callback, not NULL
- user_data opaque pointer handed to the callback
Return: 0 when the connection ended cleanly (peer close, GOAWAY, or a callback stop), -1 on a protocol or I/O error
Reads frames, reassembles requests, and calls cb once per complete request. Returns when the peer closes or sends GOAWAY, when the callback returns -1, or on an error.
< graceful shutdown
< graceful shutdown
function n_http2_server_rst_stream
int n_http2_server_rst_stream(
N_H2_SERVER * srv,
uint32_t stream_id,
uint32_t error_code
)
abort one stream with RST_STREAM
Parameters:
- srv the server connection, not NULL
- stream_id the stream to reset
- error_code an N_H2_ERR_* code
Return: 0 on success, -1 on error
< RST_STREAM
function n_http2_server_respond
int n_http2_server_respond(
N_H2_SERVER * srv,
uint32_t stream_id,
const N_H2_HEADER * headers,
size_t nheaders,
const unsigned char * body,
size_t body_len
)
write a complete response on a stream (HEADERS then DATA, END_STREAM)
Parameters:
- srv the server connection, not NULL
- stream_id the stream to answer
- headers the response header fields, :status first, not NULL
- nheaders number of header fields
- body the response body, or NULL for none
- body_len the body length
Return: 0 on success, -1 on a bad argument, a closed stream, or an I/O error
headers must start with the :status pseudo-header and hold no connection-specific field (RFC 7540 8.1.2.2 forbids them). The body is split into DATA frames at the peer's SETTINGS_MAX_FRAME_SIZE and paced by the stream and connection send windows, pumping incoming frames whenever a window is exhausted.
< HEADERS/PUSH_PROMISE/CONTINUATION: header block complete
< DATA/HEADERS: last frame of the stream
< HEADERS
< CONTINUATION
< DATA
< DATA/HEADERS: last frame of the stream
function n_http2_server_request_count
size_t n_http2_server_request_count(
const N_H2_SERVER * srv
)
the number of requests delivered to the callback so far
function n_http2_server_new
N_H2_SERVER * n_http2_server_new(
void * io,
n_h2_srv_read_fn rd,
n_h2_srv_write_fn wr
)
create a server connection over the given I/O callbacks
Parameters:
- io opaque handle handed to the callbacks
- rd read callback, not NULL
- wr write callback, not NULL
Return: the new server, or NULL on a bad argument or an allocation failure
function n_http2_server_handshake
int n_http2_server_handshake(
N_H2_SERVER * srv
)
read and validate the client connection preface, then send ours
Parameters:
- srv the server connection, not NULL
Return: 0 on success, -1 on a bad preface, a protocol error, or an I/O error
Reads the 24-byte preface, requires the client's first frame to be SETTINGS (RFC 7540 3.5), acknowledges it, and sends our own SETTINGS plus the connection-level WINDOW_UPDATE that opens our receive window.
< SETTINGS
< SETTINGS
< SETTINGS/PING: acknowledgement
function n_http2_server_goaway
int n_http2_server_goaway(
N_H2_SERVER * srv,
uint32_t error_code
)
send GOAWAY, naming the last stream we processed
Parameters:
- srv the server connection, not NULL
- error_code an N_H2_ERR_* code
Return: 0 on success, -1 on error
< GOAWAY
function n_http2_server_free
void n_http2_server_free(
N_H2_SERVER ** srv
)
free a server connection and NULL the caller's pointer
function n_http2_server_error
const char * n_http2_server_error(
const N_H2_SERVER * srv
)
the reason the connection failed, or NULL when none was recorded
Macros Documentation
define N_H2_SRV_READ_CHUNK
#define N_H2_SRV_READ_CHUNK 16384
bytes pulled from the read callback per call
define N_H2_SRV_MAX_STREAMS
#define N_H2_SRV_MAX_STREAMS 128
largest number of streams tracked at once on one connection
define N_H2_SRV_MAX_HEADERS
#define N_H2_SRV_MAX_HEADERS 128
largest number of header fields decoded from one request
define N_H2_SRV_MAX_BODY
#define N_H2_SRV_MAX_BODY (16 * 1024 * 1024)
largest request body accumulated for one stream
define N_H2_ERR_STREAM_CLOSED
#define N_H2_ERR_STREAM_CLOSED 0x5
frame received for a closed stream
define N_H2_ERR_SETTINGS_TIMEOUT
#define N_H2_ERR_SETTINGS_TIMEOUT 0x4
settings not acknowledged
define N_H2_ERR_REFUSED_STREAM
#define N_H2_ERR_REFUSED_STREAM 0x7
stream not processed
define N_H2_ERR_PROTOCOL_ERROR
#define N_H2_ERR_PROTOCOL_ERROR 0x1
unspecific protocol error
define N_H2_ERR_NO_ERROR
#define N_H2_ERR_NO_ERROR 0x0
graceful shutdown
define N_H2_ERR_INTERNAL_ERROR
#define N_H2_ERR_INTERNAL_ERROR 0x2
implementation fault
define N_H2_ERR_INADEQUATE_SECURITY
#define N_H2_ERR_INADEQUATE_SECURITY 0xc
negotiated TLS parameters not acceptable
define N_H2_ERR_HTTP_1_1_REQUIRED
#define N_H2_ERR_HTTP_1_1_REQUIRED 0xd
use HTTP/1.1 for the request
define N_H2_ERR_FRAME_SIZE_ERROR
#define N_H2_ERR_FRAME_SIZE_ERROR 0x6
frame size incorrect
define N_H2_ERR_FLOW_CONTROL_ERROR
#define N_H2_ERR_FLOW_CONTROL_ERROR 0x3
flow-control limits exceeded
define N_H2_ERR_ENHANCE_YOUR_CALM
#define N_H2_ERR_ENHANCE_YOUR_CALM 0xb
processing capacity exceeded
define N_H2_ERR_CONNECT_ERROR
#define N_H2_ERR_CONNECT_ERROR 0xa
TCP connection error for CONNECT.
define N_H2_ERR_COMPRESSION_ERROR
#define N_H2_ERR_COMPRESSION_ERROR 0x9
HPACK compression state not updated.
define N_H2_ERR_CANCEL
#define N_H2_ERR_CANCEL 0x8
stream cancelled
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_HTTP2_SERVER_HEADER
#define __NILOREA_HTTP2_SERVER_HEADER
#ifdef __cplusplus
extern "C" {
#endif
#include "n_http2.h"
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#define N_H2_SRV_MAX_HEADERS 128
#define N_H2_SRV_MAX_BODY (16 * 1024 * 1024)
#define N_H2_SRV_MAX_STREAMS 128
#define N_H2_SRV_READ_CHUNK 16384
#define N_H2_ERR_NO_ERROR 0x0
#define N_H2_ERR_PROTOCOL_ERROR 0x1
#define N_H2_ERR_INTERNAL_ERROR 0x2
#define N_H2_ERR_FLOW_CONTROL_ERROR 0x3
#define N_H2_ERR_SETTINGS_TIMEOUT 0x4
#define N_H2_ERR_STREAM_CLOSED 0x5
#define N_H2_ERR_FRAME_SIZE_ERROR 0x6
#define N_H2_ERR_REFUSED_STREAM 0x7
#define N_H2_ERR_CANCEL 0x8
#define N_H2_ERR_COMPRESSION_ERROR 0x9
#define N_H2_ERR_CONNECT_ERROR 0xa
#define N_H2_ERR_ENHANCE_YOUR_CALM 0xb
#define N_H2_ERR_INADEQUATE_SECURITY 0xc
#define N_H2_ERR_HTTP_1_1_REQUIRED 0xd
typedef ssize_t (*n_h2_srv_read_fn)(void* io, void* buf, size_t len);
typedef ssize_t (*n_h2_srv_write_fn)(void* io, const void* buf, size_t len);
typedef struct N_H2_SERVER N_H2_SERVER;
typedef struct N_H2_REQUEST {
uint32_t stream_id;
N_H2_HEADER* headers;
size_t nheaders;
const char* method;
const char* scheme;
const char* authority;
const char* path;
const unsigned char* body;
size_t body_len;
} N_H2_REQUEST;
typedef int (*n_h2_srv_request_cb)(void* user_data, N_H2_SERVER* srv, const N_H2_REQUEST* req);
N_H2_SERVER* n_http2_server_new(void* io, n_h2_srv_read_fn rd, n_h2_srv_write_fn wr);
void n_http2_server_free(N_H2_SERVER** srv);
int n_http2_server_handshake(N_H2_SERVER* srv);
int n_http2_server_run(N_H2_SERVER* srv, n_h2_srv_request_cb cb, void* user_data);
int n_http2_server_respond(N_H2_SERVER* srv, uint32_t stream_id, const N_H2_HEADER* headers, size_t nheaders, const unsigned char* body, size_t body_len);
int n_http2_server_rst_stream(N_H2_SERVER* srv, uint32_t stream_id, uint32_t error_code);
int n_http2_server_goaway(N_H2_SERVER* srv, uint32_t error_code);
const char* n_http2_server_error(const N_H2_SERVER* srv);
size_t n_http2_server_request_count(const N_H2_SERVER* srv);
#ifdef __cplusplus
}
#endif
#endif /* __NILOREA_HTTP2_SERVER_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