6 n__http2__server_8h
gullradriel edited this page 2026-10-09 00:55:18 +02:00

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 */