httpkit

package
v0.5.7 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package httpkit carries the HTTP conveniences the hand-rolled provider clients (qwen, minimax, kimi, bytedance) use: a tuned connection-pooling transport, a bounded retry RoundTripper for transient failures, an option-driven client builder that hardens HTTP/1.1, HTTP/2, or HTTP/3, and the host-side sandbox enforcement proxy (netproxy.go) that turns sandbox.NetAllowList / sandbox.NetProxy into a single enforced egress for the bwrap backend.

This file carries the sandbox enforcement proxy (host side) that turns sandbox.NetAllowList / sandbox.NetProxy into a single enforced network path. It runs on the host (outside any sandbox) and is the sandboxed child's only network egress. It supports two listening surfaces:

  • unix socket (default): the bwrap backend bind-mounts the socket into the sandbox, because its netns hides the host loopback and a bridge process pipes the netns loopback to the socket;
  • TCP loopback (ProxyConfig.TCPLoopback): the seatbelt backend, whose child shares the host network stack and dials 127.0.0.1:<port> directly under an SBPL loopback-only rule.

It is part of httpkit because the proxy is plain HTTP machinery: an http.Handler with allow-list / upstream policy over a listener.

Trust boundary: the proxy is spawned by the host application and is the sandboxed child's only network egress. A compromised child can reach exactly the destinations the proxy's policy permits — that is the configured policy, not a sandbox escape. The proxy must therefore be pinned and audited like any trust anchor. When MITM is enabled, the temporary CA becomes an additional trust anchor and must be treated with the same care.

Index

Constants

This section is empty.

Variables

View Source
var DefaultRetry = RetryConfig{
	MaxAttempts: 3,
	BaseDelay:   200 * time.Millisecond,
	MaxDelay:    2 * time.Second,
}

DefaultRetry retries transient failures twice after the first attempt with a 200ms-seeded exponential backoff.

Functions

func NewClient

func NewClient(options ...Option) *http.Client

NewClient builds an http.Client over NewRoundTripper with the configured whole-request timeout.

func NewRoundTripper

func NewRoundTripper(options ...Option) http.RoundTripper

NewRoundTripper builds the hardened base transport per Config and wraps it with the retry transport unless disabled.

func RetryCountOf

func RetryCountOf(response *http.Response) int

RetryCountOf reports the wire attempts that produced a response. It returns 0 for responses the retry transport did not stamp.

Types

type Config

type Config struct {
	Protocol Protocol

	// ClientTimeout bounds the whole request (http.Client.Timeout).
	ClientTimeout time.Duration

	// TLSClientConfig is used for TLS dialing. Nil uses system roots.
	TLSClientConfig *tls.Config

	// ResponseHeaderTimeout bounds the wait for HTTP/1.1 response headers.
	ResponseHeaderTimeout time.Duration

	// HTTP/2 health checks.
	PingInterval     time.Duration // quiet-connection PING cadence
	PingTimeout      time.Duration // close if a PING goes unanswered
	WriteByteTimeout time.Duration // close if the write path blocks

	// HTTP/3 (QUIC) connection timeouts.
	QUICHandshakeIdleTimeout time.Duration
	QUICMaxIdleTimeout       time.Duration
	QUICKeepAlivePeriod      time.Duration

	// Connection pooling (HTTP/1.1).
	MaxIdleConns        int
	MaxIdleConnsPerHost int
	IdleConnTimeout     time.Duration

	// Retry wraps the base transport; disabled with WithoutRetry.
	Retry        RetryConfig
	RetryEnabled bool
}

Config carries every knob for NewRoundTripper / NewClient. Zero values select the defaults below.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the recommended hardened defaults.

type Matcher added in v0.5.5

type Matcher struct {
	// contains filtered or unexported fields
}

Matcher evaluates sandbox.NetPolicy rules against a destination. Rules are compiled once at construction: hostnames are normalized (lowercase, trailing dot removed, IDNA → punycode) and IP/CIDR entries are parsed so Match / MatchIP stay pure and IO-free.

Semantics:

  • "example.com": the bare domain and every subdomain (domain-and-descendants, matching the legacy AllowHosts behaviour).
  • "*.example.com": subdomains only, any depth; never the bare domain.
  • "1.2.3.4" / "10.0.0.0/8": IP / CIDR, evaluated by MatchIP against locally resolved addresses.
  • Deny rules are evaluated first across the whole rule set; a single deny match wins over any allow.
  • Port 0 matches any port.

