poseidon-http-client

module
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: MIT

README

poseidon-http-client

Go Reference CI Release Go 1.25

A low-level HTTP client for Go that implements HTTP/1.1, HTTP/2, and HTTP/3 from scratch — its own framing, HPACK, QPACK, and a from-scratch QUIC stack. No net/http, no third-party protocol libraries, no cgo. It is built for load generators and tools that need fine-grained control over connections, streams, and flow control, and exposes all three protocol versions through one Do/DoStream API.

Why poseidon

  • One client, three protocol versions. HTTP/1.1, HTTP/2, and HTTP/3 through the same Do/DoStream API. The Go standard library has no HTTP/3; most stacks bolt it on via a separate library.
  • From scratch, near-zero dependencies. No quic-go, no nghttp2, no cgo. Direct dependencies are golang.org/x/net and golang.org/x/crypto (ChaCha20-Poly1305 packet protection only); the TLS 1.3 handshake uses the standard library crypto/tls. Small, auditable surface.
  • Zero-alloc codec. The wire codec for both protocol versions — HTTP/2 frame + HPACK, and QUIC + HTTP/3 frames + QPACK — runs at 0 B/op, 0 allocs/op, enforced by a CI bench gate. This matters at load-generator request rates.
  • Fine-grained control. Direct access to streams, flow-control windows, SETTINGS, pooling policy, and congestion control (NewReno default, opt-in BBR) — knobs net/http hides.
  • Load-generation features built in. Connection pooling, DNS service discovery (Resolver/Selector), bounded retries, token-bucket rate limiting, lifecycle hooks, and metrics — shared across HTTP/2 and HTTP/3.
  • Conformance-tested. ~200 conformance tests keyed to RFC sections (CI-gated), a 3-server HTTP/3 interop matrix (Caddy/quic-go, nginx/C, aioquic/Python) over real UDP, fuzzed wire parsers, -race throughout.

If you want a general-purpose web client, use net/http. If you build load generators, or need HTTP/3 plus control over the wire, use poseidon.

Install

Requires Go 1.25 or later.

go get github.com/lodgvideon/poseidon-http-client

Quick start

HTTP/2
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"time"

	"github.com/lodgvideon/poseidon-http-client/client"
	"github.com/lodgvideon/poseidon-http-client/conn"
)

func main() {
	c, err := client.NewSingleConnClient(
		"www.cloudflare.com:443",
		&conn.TLSDialer{Config: &tls.Config{
			ServerName: "www.cloudflare.com",
			NextProtos: []string{"h2"},
		}},
	)
	if err != nil {
		log.Fatalf("build client: %v", err)
	}
	defer func() { _ = c.Close() }()

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// Response is caller-owned and reusable; Reset() clears it between requests.
	resp := &client.Response{}
	if err := c.Do(ctx, client.GET("/"), resp); err != nil {
		log.Fatalf("GET /: %v", err)
	}
	fmt.Printf("HTTP/2 %d — %d bytes\n", resp.Status, len(resp.Body))
}
HTTP/3
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"time"

	"github.com/lodgvideon/poseidon-http-client/client"
	"github.com/lodgvideon/poseidon-http-client/quic"
)

func main() {
	// The simple path: one QUIC connection, buffered response.
	//
	//	c, err := client.NewH3Client("www.cloudflare.com:443",
	//	    &tls.Config{ServerName: "www.cloudflare.com"})
	//
	// Below uses NewClient so we can also select BBR congestion control.
	c, err := client.NewClient(client.ClientOptions{
		Addr:      "www.cloudflare.com:443",
		Transport: client.TransportH3,
		TLSConfig: &tls.Config{ServerName: "www.cloudflare.com"},
		H3ConnOptions: []quic.ConnOption{
			quic.WithCongestionControl(quic.CCBBR), // default is NewReno
		},
	})
	if err != nil {
		log.Fatalf("build client: %v", err)
	}
	defer func() { _ = c.Close() }()

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	resp := &client.Response{}
	if err := c.Do(ctx, client.GET("/"), resp); err != nil {
		log.Fatalf("GET /: %v", err)
	}
	fmt.Printf("HTTP/3 %d — %d bytes\n", resp.Status, len(resp.Body))
}
HTTP/1.1
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"time"

	"github.com/lodgvideon/poseidon-http-client/client"
	"github.com/lodgvideon/poseidon-http-client/conn"
)

