Releases: lodgvideon/poseidon-http-client
Release list
v0.10.0 — server-role QUIC
The quic package now accepts connections as well as dialing them, so a server can be built on it.
Server role (new)
Listen/Listener.Accept— many connections on one UDP socket, demuxed by Destination Connection ID. The caller drives each acceptedConnwithPoll, exactly as a client drives its own.NewServerConn— a connected server-roleConn. It accepts client-initiated request (bidirectional) and control (unidirectional) streams viaAcceptBidiStream/AcceptUniStream.AcceptInitial,NewServerHandshake,StartServerHandshake,ServerFlight.HandleClientHandshake,SealPacket— the server handshake primitives.ServerTransportParams/AppendServerTransportParams— per-connection parameters carrying the connection IDs a client authenticates (RFC 9000 §7.3). The advertised receive windows are exactly the ones a server connection enforces.- Stream IDs now follow the connection's role (RFC 9000 §2.1): a server opens 3, 7, 11 … and 1, 5, 9 ….
Connteardown now releases its TLS handshake.crypto/tlsruns aQUICConnhandshake on a goroutine that exits only when closed, so every terminated connection previously leaked one — client connections included.
A real client Conn completes a handshake against the listener over UDP and exchanges a request and a response, each side driven by its own Poll loop.
Not implemented on the listener: Retry / address validation, connection migration, per-peer rate limiting; each inbound Initial starts one goroutine for its handshake. Put a rate limiter in front of it before exposing it to the internet.
Also since v0.9.0
- ChaCha20-Poly1305 packet protection (#204); opt-in BBR congestion control (#203, #206)
- GSO / GRO batched send and receive on Linux (#199, #200); deferred and piggybacked ACKs (#201)
- QPACK dynamic table in both directions (#194, #195, #196, #202)
- HTTP/1.1 pool and managed transports (#219)
Requires Go 1.25, and adds golang.org/x/crypto (ChaCha20 header protection).
v0.9.0 — HTTP/3 client
Added
-
HTTP/3 client — a from-scratch, zero-dependency QUIC + HTTP/3 stack. A
second protocol alongside the HTTP/2 client, implemented in pure Go from the
RFCs with noquic-go, nonet/http, nogolang.org/x/net— the same
zero-dependency, fine-grained-control philosophy as the HTTP/2 codec. Public
entry point:http3.Dial(ctx, addr, tlsConfig) → *Client, thenClient.Do.
The layers, each conformance-tested (see
docs/RFC_COVERAGE.md; design in
docs/HTTP3_DESIGN.md):- QUIC transport (RFC 9000) — connection establishment, stream
multiplexing, bidirectional flow control (MAX_DATA/MAX_STREAM_DATA/
MAX_STREAMS), connection-ID issuance and rotation (NEW_CONNECTION_ID/
RETIRE_CONNECTION_ID), ACK ranges, transport parameters, varint framing,
CONNECTION_CLOSE, and Retry (quic/). - Loss recovery + congestion control (RFC 9002) — ACK-based loss
detection, PTO / probe timeouts, RTT estimation, and a congestion controller
with retransmission (loss.go,cc.go,pto.go,rtt.go). - QUIC-TLS (RFC 9001) — the TLS 1.3 handshake over CRYPTO streams, 1-RTT
key derivation, key update (Key Phase 0→1), and AEAD packet + header
protection (crypto*.go,handshake.go). - QPACK (RFC 9204) — a static-table encoder and decoder. The client
advertisesSETTINGS_QPACK_MAX_TABLE_CAPACITY = 0(static-only, no
dynamic-table state to synchronise); a server that references the dynamic
table anyway is rejected cleanly asQPACK_DECOMPRESSION_FAILEDrather than
mis-decoded (qpack/). - HTTP/3 (RFC 9114) — the control stream +
SETTINGS, request/response
mapping (HEADERS/DATA/ trailers / 1xx interim responses), strict frame
and message-order validation, typed HTTP/3 and QUIC error codes,
RESET_STREAM/STOP_SENDINGhandling with retryable classification
(*StreamResetError.Retryable()), andGOAWAYdrain (http3/).
- QUIC transport (RFC 9000) — connection establishment, stream
Hardened
- Receive-path resource bounds (#162–166) — the decode path is bounded
against an adversarial or buggy peer: bounded ACK / loss-tracking memory,
capped stream and CRYPTO reassembly buffers, aNEW_CONNECTION_ID
retire-flood bound, HTTP/3 response per-frame and cumulative size caps
(ErrResponseTooLarge), and eviction of reset streams from the routing map.
Tested
- Conformance suites for RFC 9000, 9001, 9002, 9114, and 9204, gate-tracked
byconformance-gate(see docs/RFC_COVERAGE.md). - Whole-stack interop, run in CI, against three independent HTTP/3 server
implementations over real UDP — Caddy (quic-go, Go), nginx (its own C QUIC
stack), and aioquic (pure Python) — plus a fault-injecting server (malformed
frames,RESET_STREAM,STOP_SENDING, dynamic-QPACK) and a loss/reorder relay
(test/integration/http3).
Known limitations
- ChaCha20-Poly1305 header protection is deferred — AES-GCM cipher suites
only (RFC 9001 §5.1). A ChaCha20-only server is a documented graceful failure. - QPACK is static-only — no dynamic-table encode; the decoder honours a
capacity of 0. Client.Dois blocking and sequential — the engine is single-goroutine
and not safe for concurrent use, so the public API exposes no concurrent
in-flight requests (QUIC multiplexing exists at the transport).- A few rare RFC 9000 conformance items remain deferred (documented in
docs/).
v0.8.0 — group-commit write batching
Added
ConnOptions.GroupCommit— opt-in write batching (group-commit). When a
HEADERS writer finds another writer queued on the write lock, it defers its
flush so the next holder batches both frames into a singletls.Conn.Write—
fewer TLS record encrypts + socket writes under high per-connection stream
concurrency. It is synchronous (the deferring writer waits for the convoy
flush and returns its error, so per-frame write-error semantics are preserved
— no async contract change), adaptive (a strict no-op with no contention;
a lone request is never delayed), and off by default. Measured on one TLS
h2 conn at 64 concurrent streams: 1.73 frames/flush, ≈−42% write syscalls,
≈+26% req/s; the win survives poseidon's dense-multiplexing pool (≈+30–35% at
1–8 conns). Motivated by a review of ozontech/framer; see
docs/WRITE_BATCHING_INVESTIGATION.md.
Note: framer'snet.Buffers/writev mechanism gives zero benefit through
crypto/tls.Conn; plaintext frame coalescing is the only TLS-viable lever.
v0.7.1 — buffered writer (fewer syscalls)
Performance
- Buffered connection writer. The transport writer is wrapped in a reused
16 KiBbufio.Writer, coalescing a DATA frame's 9-byte header and payload
into 1 write syscall instead of 2. A target-load CPU profile showed the
client is syscall-bound (~50% CPU in the socket write); a 7-DATA-frame body
drops from ~16 write syscalls to 8, with no per-frame allocation. Flushed
underwmubefore every lock release and every flow-control wait — guarded by
TestWriteBuffer_MultiChunkUpload_NoDeadlock.
No API changes. Full notes in CHANGELOG.md.
v0.7.0 — BodyMode enum
Breaking
-
Request.WantBody bool+Request.StreamBody bool→ singleRequest.BodyMode
enum:BodyDiscard(zero value, drop),BodyBuffer(intoResponse.Body),
BodyStream(Response.BodyReader). Removes the invalidWantBody+StreamBody
state and the precedence rule — the Request API is now consistently enum-based
alongsideIdempotencyMode(v0.6.0).Migration:
WantBody: true→BodyMode: client.BodyBuffer;
StreamBody: true→BodyMode: client.BodyStream; unset = discard. The
GET/POST/NewRequesthelpers setBodyBuffer, so sugar-based code is
unaffected.
Docs
- Retired
docs/USAGE.mdintodocs/CLIENT_GUIDE.md(one canonical guide).
Full notes in CHANGELOG.md. Shipped through strict
semantic-equivalence review with adversarial refutation + green CI.
v0.6.0 — ergonomic overhaul
Major ergonomics + receive-path performance release. Full notes in
CHANGELOG.md.
Highlights
Ergonomics
- Opt-in sugar:
client.H+client.HeaderField,GET/POST/NewRequest
(capture body by default),WithHeaders,Response.Header/CopyBody/Clone,
StreamEvent.DataCopy, and auto-closingClient.Stream. - Focused constructors
NewSingleConnClient/NewPoolClient/
NewManagedClient+ functionalWith*options (valid combos by construction). - Discoverable
Client.Retryer; status-class metrics (Responses2xx/
ResponsesNon2xx);EventNone. - Debug-gated leak detector (
-tags poseidondebug, zero prod cost). - Full
docs/CLIENT_GUIDE.md+ 24 compile-tested godoc examples +
examples/loadgen.
Breaking
Request.Idempotent *bool→Request.Idempotency IdempotencyMode
(IdempotencyAuto/ForceIdempotent/ForceNotIdempotent).
Performance
- Zero-allocation receive hot path (pooled per-DATA-frame buffer).
- Buffered transport reader (fewer read syscalls, esp. h2c).
Every change shipped through strict multi-agent review (adversarial refutation)
- green CI, with the zero-allocation hot path preserved throughout.