Skip to content

Handshake and observability knobs

Four server options that do not change what a connection carries, only how it gets there and what gets logged alongside it: session-ticket resumption, RFC 9000 8.1.2 Retry, the congestion controller, and qlog/NSS key log output.

Code

main.c
#define WIRED_MAIN
#include "wired.h"
static int on_request(
void* ctx,
const wired_h3reqdrive_req* req,
u64 offset,
wired_obuf* body_out,
const char** content_type,
int* more,
u64* total_size) {
(void)ctx;
(void)req;
(void)offset;
(void)more;
(void)total_size;
wired_log_str("request /\n");
*content_type = "text/plain";
wired_obuf_printf(body_out, "ok");
return 1;
}
int wired_main(int argc, char** argv) {
/* The same fixed demo identity as the HTTP/3 hello page. */
static wired_srvboot_demo_keys keys;
wired_srvboot_id id;
wired_srvboot_demo(&id, &keys, 0x50, "guide-h3");
/* Non-0 enables session-ticket resumption/0-RTT (RFC 8446 4.6.1): the
* SDK's per-process ticket seed; the sealing side and this id derive the
* same rotating ticket keys from it. */
id.ticket_key = wired_srvloop_ticket_key();
wired_srvrun_opt opt = {0};
opt.incoming_cpu = -1;
/* RFC 9000 8.1.2: answer every Initial with a stateless Retry first,
* only accepting one that carries a valid Retry token. */
opt.force_retry = 1;
wired_srvrun_obs obs = {
"demo.qlog", /* RFC 9002-shaped packet_sent/packet_received events */
"demo.keylog", /* SSLKEYLOGFILE, decrypts a capture in Wireshark */
0,
0,
1, /* CC_ALGO_CUBIC (see transport/recovery/congestion/cc/cc.h);
* 0 = NewReno, 2 = BBR */
};
u16 port = (u16)wired_cliargs_int(argc, argv, "--port", 4433);
wired_srvrun_handler h = {.cb = on_request};
return wired_server_run_opt(port, &id, h, obs, &opt) ? 0 : 1;
}

Output

Terminal window
$ ./ops-handshake-knobs --port 4433
request /
conn closed by peer (CONNECTION_CLOSE)
Terminal window
$ probe h3get 127.0.0.1:4433 /
GET / status=200 content-type=text/plain body="ok"
  • id.ticket_key = wired_srvloop_ticket_key() enables session-ticket resumption (RFC 8446 4.6.1, 0-RTT’s prerequisite). The ticket key is derived from the SDK’s per-process seed and rotates every two hours: the current key seals new tickets, and the current or previous key opens presented ones. A ticket older than that is ignored and the client gets a full handshake.
  • opt.force_retry = 1 makes every Initial without a valid Retry token get a stateless Retry first (RFC 9000 8.1.2) – the quic-interop-runner’s retry testcase’s server mode. A real client handles this transparently; the handshake still completes, just with one extra round trip.
  • obs.cc_algo picks the congestion controller for fresh connections (CC_ALGO_NEWRENO 0, CC_ALGO_CUBIC 1, CC_ALGO_BBR 2, see transport/recovery/congestion/cc/cc.h); 0 with no build override means Cubic (WIRED_CC_ALGO_DEFAULT in srvrun.c), since an explicit runtime NewReno request is itself 0 and indistinguishable from “unset” – a build wanting NewReno by default overrides -DWIRED_CC_ALGO_DEFAULT=0 instead.
  • obs.qlog_path / obs.keylog_path write a qlog (RFC 9002-shaped packet_sent/packet_received events) and an NSS key log (SSLKEYLOGFILE format) a tool like Wireshark reads to decrypt a capture – both 0 by default.