func main() {
	c, err := client.NewClient(client.ClientOptions{
		Addr:      "example.com:443",
		Transport: client.TransportH1SingleConn,
		ConnOpts: conn.ConnOptions{
			// Offer only http/1.1 so the server cannot select h2.
			Dialer: &conn.TLSDialer{Config: &tls.Config{
				ServerName: "example.com",
				NextProtos: []string{"http/1.1"},
			}},
		},
	})
	if err != nil {
		log.Fatalf("build client: %v", err)
	}
	defer func() { _ = c.Close() }()

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	resp := &client.Response{}
	if err := c.Do(ctx, client.GET("/"), resp); err != nil {
		log.Fatalf("GET /: %v", err)
	}
	fmt.Printf("HTTP/1.1 %d — %d bytes\n", resp.Status, len(resp.Body))
}

Runnable versions of all three live in examples/.

Features

HTTP/1.1 HTTP/2 HTTP/3
Spec HTTP/1.1 over TLS RFC 7540 + HPACK (RFC 7541) RFC 9114 + QUIC (RFC 9000/9001/9002) + QPACK (RFC 9204)
Constructors NewClient with TransportH1SingleConn NewSingleConnClient, NewPoolClient, NewManagedClient NewH3Client, NewH3PoolClient, NewManagedH3Client
Concurrency model One connection, requests serialized (no pipelining) Stream multiplexing, MAX_CONCURRENT_STREAMS gating Concurrent in-flight requests over one QUIC connection
Streaming Buffered DoStream, body streaming, request trailers DoStream, body streaming
Flow control Full bidirectional, dynamic SETTINGS, GOAWAY drain QUIC stream + connection flow control
Header compression HPACK, zero-alloc Dynamic QPACK, both directions (encode + decode), zero-alloc
Extras ALPN fallback: TransportALPN falls back to HTTP/1.1 when the server does not offer h2 Server push (PUSH_PROMISE), request priority, extended CONNECT (RFC 8441, WebSockets over H2), CONTINUATION, HTTP CONNECT proxy dialers, H2C prior knowledge, PING keepalive AES-128-GCM / AES-256-GCM / ChaCha20-Poly1305; NewReno default, opt-in BBR; GSO/GRO batched UDP and bounded ACK coalescing on Linux

Shared across protocols:

  • Request APIclient.GET(path), or client.Request{Method, Scheme, Authority, Path, BodyMode} (BodyBuffer or BodyStream). Response{Status, Body} is caller-owned; Reset() reuses it across requests.
  • Pooling and discovery — per-host pools (PoolOptions{MaxConnsPerHost, MaxStreamsPerConn, HealthCheckPeriod}) with least-loaded stream pick and idle eviction; Resolver + Selector service discovery.
  • Resilience — opt-in Retryer (bounded retries of idempotent requests on REFUSED_STREAM / GOAWAY / dial errors, exponential backoff + jitter), WithRateLimit(perSecond, burst), per-request timeouts via context.
  • ObservabilityClient.Hooks (OnDial, OnRequestComplete, OnRetry, …), Client.MetricsSnapshot() (counters + latency histogram), Client.PoolStats().
  • Codec-only use — the frame, hpack, and qpack packages work standalone; the frame, HPACK, and QPACK hot paths — along with the QUIC and HTTP/3 frame codecs — are bench-gated at 0 B/op, 0 allocs/op.

Documentation

Full documentation: https://lodgvideon.github.io/poseidon-http-client/

