DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
quic_server.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file quic_server.h
6 * @brief HTTP/3 server glue - binds UDP to a pool of QUIC + HTTP/3 connections (RFC 9000/9114).
7 *
8 * The last piece of the HTTP/3 stack: it owns a fixed pool of QuicConn + H3Conn engines, binds the
9 * HTTP/3 UDP port through the transport layer (det_udp), routes each inbound datagram to the right
10 * connection by its Destination Connection ID (a new client Initial opens a pool slot), drives the
11 * handshake + streams, and pulls the outbound datagrams back onto the wire. A completed HTTP/3
12 * request is surfaced through a single callback; the application answers with quic_server_respond().
13 *
14 * Threading (ESP32): det_udp delivers datagrams on the lwIP thread, but requests must be dispatched
15 * on the server's worker/main loop, so the UDP handler only copies each datagram into a lock-free
16 * ingest ring; quic_server_poll() (called from the loop) drains the ring, runs the engines, and
17 * sends replies. The engines therefore only ever run in one context. On host builds there is no UDP;
18 * datagrams are injected with quic_server_ingest() and replies captured through an output sink, so
19 * the whole server is exercised by shuttling byte buffers between it and a test client.
20 *
21 * The pool (QuicConn + H3Conn per slot + the ingest ring) is large, so like HTTP/2 it is a
22 * PSRAM-class feature. No heap; fixed storage. This module has no DetWebServer dependency - the
23 * request/response seam is a plain callback - so the route-dispatch bridge lives in the server.
24 *
25 * @author Douglas Quigg (dstroy0)
26 * @date 2026
27 */
28
29#ifndef DETERMINISTICESPASYNCWEBSERVER_QUIC_SERVER_H
30#define DETERMINISTICESPASYNCWEBSERVER_QUIC_SERVER_H
31
32#include "ServerConfig.h"
33
34#if DETWS_ENABLE_HTTP3
35
38#include <stddef.h>
39#include <stdint.h>
40
41#ifndef DETWS_QUIC_MAX_CONNS
42#define DETWS_QUIC_MAX_CONNS 2 ///< simultaneous HTTP/3 connections (each is a QuicConn + H3Conn, PSRAM-class)
43#endif
44#ifndef DETWS_QUIC_INGEST_RING
45#define DETWS_QUIC_INGEST_RING 8 ///< datagrams buffered from the lwIP thread until quic_server_poll() drains them
46#endif
47#ifndef DETWS_HTTP3_PORT
48#define DETWS_HTTP3_PORT 443 ///< default UDP port the HTTP/3 server binds (QUIC)
49#endif
50#ifndef DETWS_QUIC_SCID_LEN
51#define DETWS_QUIC_SCID_LEN 8 ///< length of the connection ID the server chooses for itself
52#endif
53#ifndef DETWS_QUIC_IDLE_MS
54#define DETWS_QUIC_IDLE_MS 30000 ///< reclaim a connection idle this long (also advertised as max_idle_timeout)
55#endif
56
57/**
58 * @brief A completed HTTP/3 request handed to the application on the poll thread.
59 *
60 * Reply synchronously with quic_server_respond(@p conn_id, @p stream_id, ...) (typically from inside
61 * this call). @p body / @p body_len are valid only during the call.
62 */
63typedef void (*QuicServerRequestFn)(void *app, uint32_t conn_id, uint64_t stream_id, const char *method,
64 const char *path, const char *authority, const uint8_t *body, size_t body_len);
65
66/** @brief Server configuration: the Ed25519 leaf certificate + its key, and a randomness source. */
67struct QuicServerConfig
68{
69 const uint8_t *cert_der; ///< DER X.509 leaf certificate (Ed25519 public key)
70 size_t cert_len;
71 uint8_t ed25519_seed[32]; ///< Ed25519 private seed matching the certificate
72 void (*rng)(uint8_t *out, size_t len); ///< fills @p out with @p len random bytes (ephemeral keys, SCIDs)
73};
74
75/**
76 * @brief Start the HTTP/3 server: install @p cfg, bind @p port over UDP, and route datagrams into the
77 * connection pool. @p on_request is invoked (on the poll thread) for each completed request.
78 * @return false if UDP is unavailable (host build) or the bind fails; the server is still usable on
79 * host builds through quic_server_ingest() / the output sink.
80 */
81bool quic_server_begin(uint16_t port, const QuicServerConfig *cfg, QuicServerRequestFn on_request, void *app);
82
83/**
84 * @brief Drive the server once: drain queued inbound datagrams into their connections, run the
85 * handshake + HTTP/3 engines (which may fire @p on_request), and flush outbound datagrams. Call every
86 * loop iteration. @p now_ms is the caller's monotonic millisecond clock (the module stays
87 * platform-agnostic); closed or idle (DETWS_QUIC_IDLE_MS) connections are reaped here.
88 */
89void quic_server_poll(uint32_t now_ms);
90
91/**
92 * @brief Send an HTTP/3 response (HEADERS + DATA, finishing the stream) for @p stream_id on the
93 * connection @p conn_id. Call from within the request callback. @return false on a stale conn_id /
94 * stream or a serialization overflow.
95 */
96bool quic_server_respond(uint32_t conn_id, uint64_t stream_id, int status, const char *content_type,
97 const uint8_t *body, size_t body_len);
98
99/** @brief Number of pool slots currently in use (open connections). For diagnostics / tests. */
100uint8_t quic_server_active_conns(void);
101
102/** @brief Stop the server: close the UDP binding and release every pool slot. */
103void quic_server_stop(void);
104
105// ---------------------------------------------------------------------------
106// Host / test seam (no UDP on host builds)
107// ---------------------------------------------------------------------------
108#if !defined(ARDUINO)
109/** @brief Sink invoked for every outbound datagram (host builds route sends here instead of UDP). */
110typedef void (*QuicServerOutFn)(void *ctx, const uint8_t *datagram, size_t len, const char *ip, uint16_t port);
111
112/** @brief Register the outbound-datagram sink used on host builds. */
113void quic_server_set_out_sink(QuicServerOutFn fn, void *ctx);
114
115/**
116 * @brief Inject a received datagram from @p ip:@p port (the host-build stand-in for the UDP handler).
117 * quic_server_poll() then processes it exactly as a real datagram. @return false if the ring is full.
118 */
119bool quic_server_ingest(const uint8_t *datagram, size_t len, const char *ip, uint16_t port);
120#endif
121
122#endif // DETWS_ENABLE_HTTP3
123#endif // DETERMINISTICESPASYNCWEBSERVER_QUIC_SERVER_H
User-facing configuration for DeterministicESPAsyncWebServer.
HTTP/3 application engine over QUIC streams (RFC 9114).
Stateful QUIC v1 server connection engine (RFC 9000 / RFC 9001).