English | 简体中文
A QUIC / HTTP/3 implementation in pure Zig.
Current state: Transport + application layer production-ready (36/37 features, 1820 tests, three-implementation interop verified). Full HTTP/3, QPACK, WebTransport, and HTTP Datagrams. Public APIs may still evolve.
- QUIC v1 & v2 (RFC 9000 / RFC 9369) — handshake, streams, flow control, connection migration, path validation, Retry, stateless reset, key update, version negotiation, DATAGRAM, multipath, ECN, PMTUD, GSO/GRO
- TLS 1.3 (RFC 8446 / RFC 9001) — pure Zig, no C dependencies. ECDSA P-256, X25519, X25519Kyber768 (post-quantum), AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305, 0-RTT, session resumption
- Loss Detection & Congestion Control (RFC 9002 / RFC 9438) — NewReno, CUBIC, packet pacing
- HTTP/3 (RFC 9114) — full connection management, SETTINGS, GOAWAY, stream state machine, QPACK static + dynamic table, production async runtime drivers
- WebTransport (draft-ietf-webtrans-http3) — full session management, uni/bidi framing, CLOSE capsule, datagrams
- qlog (draft-ietf-quic-qlog) — QUIC event logging
- External interop — verified against quic-go (Go), quiche (Rust), s2n-quic (Rust): handshake + transfer
Add to your build.zig.zon:
zig fetch --save git+https://github.com/venjiang/quiczThen in your build.zig:
const quicz_dep = b.dependency("quicz", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("quicz", quicz_dep.module("quicz"));quicz.runtime provides an event-driven server/client on Zig 0.16 std.Io
(threaded). The server spawns an independent handler task per connection
(std.http model); the client drives an async session.
const std = @import("std");
const quicz = @import("quicz");
const Server = quicz.runtime.server.Server;
const Client = quicz.runtime.client.Client;
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var threaded = std.Io.Threaded.init(allocator, .{});
defer threaded.deinit();
const io = threaded.io();
// Server: serve(handler) spawns a driving task + per-connection handlers.
var server = try Server.init(allocator, io, .{
.port = 4433,
.alpn = &.{"hq-interop"},
.cert_der = &cert_der,
.private_key = &key,
});
defer server.deinit();
try server.serve(&echoHandler); // fn(ServerConnection) std.Io.Cancelable!void
// Client: connect, send, receive via async session.
var client = try Client.init(allocator, io, .{
.server_port = 4433,
.server_name = "localhost",
.alpn = &.{"hq-interop"},
});
defer client.deinit();
const ok = try client.runEchoSession("hello");
}Server handler signature: fn (ServerConnection) std.Io.Cancelable!void.
Per-connection ServerConnection.acceptStream() returns a Stream with
receive(buf) / send(data, fin). See examples/io_echo.zig and
examples/multi_conn_test.zig.
For applications that need fine-grained control over packet processing, TLS backend driving, or custom endpoint routing:
const quicz = @import("quicz");
// Packet-level connection state machine
var conn = try quicz.Connection.init(allocator, .client, .{
.initial_max_data = 65_536,
.initial_max_streams_bidi = 16,
});
defer conn.deinit();
// TLS 1.3 handshake state machine (pure Zig)
const tls13 = quicz.tls13;
// TLS-backed transport wrappers
const Tls13ClientEndpoint = quicz.Tls13ClientEndpoint;
const Tls13ServerTransport = quicz.Tls13ServerTransport;
// Endpoint routing, CID registry, timers
const EndpointConnectionLifecycle = quicz.EndpointConnectionLifecycle;
// Packet protection: AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305
const protection = quicz.protection;
// Congestion control: NewReno, CUBIC
const cubic = quicz.cubic;
// HTTP/3, QPACK, WebTransport
const h3 = quicz.h3;
const qpack = quicz.qpack;
const webtransport = quicz.webtransport;
// qlog event logging
const qlog = quicz.qlog;The runtime interop server (interop_runtime_server.zig) and client
(interop_runtime_client.zig) demonstrate the production runtime API:
Server.serve(handler) for multi-connection echo, Client.connect() for
handshake and stream I/O. External echo clients (quiche/s2n-quic/quinn/quic-go)
connect to the runtime server for reverse-direction interop.
Benchmark results (Apple M-series, macOS loopback, ReleaseFast):
| Metric | Result |
|---|---|
| Stream Upload (threaded) | ~1.94 GB/s |
| Echo Latency (1 KB RTT) | P50=19μs, P99=55μs |
| Multi-Stream (4x) | ~800 MB/s |
| Loss Recovery (1% loss) | ~117 MB/s |
| Loss Recovery (5% loss) | ~67 MB/s |
Comparison with other QUIC implementations:
| Implementation | Language | Throughput | Latency P50 |
|---|---|---|---|
| msquic | C | 1.5-2.5 GB/s (Linux XDP) | ~5-15μs |
| quicz | Zig | ~1.94 GB/s (macOS) | ~19μs |
| s2n-quic | Rust | ~800 MB/s (Linux GSO) | ~20-40μs |
| quic-go | Go | 400-600 MB/s (Linux GSO) | ~50-100μs |
| quiche | Rust | 300-500 MB/s | ~30-80μs |
Run benchmarks: zig build run-quic-bench
Full details: docs/en/benchmark.md
Requires Zig 0.16.0.
zig build # build library
zig build test --summary all # 1867 unit tests
zig build run-tls13-udp-loopback # TLS 1.3 UDP loopback
zig build run-h3-loopback # HTTP/3 + QPACK dynamic over UDP
zig build run-h3-runtime-loopback # HTTP/3 + QPACK on the std.Io runtime
zig build run-h3-server # HTTP/3 server on the std.Io runtime (curl-testable)
zig build run-interop-client-standalone # interop self-test
zig fmt --check build.zig src examples # format checkquicz passes a full bidirectional interop matrix (7/7) against four major
implementations. All tests use certificate-verified TLS 1.3. Certificates are
a proper CA (testdata/quicz-echo-ca.pem) + CA-signed leaf
(testdata/cert.pem), so strict webpki-based clients (s2n-quic, rustls/quinn)
accept the trust chain.
| Direction | Peer | Result |
|---|---|---|
| Forward (quicz client → server) | quic-go | echo_bytes=19, cert verified |
| Forward (quicz client → server) | quiche | echo_bytes=19, cert verified |
| Forward (quicz client → server) | s2n-quic | echo_bytes=19, cert verified |
| Reverse (client → quicz server) | quic-go | echo_streams=2, echo_bytes=10 |
| Reverse (client → quicz server) | quinn | echo_streams=2, echo_bytes=10 |
| Reverse (client → quicz server) | quiche | echo_streams=2, echo_bytes=10 |
| Reverse (client → quicz server) | s2n-quic | echo_streams=2, echo_bytes=10 |
# Start the quicz runtime server
zig build && zig-out/bin/quicz-interop-runtime-server 4433 cert.pem key.pem
# Forward: quicz client → external server
zig-out/bin/quicz-interop-runtime-client 127.0.0.1 4433 quicz-echo-ca.pem localhost
# Reverse matrix (external clients → quicz server), all four peers
examples/interop/run_reverse_interop.sh all 4433THREAT_MODEL.md documents the trust boundary and the
defenses against in-scope attacks (amplification, packet injection, stateless
reset token guessing, version downgrade, hostile transport parameters, Retry
token forgery), each with code and test references.
| Path | Description |
|---|---|
src/quic/api.zig |
High-level API — Endpoint / Connection / Stream |
src/runtime/ |
I/O runtime - async server/client (std.Io) |
src/quic/connection.zig |
Connection state machine (11K lines) |
src/quic/endpoint.zig |
Endpoint routing, CID registry, ECN policy |
src/quic/endpoint_lifecycle.zig |
Connection lifecycle management |
src/quic/tls13_client_endpoint.zig |
Client endpoint (handshake + stream I/O) |
src/quic/tls13_server_endpoint.zig |
Server endpoint (multi-connection routing) |
src/quic/udp_event_loop.zig |
UDP socket I/O (IPv4 + IPv6 dual-stack) |
src/tls/tls13.zig |
Pure Zig TLS 1.3 (9.4K lines, 222 tests) |
src/tls/pq_kex.zig |
X25519Kyber768 post-quantum key exchange |
src/tls/pem.zig |
PEM (RFC 7468) decoding + SEC1/PKCS#8 P-256 private key parsing |
src/quic/protection.zig |
Packet protection (AES-GCM, ChaCha20-Poly1305) |
src/quic/recovery.zig |
Loss detection and recovery (RFC 9002) |
src/quic/cubic.zig |
Congestion controller (NewReno + CUBIC) |
src/h3/ |
HTTP/3, QPACK, WebTransport |
src/qlog/ |
qlog event logging |
examples/ |
Runnable examples and interop probes |
docs/en/ / docs/zh-CN/ |
Design docs and task matrix |
MIT. See LICENSE.