Available in English, 中文, Español, Русский, and 日本語.

Non-goals

Deliberately out of scope for 1.0:

  • 0-RTT / session resumption
  • QUIC connection migration
  • HTTP/3 server push

The client never initiates these; a peer offering them is simply not engaged — nothing fails. An unsupported TLS cipher suite fails cleanly with a typed ErrCryptoSuite (no hang, no panic).

Disclaimer

This is young software — v1.0 is its first release. It is provided as is, use at your own risk (MIT license, no warranty). It implements security-sensitive protocols (TLS 1.3, QUIC, HPACK/QPACK) from scratch; while conformance-tested, fuzzed, and interop-verified, it has not had a formal third-party security audit. Do not treat it as a drop-in replacement for net/http in security-critical production without your own review. Report vulnerabilities privately — see SECURITY.md — not via public issues.

Contributing

See CONTRIBUTING.md.

License

MIT.

Directories

Path Synopsis
Package client provides a high-level HTTP/2 client API on top of the conn package.
Package client provides a high-level HTTP/2 client API on top of the conn package.
Package conn implements the Phase B HTTP/2 connection layer on top of the Phase A frame and HPACK codecs.
Package conn implements the Phase B HTTP/2 connection layer on top of the Phase A frame and HPACK codecs.
examples
http1 command
Command http1-example issues a single HTTP/1.1 request with the poseidon client.
Command http1-example issues a single HTTP/1.1 request with the poseidon client.
http2 command
Command http2-example issues a single HTTP/2 GET with the poseidon client.
Command http2-example issues a single HTTP/2 GET with the poseidon client.
http3 command
Command http3-example issues a single HTTP/3 GET over QUIC with the poseidon client, and shows how to opt into BBR congestion control.
Command http3-example issues a single HTTP/3 GET over QUIC with the poseidon client, and shows how to opt into BBR congestion control.
loadgen command
Command loadgen is a minimal HTTP/2 load generator built on the poseidon client.
Command loadgen is a minimal HTTP/2 load generator built on the poseidon client.
Package frame implements the HTTP/2 framing layer (RFC 7540) without any networking.
Package frame implements the HTTP/2 framing layer (RFC 7540) without any networking.
Package hpack implements HPACK (RFC 7541) header compression for HTTP/2.
Package hpack implements HPACK (RFC 7541) header compression for HTTP/2.
Package http1 implements the HTTP/1.1 wire protocol (RFC 7230/7231) as a zero-dependency (no net/http) connection abstraction for the poseidon client layer.
Package http1 implements the HTTP/1.1 wire protocol (RFC 7230/7231) as a zero-dependency (no net/http) connection abstraction for the poseidon client layer.
Package http3 implements HTTP/3 framing and request/response mapping over QUIC (RFC 9114).
Package http3 implements HTTP/3 framing and request/response mapping over QUIC (RFC 9114).
internal
bytesx
Package bytesx provides private byte-level helpers shared across the wire codecs: big-endian fixed-width uint24/uint31 (RFC 7540, used by frame), the QUIC variable-length-integer codec (RFC 9000 §16, used by quic and http3), a sync.Pool-backed byte-buffer pool, and RFC 7540 §6.1 padding stripping.
Package bytesx provides private byte-level helpers shared across the wire codecs: big-endian fixed-width uint24/uint31 (RFC 7540, used by frame), the QUIC variable-length-integer codec (RFC 9000 §16, used by quic and http3), a sync.Pool-backed byte-buffer pool, and RFC 7540 §6.1 padding stripping.
Package qpack implements QPACK field compression for HTTP/3 (RFC 9204).
Package qpack implements QPACK field compression for HTTP/3 (RFC 9204).
Package quic implements a complete QUIC v1 client transport from scratch for the HTTP/3 client (design in docs/HTTP3_DESIGN.md).
Package quic implements a complete QUIC v1 client transport from scratch for the HTTP/3 client (design in docs/HTTP3_DESIGN.md).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL