|
wired
libc-free QUIC/HTTP3 SDK in C for x86_64-linux
|
The complete server event loop: the sanctioned socket-owning layer over the socket-free srvboot/srvloop core. More...
#include "app/http3/server/srvboot/srvboot.h"#include "app/http3/server/srvinbox/srvinbox.h"#include "app/http3/server/srvloop/srvloop.h"#include "app/http3/server/srvxdp/srvxdp.h"#include "app/qpack/qpack/field.h"#include "app/webtransport/capsule/wtcapsule/wtcapsule.h"#include "app/webtransport/session/session/session.h"Go to the source code of this file.
Data Structures | |
| struct | wired_wt_resource_decision |
| draft-ietf-webtrans-http3-16 SS3.2 (WTH3-016/WTH3-018): the app's verdict for one Extended CONNECT's :authority/:path, filled in by a registered wired_wt_resource_check before the session is established. More... | |
| struct | wired_http_exchange |
| One request/response exchange handed to a wired_http_handler. More... | |
| struct | wired_srvrun_handler |
| The application's request responder: the callback and its opaque context, registered on the loop as a pair (wired_srvloop_set_handler takes the same pair). More... | |
| struct | wired_srvrun_obs |
| Optional debug-log file paths, each 0 to disable (the default): a qlog (RFC 9002-shaped packet_sent/packet_received events, JSON-SEQ framed) and an NSS key log (SSLKEYLOGFILE format) for decrypting a capture in Wireshark. More... | |
| struct | wired_srvrun_opt |
| Opt-in polling-driver knobs, both off (0) by default so wired_server_run's behavior is unchanged. More... | |
| struct | wired_srvrun_wt_usage |
| draft-ietf-webtrans-http3-15 SS8: WebTransport feature-usage totals for one env, monotonic since wired_srvrun_env_init – the operational visibility the draft asks servers to keep for anomaly detection. More... | |
| struct | wired_wt_sched |
| A server-sent stream's send class and its place inside a flow, set by wired_server_wt_stream_schedule (the MoQT hub's draft-ietf-moq-transport -22 7.2 order on top of the RFC 9218 urgency). More... | |
| struct | wired_wt_occupancy |
| Current WebTransport occupancy of the server loop the caller runs in, against the compile-time capacities – for an app exposing stats. More... | |
Macros | |
| #define | WIRED_HTTP_MAX_FIELDS 8 |
| Most response header fields one wired_http_handler round may set. | |
| #define | WIRED_HTTP_FIELD_BYTES_MAX 384 |
| Upper bound on the summed name, value and content_type bytes of one response's header fields, so the framed HEADERS always fits the response row's header room. | |
Typedefs | |
| typedef struct wired_srvrun_env | wired_srvrun_env |
| Opaque: one server loop instance's mutable state (connection table, response/rx storage, pacing scratch). | |
| typedef void(* | wired_wt_on_datagram) (void *app_ctx, wired_wt_session *s, wired_span data) |
| draft-ietf-webtrans-http3-15 SS4: app-facing delivery of one received QUIC DATAGRAM (RFC 9221 5) associated with a WebTransport session, drained from wired_srvloop's rx_datagrams queue once per connection step. | |
| typedef void(* | wired_wt_on_stream_data) (void *app_ctx, wired_wt_session *s, u64 stream_id, wired_span data, int fin) |
| draft-ietf-webtrans-http3-15 4.3: app-facing delivery of one reassembled chunk of a WebTransport bidi/uni stream's bytes, drained from wired_srvloop's wt_streams/wt_uni_streams once per connection step. | |
| typedef void(* | wired_wt_on_stream_reset) (void *app_ctx, wired_wt_session *s, u64 stream_id, int mapped, u32 app_error_code) |
| draft-ietf-webtrans-http3-15 4.4 (WTH3-040): app-facing delivery of a RESET_STREAM/STOP_SENDING received on one of session s's own WT bidi/uni streams. | |
| typedef void(* | wired_wt_on_session) (void *app_ctx, wired_wt_session *s, wired_span path, wired_span protocol) |
| WebTransport subprotocol negotiation (draft-ietf-webtrans-http3-15 SS3.4): app-facing notification that a WebTransport session was established (its 2xx accept response has been built). | |
| typedef void(* | wired_wt_on_session_close) (void *app_ctx, wired_wt_session *s) |
| App-facing notification that a WebTransport session ended server-side: its own CONNECT stream closed, it violated a WT flow-control limit, the server sent WT_CLOSE_SESSION, or its whole connection tore down (peer CONNECTION_CLOSE, boot failure, or the idle-timeout sweep). | |
| typedef void(* | wired_wt_on_session_draining) (void *app_ctx, wired_wt_session *s) |
| draft-ietf-webtrans-http3-15 4.7: the peer sent WT_DRAIN_SESSION on s's CONNECT stream – an advisory request to wind the session down soon. | |
| typedef void(* | wired_rawq_on_session) (void *app_ctx, wired_wt_session *s, wired_span alpn) |
| draft-ietf-moq-transport-19 3.1.5 / -22 6.2.2 (native QUIC): app-facing notification that a raw-QUIC application connection (its ALPN one of wired_srvboot_id.raw_alpns) got its implicit session. | |
| typedef void(* | wired_wt_resource_check) (void *app_ctx, wired_span authority, wired_span path, wired_wt_resource_decision *out) |
| draft-ietf-webtrans-http3-15 SS3.2: app-facing resource lookup for one Extended CONNECT, called before Origin verification/session establishment. | |
| typedef void(* | wired_srvrun_on_step) (void *ctx, u64 now_ms) |
| Per-step application hook (wired_srvrun_opt.on_step). | |
| typedef qpack_field | wired_http_field |
| One response header field line: lowercase name and value views (RFC 9114 4.2). | |
| typedef int(* | wired_http_handler) (void *ctx, wired_http_exchange *x) |
| Build (a round of) the response for a decoded request: like wired_srvloop_handler, but able to choose the status and add response header fields (see wired_http_exchange). | |
Functions | |
| int | wired_server_run (u16 port, wired_srvboot_id *id, wired_srvrun_handler h, wired_srvrun_obs obs) |
| The complete server event loop: bind a UDP socket on port, then forever receive datagrams and drive them — a fresh client Initial cold-starts a connection (wired_srvboot_accept), any later datagram steps the live loop (wired_srvloop_step) — sealing every reply straight back. | |
| int | wired_server_run_opt (u16 port, wired_srvboot_id *id, wired_srvrun_handler h, wired_srvrun_obs obs, const wired_srvrun_opt *opt) |
| Same as wired_server_run, plus opt-in polling-driver behavior. | |
| usz | wired_srvrun_env_size (void) |
| Byte size of one wired_srvrun_env instance, for a caller allocating one (statically, or via an SDK-external allocator) to pass to wired_srvrun_env_init / wired_srvrun_serve_env. | |
| void | wired_srvrun_env_init (wired_srvrun_env *env) |
| Zero-initialize env (its connection table, response/rx storage, and pacing scratch) before its first use with wired_srvrun_serve_env. | |
| void | wired_srvrun_env_wt_usage (const wired_srvrun_env *env, wired_srvrun_wt_usage *out) |
| Snapshot env's WebTransport usage counters into *out. | |
| int | wired_srvrun_serve_env (wired_srvrun_env *env, u16 port, wired_srvboot_id *id, wired_srvrun_handler h, wired_srvrun_obs obs, const wired_srvrun_opt *opt) |
| Same as wired_server_run_opt, but driven off caller-owned env instead of the SDK's single process-wide instance – lets a caller run more than one independent server loop in the same process (e.g. | |
| int * | wired_srvrun_shutdown_word (void) |
| The process-wide graceful-shutdown word (RFC 9114 5.2): 0 until a shutdown is requested, then monotonically 1 (never reset outside test teardown). | |
| int | wired_server_broadcast_datagram (wired_span data) |
| RFC 9221 5 / draft-ietf-webtrans-http3-15 SS4: broadcast data as a QUIC DATAGRAM to every connection with an active WebTransport session (queued for each such connection's own next step, same single-slot last-writer- wins contract as this SDK's per-connection DATAGRAM queue). | |
| int | wired_server_broadcast_datagram_ring (wired_span data) |
| Queue one HTTP Datagram (RFC 9297) to EVERY active WebTransport session in the caller's server loop – the burst-safe sibling of wired_server_broadcast_datagram. | |
| i64 | wired_server_wt_open_uni (wired_wt_session *s, wired_span payload) |
| Open a new server-initiated unidirectional stream on s's connection (RFC 9000 2.1: id 3 mod 4) and send the whole payload on it, closing with FIN on the final slice. | |
| i64 | wired_server_wt_open_bidi (wired_wt_session *s, wired_span payload) |
| Same as wired_server_wt_open_uni, but a server-initiated bidirectional stream (RFC 9000 2.1: id 1 mod 4; signal prefix varint 0x41). | |
| int | wired_server_wt_stream_reply (wired_wt_session *s, u64 stream_id, wired_span payload) |
| Send payload on this endpoint's send direction of the client-initiated bidirectional stream stream_id (a WebTransport data stream the client opened), closing with FIN on the final slice. | |
| i64 | wired_server_wt_open_uni_stream (wired_wt_session *s, wired_span payload) |
| Same as wired_server_wt_open_uni, but WITHOUT closing the stream: no FIN is sent after payload's final slice, and the stream stays open for further wired_server_wt_stream_send rounds until one carries fin=1 (or wired_server_wt_stream_reset aborts it). | |
| i64 | wired_server_wt_open_bidi_stream (wired_wt_session *s, wired_span payload) |
| Same as wired_server_wt_open_uni_stream, but a server-initiated bidirectional stream (RFC 9000 2.1: id 1 mod 4; signal prefix varint 0x41), its receive side pre-registered like wired_server_wt_open_bidi. | |
| int | wired_server_wt_stream_reply_open (wired_wt_session *s, u64 stream_id, wired_span payload) |
| Same as wired_server_wt_stream_reply, but WITHOUT closing the stream – the opening round of a reply on a client-initiated bidi stream that stays open for further wired_server_wt_stream_send rounds. | |
| int | wired_server_wt_stream_send (wired_wt_session *s, u64 stream_id, wired_span payload, int fin) |
| Append one more round of bytes to a stream opened with wired_server_wt_ open_uni_stream/open_bidi_stream/stream_reply_open: payload goes out at the stream's cumulative offset (RFC 9000 19.8), and fin=1 ends the stream with FIN on this round's final slice, after which the send slot frees itself once fully acknowledged (the one-shot opens' behavior). | |
| int | wired_server_wt_stream_priority (wired_wt_session *s, u64 stream_id, u8 urgency) |
| Set the send urgency of a server-sent stream that still holds its send slot (RFC 9218 2.1: 0 most urgent . | |
| int | wired_server_wt_stream_schedule (wired_wt_session *s, u64 stream_id, const wired_wt_sched *k) |
| Set a server-sent stream's full send class (see wired_wt_sched). | |
| u64 | wired_server_wt_est_kbps (wired_wt_session *s) |
| The connection's current delivery-rate estimate for s, in kbps: BBR's bottleneck bandwidth once it has a sample, else cwnd / smoothed RTT. | |
| int | wired_server_wt_stream_fin (wired_wt_session *s, u64 stream_id) |
| Ends a stream opened for appending with NO further bytes: a bare FIN on an otherwise-empty final round (RFC 9000 19.8 permits a zero-length STREAM frame carrying only FIN). | |
| int | wired_server_wt_stream_hold (wired_wt_session *s, u64 stream_id, int hold) |
| Hold or release the receive flow-control credit of a client-initiated stream (RFC 9000 4.1/19.10): while held, the loop stops RAISING the advertised MAX_STREAM_DATA for stream_id as the app consumes its bytes, so the peer's remaining send window drains and it pauses – backpressure without discarding anything. | |
| int | wired_server_wt_stream_reset (wired_wt_session *s, u64 stream_id, u32 error_code) |
| Abort a stream opened for appending: queue a standard RESET_STREAM (RFC 9000 19.4) on stream_id carrying error_code mapped into HTTP/3's WebTransport range (draft-ietf-webtrans-http3-15 SS4.4/8.2) and the stream's bytes-sent-so-far as its Final Size, delivered on one of the loop's next steps. | |
| int | wired_server_wt_stream_stop (wired_wt_session *s, u64 stream_id, u32 error_code) |
| Asks the peer to stop sending on a PEER-initiated stream: queues one STOP_SENDING (RFC 9000 19.5) carrying error_code mapped into HTTP/3's WebTransport range, kept for resending until ACKed like a reset (RFC 9000 13.3). | |
| int | wired_server_wt_stream_inflight (wired_wt_session *s, u64 stream_id) |
| 1 while a send slot on s's connection still holds stream_id – its bytes not yet fully acknowledged, or the stream still open for appends – and 0 once the slot was reaped or stream_id never named a server-sent stream on this session. | |
| int | wired_server_wt_send_datagram_to (wired_wt_session *s, wired_span payload) |
| Queue one HTTP Datagram (RFC 9297) to this session's peer: the SDK prefixes the quarter-stream-id varint (the session's CONNECT stream id / 4, RFC 9297 2.1) and sends it as a QUIC DATAGRAM (RFC 9221) on one of the loop's next steps. | |
| int | wired_server_wt_close_session (wired_wt_session *s, u32 app_error_code, wired_span message) |
| draft-ietf-webtrans-http3-15 SS4.2/SS4.4/8.2 (WTH3-067): close s's WebTransport session: send a WT_CLOSE_SESSION capsule (app_error_code, message) followed immediately by FIN on the session's own CONNECT stream, then reset every other WT stream the session owns with WT_SESSION_GONE and transition the session to closed. | |
| int | wired_server_wt_drain_session (wired_wt_session *s) |
| draft-ietf-webtrans-http3-15 4.7: ask the peer to wind s down by sending a WT_DRAIN_SESSION capsule on s's CONNECT stream. | |
| int | wired_server_session_is_raw (wired_wt_session *s) |
| 1 when s is the implicit session of a raw-QUIC application connection (wired_rawq_on_session), 0 for a WebTransport session or when s resolves to no live connection. | |
| int | wired_server_wt_occupancy (wired_wt_occupancy *out) |
| Snapshot the calling loop's WebTransport occupancy into *out. | |
| void | wired_srvrun_broadcast_register (int index, int n_total, wired_srvinbox_ring *inbox_row, wired_srvrun_env *env) |
| Register the calling thread as srvthreads worker index of n_total, with its own N-ring inbox row (inbox_row[j] receives broadcasts sent by worker j, j != index only – the caller's own broadcasts reach its own connections directly, not through this row, so double-delivery to itself never happens) and its own env (the connection table wired_server_broadcast_datagram fans out into when this thread is the caller). | |
| void | wired_srvrun_broadcast_unregister (void) |
| Unregister the calling thread's broadcast registry entry (symmetric with wired_srvrun_broadcast_register), typically at worker shutdown. | |
The complete server event loop: the sanctioned socket-owning layer over the socket-free srvboot/srvloop core.
| typedef qpack_field wired_http_field |
One response header field line: lowercase name and value views (RFC 9114 4.2).
Both must stay valid after the handler returns (static or ctx-owned storage): they are encoded once the call is over.
| typedef int(* wired_http_handler) (void *ctx, wired_http_exchange *x) |
Build (a round of) the response for a decoded request: like wired_srvloop_handler, but able to choose the status and add response header fields (see wired_http_exchange).
| ctx | the opaque context registered alongside this callback |
| x | the exchange to fill in |
| typedef void(* wired_rawq_on_session) (void *app_ctx, wired_wt_session *s, wired_span alpn) |
draft-ietf-moq-transport-19 3.1.5 / -22 6.2.2 (native QUIC): app-facing notification that a raw-QUIC application connection (its ALPN one of wired_srvboot_id.raw_alpns) got its implicit session.
The session is created when the handshake is confirmed and is reported before any of its stream bytes are delivered; it then flows through the same wt_on_stream_data/wt_on_datagram/wt_on_stream_reset/wt_on_session_close callbacks as a WebTransport session (wired_server_session_is_raw tells them apart). alpn is a view into the static raw_alpns configuration.
| app_ctx | opaque context registered alongside this callback |
| s | the connection's implicit session |
| alpn | the negotiated ALPN protocol id, e.g. "moqt-19" |
| typedef struct wired_srvrun_env wired_srvrun_env |
Opaque: one server loop instance's mutable state (connection table, response/rx storage, pacing scratch).
wired_server_run/wired_server_run_opt drive a single process-wide instance of this internally; wired_srvrun_env_* plus wired_srvrun_serve_env let a caller allocate and run additional, independent instances (e.g. one per thread). Never dereference members directly – allocate wired_srvrun_env_size() bytes and pass the pointer through.
| typedef void(* wired_srvrun_on_step) (void *ctx, u64 now_ms) |
Per-step application hook (wired_srvrun_opt.on_step).
| ctx | opaque context registered alongside this callback |
| now_ms | the loop's monotonic clock at this step |
| typedef void(* wired_wt_on_datagram) (void *app_ctx, wired_wt_session *s, wired_span data) |
draft-ietf-webtrans-http3-15 SS4: app-facing delivery of one received QUIC DATAGRAM (RFC 9221 5) associated with a WebTransport session, drained from wired_srvloop's rx_datagrams queue once per connection step.
data is a view into per-step scratch, not valid past the call.
| app_ctx | opaque context registered alongside this callback |
| s | the session the datagram is associated with |
| data | the datagram's payload bytes |
| typedef void(* wired_wt_on_session) (void *app_ctx, wired_wt_session *s, wired_span path, wired_span protocol) |
WebTransport subprotocol negotiation (draft-ietf-webtrans-http3-15 SS3.4): app-facing notification that a WebTransport session was established (its 2xx accept response has been built).
path and protocol are views into per-connection/per-call scratch, not valid past the call.
| app_ctx | opaque context registered alongside this callback |
| s | the session that was just established |
| path | the Extended CONNECT's :path value |
| protocol | the negotiated subprotocol (the raw token, not its sf-string encoding); empty when no subprotocol was negotiated |
| typedef void(* wired_wt_on_session_close) (void *app_ctx, wired_wt_session *s) |
App-facing notification that a WebTransport session ended server-side: its own CONNECT stream closed, it violated a WT flow-control limit, the server sent WT_CLOSE_SESSION, or its whole connection tore down (peer CONNECTION_CLOSE, boot failure, or the idle-timeout sweep).
Fires once per session, before the session object's storage can be reused by a later connection – an app keying its own per-session state on the wired_wt_session pointer MUST release that state here, or a reconnecting client landing in the reused slot inherits the dead session's identity. s is only valid for the duration of the call.
| app_ctx | opaque context registered alongside this callback |
| s | the session that ended |
| typedef void(* wired_wt_on_session_draining) (void *app_ctx, wired_wt_session *s) |
draft-ietf-webtrans-http3-15 4.7: the peer sent WT_DRAIN_SESSION on s's CONNECT stream – an advisory request to wind the session down soon.
The session stays open; the app decides when to close it. Fires once per session, however often the peer repeats the capsule. s is only valid for the duration of the call.
| app_ctx | opaque context registered alongside this callback |
| s | the session the peer asked to drain |
| typedef void(* wired_wt_on_stream_data) (void *app_ctx, wired_wt_session *s, u64 stream_id, wired_span data, int fin) |
draft-ietf-webtrans-http3-15 4.3: app-facing delivery of one reassembled chunk of a WebTransport bidi/uni stream's bytes, drained from wired_srvloop's wt_streams/wt_uni_streams once per connection step.
data is a view into per-step scratch, not valid past the call. fin=1 marks the stream's end.
| app_ctx | opaque context registered alongside this callback |
| s | the session the stream is associated with |
| stream_id | the WT bidi/uni stream id |
| data | the stream's reassembled bytes so far past the signal/type byte |
| fin | 1 once the stream's FIN has been seen |
| typedef void(* wired_wt_on_stream_reset) (void *app_ctx, wired_wt_session *s, u64 stream_id, int mapped, u32 app_error_code) |
draft-ietf-webtrans-http3-15 4.4 (WTH3-040): app-facing delivery of a RESET_STREAM/STOP_SENDING received on one of session s's own WT bidi/uni streams.
"If a RESET_STREAM or STOP_SENDING frame is received with an error code outside the WT_APPLICATION_ERROR range, then the implementation should deliver this to the application as a stream reset with no application error code" – mapped is 1 when the wire error code fell inside the WT_APPLICATION_ERROR range (wired_wterrmap_from_http3 accepted it) and app_error_code is the recovered WebTransport application error code, or mapped is 0 (app_error_code left at 0, not itself meaningful) for a wire code outside that range or on a reserved codepoint.
| app_ctx | opaque context registered alongside this callback |
| s | the session the stream is associated with |
| stream_id | the WT bidi/uni stream id the reset arrived on |
| mapped | 1 if app_error_code is a recovered WT application error code |
| app_error_code | the recovered code, valid only when mapped is 1 |
| typedef void(* wired_wt_resource_check) (void *app_ctx, wired_span authority, wired_span path, wired_wt_resource_decision *out) |
draft-ietf-webtrans-http3-15 SS3.2: app-facing resource lookup for one Extended CONNECT, called before Origin verification/session establishment.
authority/path are views into per-call scratch, not valid past the call. out is caller-zeroed before the call (a callback that never touches it accepts the resource, see wired_wt_resource_decision's own doc).
| app_ctx | opaque context registered alongside this callback |
| authority | the Extended CONNECT's :authority value |
| path | the Extended CONNECT's :path value |
| out | the verdict to fill in |
| int wired_server_broadcast_datagram | ( | wired_span | data | ) |
RFC 9221 5 / draft-ietf-webtrans-http3-15 SS4: broadcast data as a QUIC DATAGRAM to every connection with an active WebTransport session (queued for each such connection's own next step, same single-slot last-writer- wins contract as this SDK's per-connection DATAGRAM queue).
Typically called from inside a wired_wt_on_datagram callback to fan a received message out to every other participant (e.g. a chat app) — that callback has no handle to the connection table itself, so this is the only way to reach connections other than the one that just received data. Callable only from inside the server's own loop (i.e. from a callback); this SDK's server loop is single-threaded and this call is neither signal-safe nor thread-safe.
| data | payload to broadcast; must fit the per-connection DATAGRAM cap |
Threaded fan-out: a caller running inside a srvthreads worker (registered via wired_srvrun_broadcast_register) is fanned out into ITS OWN registered env – srvthreads gives every worker its own connection table, so the single process-wide g_srvrun_env would be empty/unused for a registered worker and reach no one. With 2 or more workers registered, the payload is also pushed into every OTHER registered worker's inbox row (wired_srvinbox_push) so each one's own wired_srvrun_serve_env loop delivers it to its own WT sessions on a later step (srvrun.c drains the calling worker's inbox row once per step). An unregistered caller (the default, single-process wired_server_run(_opt) or a lone wired_srvrun_serve_env instance) fans out directly to the single g_srvrun_env.
| int wired_server_broadcast_datagram_ring | ( | wired_span | data | ) |
Queue one HTTP Datagram (RFC 9297) to EVERY active WebTransport session in the caller's server loop – the burst-safe sibling of wired_server_broadcast_datagram.
Where that API holds one pending payload per connection (a second broadcast in the same loop step overwrites the first, last-writer-wins), this one queues each target into the same bounded ring wired_server_wt_send_datagram_to uses, so several broadcasts inside one step – an audio burst, say – are all delivered on the loop's next step, none overwritten. Each target session's own quarter-stream-id varint (RFC 9297 2.1) is prefixed at queue time and the payload is copied, so it need not outlive the call. A session is a target only when its connection is up, this endpoint's SETTINGS have been sent (RFC 9297 2.1), and the session's CONNECT stream send side is still open (RFC 9297 2) – the same gates wired_server_wt_send_datagram_to applies. Frames exceeding the peer's advertised max_datagram_frame_size are dropped at send time (RFC 9221 3). Callable only from inside the server's own loop (a callback), same contract as wired_server_broadcast_datagram.
Single-process only: unlike wired_server_broadcast_datagram, this API does not push into the srvthreads inbox mesh – a registered worker reaches its OWN env's sessions only. Reaching other workers' sessions burst-safely would also need the inbox drain path to queue into the ring (it currently feeds the single-slot queue), not built yet.
| data | the HTTP Datagram payload (qsid prefix NOT included); each target's prefixed copy must fit a ring slot |
| int wired_server_run | ( | u16 | port, |
| wired_srvboot_id * | id, | ||
| wired_srvrun_handler | h, | ||
| wired_srvrun_obs | obs ) |
The complete server event loop: bind a UDP socket on port, then forever receive datagrams and drive them — a fresh client Initial cold-starts a connection (wired_srvboot_accept), any later datagram steps the live loop (wired_srvloop_step) — sealing every reply straight back.
id is the fixed server identity, h the application's request responder. This owns the socket and blocks in recvfrom, the sanctioned socket-owning layer over the socket-free srvboot/srvloop core. RFC 9000 7: one connection at a time, in arrival order.
Graceful shutdown (RFC 9114 5.2): installs a SIGTERM handler before serving. On receipt, new connections stop being accepted, GOAWAY is sent once to every live connection, and the process exits once every connection has drained or a bounded grace period elapses. ponytail: a SIGTERM delivered before this handler is installed (a narrow startup race) falls back to the default action (immediate exit, no GOAWAY) — acceptable for a demo; a process supervisor should signal only after observing the "listening" log line.
Certificate hot reload (SIGHUP): when obs.cert_path/key_path are both set, installs a SIGHUP handler; on receipt, re-reads and re-decodes the PEM pair and updates *id in place (chain/chain_count/cert_seed only) so every connection cold-started afterward uses the new material. A connection already past its handshake never re-reads id, so a reload never disturbs it. id is therefore NOT const: the caller must keep it (and its backing PEM-derived buffers) alive and mutable for the whole run. A reload that fails to read or decode the new PEM pair leaves the previous identity in place.
| port | UDP port to bind |
| id | the fixed server identity; updated in place on a SIGHUP reload |
| h | the application's request responder |
| obs | optional qlog/keylog file paths and cert reload paths, each 0 to disable |
| int wired_server_run_opt | ( | u16 | port, |
| wired_srvboot_id * | id, | ||
| wired_srvrun_handler | h, | ||
| wired_srvrun_obs | obs, | ||
| const wired_srvrun_opt * | opt ) |
Same as wired_server_run, plus opt-in polling-driver behavior.
opt must not be 0; wired_server_run itself passes an opt with every knob at its disabled default (all-zero except incoming_cpu, which is -1; see wired_srvrun_opt) so its behavior is byte-identical to before this was added.
| port | UDP port to bind |
| id | the fixed server identity; updated in place on a SIGHUP reload |
| h | the application's request responder |
| obs | optional qlog/keylog file paths and cert reload paths, each 0 to disable |
| opt | busy_poll / so_busy_poll_us knobs, see wired_srvrun_opt |
| int wired_server_session_is_raw | ( | wired_wt_session * | s | ) |
1 when s is the implicit session of a raw-QUIC application connection (wired_rawq_on_session), 0 for a WebTransport session or when s resolves to no live connection.
On a raw session the wired_server_wt_* send API applies the native-QUIC binding: no stream signal or datagram quarter-stream-id, reset codes sent verbatim, and close as a CONNECTION_CLOSE 0x1d carrying the code (draft-ietf-moq-transport-19 3.5, -22 6.6; RFC 9000 19.19). Callable only from inside the server's own loop (a callback).
| s | the session to ask about |
| int wired_server_wt_close_session | ( | wired_wt_session * | s, |
| u32 | app_error_code, | ||
| wired_span | message ) |
draft-ietf-webtrans-http3-15 SS4.2/SS4.4/8.2 (WTH3-067): close s's WebTransport session: send a WT_CLOSE_SESSION capsule (app_error_code, message) followed immediately by FIN on the session's own CONNECT stream, then reset every other WT stream the session owns with WT_SESSION_GONE and transition the session to closed.
Queued (not sent inline) for delivery on one of the loop's next steps, the same deferred shape as wired_server_wt_ send_datagram_to; a second call before the first drains overwrites the pending one (last-writer-wins, same policy as this SDK's other single-slot WT queues). Callable only from inside the server's own loop (a callback). On a raw-QUIC session (wired_server_session_is_raw) the close is instead the connection's application CONNECTION_CLOSE (0x1d, RFC 9000 19.19) carrying app_error_code verbatim and at most 48 bytes of message (draft-ietf-moq-transport-19 3.5, -22 6.6).
| s | the session to close |
| app_error_code | the WebTransport application error code to report |
| message | UTF-8 reason, message.n <= WTCAPSULE_CLOSE_MESSAGE_MAX (copied at call time, so it need not outlive the call); a longer message is truncated to the cap |
| int wired_server_wt_drain_session | ( | wired_wt_session * | s | ) |
draft-ietf-webtrans-http3-15 4.7: ask the peer to wind s down by sending a WT_DRAIN_SESSION capsule on s's CONNECT stream.
Queued for one of the loop's next steps, and sent at most once per session: a repeat call (or the drain the server's own GOAWAY already sent) succeeds without sending again. The session stays open. Callable only from inside the server's own loop (a callback).
| s | the session to drain |
| u64 wired_server_wt_est_kbps | ( | wired_wt_session * | s | ) |
The connection's current delivery-rate estimate for s, in kbps: BBR's bottleneck bandwidth once it has a sample, else cwnd / smoothed RTT.
It tracks what the congestion controller allows, so a sender that stays below its window (app-limited) may see it grow slowly.
| s | the session whose connection is asked |
| int wired_server_wt_occupancy | ( | wired_wt_occupancy * | out | ) |
Snapshot the calling loop's WebTransport occupancy into *out.
Callable only from inside the server's own loop (a callback).
| out | receives the counts |
| i64 wired_server_wt_open_bidi | ( | wired_wt_session * | s, |
| wired_span | payload ) |
Same as wired_server_wt_open_uni, but a server-initiated bidirectional stream (RFC 9000 2.1: id 1 mod 4; signal prefix varint 0x41).
| s | the session whose connection the stream is opened on |
| payload | the complete stream bytes, signal prefix included |
| i64 wired_server_wt_open_bidi_stream | ( | wired_wt_session * | s, |
| wired_span | payload ) |
Same as wired_server_wt_open_uni_stream, but a server-initiated bidirectional stream (RFC 9000 2.1: id 1 mod 4; signal prefix varint 0x41), its receive side pre-registered like wired_server_wt_open_bidi.
| s | the session whose connection the stream is opened on |
| payload | the first round's bytes, signal prefix included |
| i64 wired_server_wt_open_uni | ( | wired_wt_session * | s, |
| wired_span | payload ) |
Open a new server-initiated unidirectional stream on s's connection (RFC 9000 2.1: id 3 mod 4) and send the whole payload on it, closing with FIN on the final slice.
payload must already carry the WebTransport stream signal prefix (draft-ietf-webtrans-http3-15 4.2: varint 0x54 + the CONNECT stream id, wired_wtwire_signal_put) – this SDK sends the bytes verbatim. A payload up to the send slot's own staging capacity (SRVRUN_WTSEND_BUF, 65536 bytes) is COPIED: the caller's storage is free the moment the call returns. Only a larger payload is held as a VIEW, and then the caller must keep it alive and unmoved until every byte has been acknowledged (the send slot frees itself then). Delivery is congestion/flow-control gated and paced like any response stream (RFC 9000 4.1 / RFC 9002 7). Callable only from inside the server's own loop (a callback), same contract as wired_server_broadcast_datagram.
| s | the session whose connection the stream is opened on |
| payload | the complete stream bytes, signal prefix included |
| i64 wired_server_wt_open_uni_stream | ( | wired_wt_session * | s, |
| wired_span | payload ) |
Same as wired_server_wt_open_uni, but WITHOUT closing the stream: no FIN is sent after payload's final slice, and the stream stays open for further wired_server_wt_stream_send rounds until one carries fin=1 (or wired_server_wt_stream_reset aborts it).
The same view/liveness contract as wired_server_wt_open_uni applies to payload.
| s | the session whose connection the stream is opened on |
| payload | the first round's bytes, signal prefix included |
| int wired_server_wt_send_datagram_to | ( | wired_wt_session * | s, |
| wired_span | payload ) |
Queue one HTTP Datagram (RFC 9297) to this session's peer: the SDK prefixes the quarter-stream-id varint (the session's CONNECT stream id / 4, RFC 9297 2.1) and sends it as a QUIC DATAGRAM (RFC 9221) on one of the loop's next steps.
The payload is copied at queue time, so it need not outlive the call. Unlike wired_server_broadcast_datagram's single last-writer-wins slot, sends queue into a bounded ring, so a burst of many datagrams is delivered without overwriting. Frames exceeding the peer's advertised max_datagram_frame_size are dropped at send time (RFC 9221 3), matching this SDK's existing per-connection DATAGRAM policy.
| s | the session the datagram is addressed to |
| payload | the HTTP Datagram payload (qsid prefix NOT included) |
| int wired_server_wt_stream_fin | ( | wired_wt_session * | s, |
| u64 | stream_id ) |
Ends a stream opened for appending with NO further bytes: a bare FIN on an otherwise-empty final round (RFC 9000 19.8 permits a zero-length STREAM frame carrying only FIN).
Use this instead of wired_server_wt_stream_send when the app has determined the stream is over but has no new bytes to send with it – e.g. the peer's own source stream closed with no trailing data of its own on that side (a WebTransport writer's close() can arrive as its own byte-less frame, separate from the data written just before it). The send slot frees itself once this round is fully acknowledged, same as wired_server_wt_stream_send's fin=1 round.
| s | the session whose connection carries the stream |
| stream_id | a stream opened by one of the three open_*_stream calls, or already appended to via wired_server_wt_stream_send(fin=0) |
| int wired_server_wt_stream_hold | ( | wired_wt_session * | s, |
| u64 | stream_id, | ||
| int | hold ) |
Hold or release the receive flow-control credit of a client-initiated stream (RFC 9000 4.1/19.10): while held, the loop stops RAISING the advertised MAX_STREAM_DATA for stream_id as the app consumes its bytes, so the peer's remaining send window drains and it pauses – backpressure without discarding anything.
The advertisement is never lowered (an advertisement MUST NOT decrease), so bytes the peer was already granted still arrive and must be absorbed by the app; hold=0 resumes raising on one of the loop's next steps. A hold placed on a bidi stream before its first grant still lets that one grant through (it lifts the stream from the request-stream initial credit to the WebTransport buffer). Applies to the receive slot (bidi or uni) currently reassembling stream_id on s's connection. Like the other wired_server_wt_* calls, call this from within the loop's callbacks.
| s | the session whose connection carries the stream |
| stream_id | the client-initiated stream whose credit to hold |
| hold | 1 to freeze the advertised credit, 0 to resume raising |
| int wired_server_wt_stream_inflight | ( | wired_wt_session * | s, |
| u64 | stream_id ) |
1 while a send slot on s's connection still holds stream_id – its bytes not yet fully acknowledged, or the stream still open for appends – and 0 once the slot was reaped or stream_id never named a server-sent stream on this session.
A payload handed to wired_server_wt_open_uni above the slot's staging capacity is held as a VIEW (see its doc); this is the signal that the view may be reused.
| s | the session whose connection carries the stream |
| stream_id | a stream id returned by one of the open_* calls |
| int wired_server_wt_stream_priority | ( | wired_wt_session * | s, |
| u64 | stream_id, | ||
| u8 | urgency ) |
Set the send urgency of a server-sent stream that still holds its send slot (RFC 9218 2.1: 0 most urgent .
. 7, default 3). Each pump pass sends only from the most urgent streams that can send right now; streams of equal urgency keep sharing the pass in turn. The setting lasts until the stream's send slot is released.
| s | the session whose connection carries the stream |
| stream_id | a stream holding an open send slot |
| urgency | 0..7, lower is more urgent |
| int wired_server_wt_stream_reply | ( | wired_wt_session * | s, |
| u64 | stream_id, | ||
| wired_span | payload ) |
Send payload on this endpoint's send direction of the client-initiated bidirectional stream stream_id (a WebTransport data stream the client opened), closing with FIN on the final slice.
No prefix is added – the bytes go out verbatim, and the same view/liveness contract as wired_server_wt_open_uni applies.
| s | the session whose connection carries the stream |
| stream_id | the client-opened bidi stream to reply on |
| payload | the bytes to send |
| int wired_server_wt_stream_reply_open | ( | wired_wt_session * | s, |
| u64 | stream_id, | ||
| wired_span | payload ) |
Same as wired_server_wt_stream_reply, but WITHOUT closing the stream – the opening round of a reply on a client-initiated bidi stream that stays open for further wired_server_wt_stream_send rounds.
| s | the session whose connection carries the stream |
| stream_id | the client-opened bidi stream to reply on |
| payload | the first round's bytes |
| int wired_server_wt_stream_reset | ( | wired_wt_session * | s, |
| u64 | stream_id, | ||
| u32 | error_code ) |
Abort a stream opened for appending: queue a standard RESET_STREAM (RFC 9000 19.4) on stream_id carrying error_code mapped into HTTP/3's WebTransport range (draft-ietf-webtrans-http3-15 SS4.4/8.2) and the stream's bytes-sent-so-far as its Final Size, delivered on one of the loop's next steps.
Resets queue into a small bounded latch (several streams can be aborted between steps – e.g. a relay fanning one source out to many subscribers); a FULL latch refuses the call and leaves the stream untouched, so a caller can retry once a later step drains it. On success the stream's send slot is freed at once, releasing the app's payload view and dropping any not-yet-sent bytes (RFC 9000 19.4: delivery is abandoned). No STOP_SENDING accompanies the reset: the streams this API opens are server-initiated uni streams, which have no peer-to-server half to stop (RFC 9000 19.5).
| s | the session whose connection carries the stream |
| stream_id | the stream to abort |
| error_code | the WebTransport application error code to report |
| int wired_server_wt_stream_schedule | ( | wired_wt_session * | s, |
| u64 | stream_id, | ||
| const wired_wt_sched * | k ) |
Set a server-sent stream's full send class (see wired_wt_sched).
Each pump pass sends from the best (urgency, fine) class among streams that can send right now, one slice per stream; inside a nonzero flow only the streams with the lowest (order, tie) among that flow's ready streams of the class take part. A stream that cannot send never holds back another. The setting lasts until the send slot is released.
| s | the session whose connection carries the stream |
| stream_id | a stream holding an open send slot |
| k | the class; k->urgency must be 0..7 |
| int wired_server_wt_stream_send | ( | wired_wt_session * | s, |
| u64 | stream_id, | ||
| wired_span | payload, | ||
| int | fin ) |
Append one more round of bytes to a stream opened with wired_server_wt_ open_uni_stream/open_bidi_stream/stream_reply_open: payload goes out at the stream's cumulative offset (RFC 9000 19.8), and fin=1 ends the stream with FIN on this round's final slice, after which the send slot frees itself once fully acknowledged (the one-shot opens' behavior).
An accepted round is COPIED into the slot's own staging (the caller's storage is free the moment the call returns) and pipelines onto the wire behind any earlier rounds still awaiting their ACKs – rounds queue up to the slot's staging capacity (SRVRUN_WTSEND_BUF, 65536 bytes of not-yet- ACKed rounds), and only a round that no longer fits is refused. A stream whose OPENING payload was oversized (held as a view) refuses appends until that view fully ACKs.
| s | the session whose connection carries the stream |
| stream_id | a stream opened by one of the three calls above |
| payload | this round's bytes; must be non-empty (the FIN rides the final slice, so a bare-FIN round has nothing to carry it) |
| fin | 1 to end the stream after this round, 0 to keep it open |
| int wired_server_wt_stream_stop | ( | wired_wt_session * | s, |
| u64 | stream_id, | ||
| u32 | error_code ) |
Asks the peer to stop sending on a PEER-initiated stream: queues one STOP_SENDING (RFC 9000 19.5) carrying error_code mapped into HTTP/3's WebTransport range, kept for resending until ACKed like a reset (RFC 9000 13.3).
The receive-side counterpart of wired_server_wt_stream_reset: a client uni stream has no server send part to reset, and a RESET_STREAM arriving for what the client sees as its send-only stream is connection-fatal at the client (RFC 9000 19.4).
| s | the session whose connection carries the stream |
| stream_id | the peer-initiated stream to stop |
| error_code | the WebTransport application error code to report |
| void wired_srvrun_broadcast_register | ( | int | index, |
| int | n_total, | ||
| wired_srvinbox_ring * | inbox_row, | ||
| wired_srvrun_env * | env ) |
Register the calling thread as srvthreads worker index of n_total, with its own N-ring inbox row (inbox_row[j] receives broadcasts sent by worker j, j != index only – the caller's own broadcasts reach its own connections directly, not through this row, so double-delivery to itself never happens) and its own env (the connection table wired_server_broadcast_datagram fans out into when this thread is the caller).
Called once by each worker before serving, typically right before wired_srvrun_serve_env. n_total, inbox_row and env are owned by the caller (srvthreads); srvrun only keeps pointers into them and never allocates or frees them itself, so all three must stay live and unmoved until wired_srvrun_broadcast_unregister.
| index | this worker's 0-based index, < n_total |
| n_total | total worker count in the mesh, <= 16 (srvrun's fixed registry capacity) |
| inbox_row | this worker's own row of n_total rings, row[j] fed by worker j's broadcasts (j != index; row[index] is never written) |
| env | this worker's own wired_srvrun_env, the connection table a broadcast this thread sends is fanned out into directly |
| void wired_srvrun_broadcast_unregister | ( | void | ) |
Unregister the calling thread's broadcast registry entry (symmetric with wired_srvrun_broadcast_register), typically at worker shutdown.
A no-op if the calling thread was never registered.
| void wired_srvrun_env_wt_usage | ( | const wired_srvrun_env * | env, |
| wired_srvrun_wt_usage * | out ) |
Snapshot env's WebTransport usage counters into *out.
| env | the loop instance (the process-wide one for wired_server_run callers is not addressable; use wired_srvrun_serve_env's) |
| out | receives the totals |
| int wired_srvrun_serve_env | ( | wired_srvrun_env * | env, |
| u16 | port, | ||
| wired_srvboot_id * | id, | ||
| wired_srvrun_handler | h, | ||
| wired_srvrun_obs | obs, | ||
| const wired_srvrun_opt * | opt ) |
Same as wired_server_run_opt, but driven off caller-owned env instead of the SDK's single process-wide instance – lets a caller run more than one independent server loop in the same process (e.g.
one per thread, each with its own env and its own bound port). Set opt->no_signal_handlers=1 for every instance but one, since SIGTERM/SIGHUP are process-wide.
| env | caller-owned state, sized wired_srvrun_env_size() and initialized with wired_srvrun_env_init before the first call |
| port | UDP port to bind |
| id | the fixed server identity; updated in place on a SIGHUP reload |
| h | the application's request responder |
| obs | optional qlog/keylog file paths and cert reload paths, each 0 to disable |
| opt | busy_poll / so_busy_poll_us / no_signal_handlers knobs |
| int * wired_srvrun_shutdown_word | ( | void | ) |
The process-wide graceful-shutdown word (RFC 9114 5.2): 0 until a shutdown is requested, then monotonically 1 (never reset outside test teardown).
Every wired_srvrun_serve_env/wired_server_run loop in the process polls this once per iteration (srvrun.c's srvrun_shutdown_requested) to stop accepting new connections and start draining live ones – the same word wired_sigterm_install's default handler sets. A caller running its own multi-threaded fan-out over wired_srvrun_serve_env (one instance per thread, wired_srvrun_opt.no_signal_handlers=1 on every instance but the one installing signals) uses the returned pointer to both observe the word (e.g. a timed futex wait) and set it (e.g. from its own signal handler), so every thread's loop sees the same 0->1 transition without a separate flag to keep in sync.