AllowHosts is compiled as trailing allow rules, so explicit deny rules always take precedence over the legacy allow-list.

func NewMatcher added in v0.5.5

func NewMatcher(policy sandbox.NetPolicy) (*Matcher, error)

NewMatcher compiles policy into a Matcher. The policy should already have passed NetPolicy.Validate; this constructor re-checks the rule forms it needs and returns Validation errors for malformed hosts.

func (*Matcher) HasIPRules added in v0.5.5

func (m *Matcher) HasIPRules() bool

HasIPRules reports whether any rule needs local DNS resolution (exact IP or CIDR entries).

func (*Matcher) Match added in v0.5.5

func (m *Matcher) Match(host string, port int) (sandbox.NetAction, string, bool)

Match evaluates hostname rules for host:port. It never resolves DNS; callers apply IP/CIDR rules separately via MatchIP. The returned rule string describes the decisive rule ("" when nothing matched).

func (*Matcher) MatchIP added in v0.5.5

func (m *Matcher) MatchIP(ip netip.Addr, port int) (sandbox.NetAction, string, bool)

MatchIP evaluates IP/CIDR rules against one resolved address.

type Option

type Option func(*Config)

Option mutates a Config.

func WithConnectionPool

func WithConnectionPool(maxIdleConns, maxIdleConnsPerHost int, idleConnTimeout time.Duration) Option

WithConnectionPool tunes HTTP/1.1 keep-alive pooling.

func WithHTTP1

func WithHTTP1() Option

WithHTTP1 selects the hardened HTTP/1.1 transport.

func WithHTTP2

func WithHTTP2() Option

WithHTTP2 selects the hardened HTTP/2 transport.

func WithHTTP2Timeouts

func WithHTTP2Timeouts(pingInterval, pingTimeout, writeByteTimeout time.Duration) Option

WithHTTP2Timeouts sets the HTTP/2 health-check knobs.

func WithHTTP3

func WithHTTP3() Option

WithHTTP3 selects the QUIC-based HTTP/3 transport.

func WithProtocol

func WithProtocol(protocol Protocol) Option

func WithQUICTimeouts

func WithQUICTimeouts(handshakeIdleTimeout, maxIdleTimeout, keepAlivePeriod time.Duration) Option

WithQUICTimeouts sets the HTTP/3 connection timeouts: QUIC handshake, connection idle timeout, and keep-alive period for dead-peer detection.

func WithResponseHeaderTimeout

func WithResponseHeaderTimeout(timeout time.Duration) Option

func WithRetry

func WithRetry(config RetryConfig) Option

func WithRetryAttempts

func WithRetryAttempts(maxAttempts int) Option

WithRetryAttempts sets the total wire attempts including the first, keeping the default backoff curve. Zero or negative disables transport retries so an outer retry owner (route.Router) controls the full budget.

func WithTLSClientConfig

func WithTLSClientConfig(config *tls.Config) Option

WithTLSClientConfig supplies the TLS configuration used for dialing (custom CA roots, client certificates, server name overrides).

func WithTimeout

func WithTimeout(timeout time.Duration) Option

func WithoutRetry

func WithoutRetry() Option

type Protocol

type Protocol int

Protocol selects the transport family built by NewRoundTripper.

const (
	// ProtocolHTTP1 is the default: HTTP/1.1 with ResponseHeaderTimeout,
	// so a stalled connection is bounded and retried on a fresh one.
	ProtocolHTTP1 Protocol = iota
	// ProtocolHTTP2 keeps multiplexing and bounds the write path with
	// periodic PING health checks and WriteByteTimeout.
	ProtocolHTTP2
	// ProtocolHTTP3 runs HTTP over QUIC (UDP). Streams are independent at
	// the transport level and QUIC idle timeouts detect dead peers.
	ProtocolHTTP3
)

type Proxy

type Proxy struct {
	// contains filtered or unexported fields
}

Proxy is a host-side enforcement proxy listening on a unix socket (default) or TCP loopback (ProxyConfig.TCPLoopback). Create it with Start and stop it with Close.

func Start

func Start(cfg ProxyConfig) (*Proxy, error)

Start serves the enforcement proxy in the background and returns immediately. With ProxyConfig.TCPLoopback it binds an ephemeral loopback TCP port (Addr reports it); otherwise it binds a unix socket in a fresh per-run temp directory (mode 0600) whose path is available via SocketPath for bind-mounting into a sandbox.

func (*Proxy) Addr

func (p *Proxy) Addr() net.Addr

Addr returns the bound listener address. For a TCP-loopback proxy this is the 127.0.0.1:<port> the sandboxed child must be allowed to dial; for a unix-socket proxy it is the socket path address.

func (*Proxy) CAPEM added in v0.5.5

func (p *Proxy) CAPEM() []byte

CAPEM returns the temporary MITM root CA in PEM form, for bundle injection into the sandbox. Nil when MITM is disabled.

func (*Proxy) Close

func (p *Proxy) Close() error

Close stops the server, closes the listener, and removes the temp directory (including the socket file) when one was created.

func (*Proxy) ServeHTTP

func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler for both HTTP absolute-form requests and CONNECT tunnels.

func (*Proxy) SocketPath

func (p *Proxy) SocketPath() string

SocketPath returns the unix socket path to bind into the sandbox. It is empty when the proxy was started with TCPLoopback.

type ProxyConfig

type ProxyConfig struct {
	// Mode is sandbox.NetAllowList or sandbox.NetProxy.
	Mode sandbox.NetMode
	// AllowHosts is the deprecated allow-list: hostname suffixes and
	// exact IP literals. Compiled as trailing allow rules (see
	// Matcher); explicit Rules deny rules always win.
	AllowHosts []string
	// Rules are explicit host/port allow or deny rules. In
	// NetAllowList mode they define what is reachable; in NetProxy
	// mode deny rules override the upstream and everything else is
	// delegated to the upstream.
	Rules []sandbox.NetRule
	// Upstream is the proxy-mode upstream URL: "http://host:port" or
	// "socks5://[user:pass@]host:port". Ignored in allow-list mode.
	Upstream string
	// TCPLoopback binds 127.0.0.1:0 (an ephemeral loopback TCP port)
	// instead of a unix socket. The seatbelt backend uses this because
	// its child shares the host network stack and reaches the proxy
	// through the single SBPL-allowed loopback port. The bwrap backend
	// leaves it false and uses the unix socket for its bridge.
	TCPLoopback bool
	// MITM enables TLS termination for CONNECT tunnels (opt-in).
	MITM *sandbox.MITMPolicy
	// OnDecision receives one audit record per allow/deny decision.
	// It must not block the proxy; keep it fast and non-throwing.
	OnDecision func(ProxyDecision)
	// Hooks observe/block traffic. OnConnect applies to every CONNECT;
	// OnRequest / OnResponse run only on MITM-decrypted traffic.
	Hooks mitm.ProxyHooks
	// OutboundRoots overrides the roots used to verify the real
	// target's TLS certificate during MITM. Nil means system roots.
	OutboundRoots *x509.CertPool
}

ProxyConfig configures one enforcement proxy instance. It is named ProxyConfig (not Config) because httpkit already owns Config for the transport builder.

type ProxyDecision added in v0.5.5

type ProxyDecision struct {
	Host   string
	Port   int
	Action sandbox.NetAction
	Mode   sandbox.NetMode
	Rule   string
}

ProxyDecision is one audit record: which destination was decided, with which action, and which rule ("" = mode default) decided it.

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the total number of tries including the first.
	MaxAttempts int
	// BaseDelay seeds the exponential backoff.
	BaseDelay time.Duration
	// MaxDelay caps one backoff sleep (a Retry-After hint may exceed it).
	MaxDelay time.Duration
}

RetryConfig bounds the retry behaviour.

Directories

Path Synopsis
Package mitm implements opt-in TLS termination for the sandbox enforcement proxy: a per-run temporary CA signs leaf certificates for CONNECT hosts, the proxy terminates TLS with the child, applies hooks, and re-establishes TLS to the real target.
Package mitm implements opt-in TLS termination for the sandbox enforcement proxy: a per-run temporary CA signs leaf certificates for CONNECT hosts, the proxy terminates TLS with the child, applies hooks, and re-establishes TLS to the real target.

Jump to

Keyboard shortcuts

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