Skip to content

Releases: lodgvideon/poseidon-http-client

v0.10.0 — server-role QUIC

Choose a tag to compare

@lodgvideon lodgvideon released this 14 Jul 23:39
Immutable release. Only release title and notes can be modified.
15992e3

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 accepted Conn with Poll, exactly as a client drives its own.
  • NewServerConn — a connected server-role Conn. It accepts client-initiated request (bidirectional) and control (unidirectional) streams via AcceptBidiStream / 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 ….
  • Conn teardown now releases its TLS handshake. crypto/tls runs a QUICConn handshake 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

Choose a tag to compare

@lodgvideon lodgvideon released this 11 Jul 21:40
Immutable release. Only release title and notes can be modified.
9aa6dd5

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 no quic-go, no net/http, no golang.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, then Client.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
      advertises SETTINGS_QPACK_MAX_TABLE_CAPACITY = 0 (static-only, no
      dynamic-table state to synchronise); a server that references the dynamic
      table anyway is rejected cleanly as QPACK_DECOMPRESSION_FAILED rather 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_SENDING handling with retryable classification
      (*StreamResetError.Retryable()), and GOAWAY drain (http3/).

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, a NEW_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
    by conformance-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.Do is 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

Choose a tag to compare

@lodgvideon lodgvideon released this 11 Jul 21:40
Immutable release. Only release title and notes can be modified.
f922cff

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 single tls.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's net.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)

Choose a tag to compare

@lodgvideon lodgvideon released this 07 Jul 19:49
Immutable release. Only release title and notes can be modified.
8ed0794

Performance

  • Buffered connection writer. The transport writer is wrapped in a reused
    16 KiB bufio.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
    under wmu before 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

Choose a tag to compare

@lodgvideon lodgvideon released this 06 Jul 22:27
Immutable release. Only release title and notes can be modified.
a7824d9

Breaking

  • Request.WantBody bool + Request.StreamBody bool → single Request.BodyMode
    enum: BodyDiscard (zero value, drop), BodyBuffer (into Response.Body),
    BodyStream (Response.BodyReader). Removes the invalid WantBody+StreamBody
    state and the precedence rule — the Request API is now consistently enum-based
    alongside IdempotencyMode (v0.6.0).

    Migration: WantBody: trueBodyMode: client.BodyBuffer;
    StreamBody: trueBodyMode: client.BodyStream; unset = discard. The
    GET/POST/NewRequest helpers set BodyBuffer, so sugar-based code is
    unaffected.

Docs

  • Retired docs/USAGE.md into docs/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

Choose a tag to compare

@lodgvideon lodgvideon released this 23 Jun 11:56
Immutable release. Only release title and notes can be modified.
0192087

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-closing Client.Stream.
  • Focused constructors NewSingleConnClient / NewPoolClient /
    NewManagedClient + functional With* 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 *boolRequest.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.