quic

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package quic implements a complete QUIC v1 transport from scratch, in both client and server roles (design in docs/HTTP3_DESIGN.md). The client role carries the HTTP/3 client; the server role (Listen, Listener.Accept, Conn.AcceptBidiStream, Conn.AcceptUniStream) reuses the same Conn, crypto, packet handling, and stream machinery. It is a QUIC server role, not an HTTP/3 server — the HTTP/3 mapping on top is client-only. It is the full engine, not just a codec: connection establishment and lifecycle (RFC 9000), stream multiplexing and flow control, the TLS 1.3 handshake over CRYPTO streams and AEAD packet + header protection with key update (RFC 9001), and ACK-based loss detection, PTO, RTT estimation, and NewReno congestion control with pacing (RFC 9002).

The frame codec is one layer within it: frames live inside a decrypted QUIC packet payload — a byte slice, not a stream — so the parser operates over a []byte and dispatches each frame to a FrameHandler (mirroring the frame package's Handler-visitor read model). All multi-byte fields are QUIC variable-length integers (internal/bytesx), a different encoding from the HPACK/QPACK prefixed integer.

The package does no application-level HTTP; http3 layers on top. It is NOT safe for concurrent use except where documented; the owning connection serializes access.

Index

Constants

View Source
const (
	ErrCodeNoError                 uint64 = 0x00 // NO_ERROR
	ErrCodeInternalError           uint64 = 0x01 // INTERNAL_ERROR
	ErrCodeConnectionRefused       uint64 = 0x02 // CONNECTION_REFUSED
	ErrCodeFlowControlError        uint64 = 0x03 // FLOW_CONTROL_ERROR
	ErrCodeStreamLimitError        uint64 = 0x04 // STREAM_LIMIT_ERROR
	ErrCodeStreamStateError        uint64 = 0x05 // STREAM_STATE_ERROR
	ErrCodeFinalSizeError          uint64 = 0x06 // FINAL_SIZE_ERROR
	ErrCodeFrameEncodingError      uint64 = 0x07 // FRAME_ENCODING_ERROR
	ErrCodeTransportParameterError uint64 = 0x08 // TRANSPORT_PARAMETER_ERROR
	ErrCodeConnectionIDLimitError  uint64 = 0x09 // CONNECTION_ID_LIMIT_ERROR
	ErrCodeProtocolViolation       uint64 = 0x0a // PROTOCOL_VIOLATION
	ErrCodeInvalidToken            uint64 = 0x0b // INVALID_TOKEN
	ErrCodeApplicationError        uint64 = 0x0c // APPLICATION_ERROR
	ErrCodeCryptoBufferExceeded    uint64 = 0x0d // CRYPTO_BUFFER_EXCEEDED
	ErrCodeKeyUpdateError          uint64 = 0x0e // KEY_UPDATE_ERROR
	ErrCodeAEADLimitReached        uint64 = 0x0f // AEAD_LIMIT_REACHED
	ErrCodeNoViablePath            uint64 = 0x10 // NO_VIABLE_PATH
	// CRYPTO_ERROR is the range 0x0100-0x01ff (0x0100 + the TLS alert).
	ErrCodeCryptoBase uint64 = 0x0100
)

Transport error codes (RFC 9000 §20.1), carried in a CONNECTION_CLOSE frame of type 0x1c.

View Source
const (
	FramePadding            uint64 = 0x00
	FramePing               uint64 = 0x01
	FrameACK                uint64 = 0x02
	FrameACKECN             uint64 = 0x03
	FrameResetStream        uint64 = 0x04
	FrameStopSending        uint64 = 0x05
	FrameCrypto             uint64 = 0x06
	FrameNewToken           uint64 = 0x07
	FrameStreamBase         uint64 = 0x08
	FrameStreamMax          uint64 = 0x0f
	FrameMaxData            uint64 = 0x10
	FrameMaxStreamData      uint64 = 0x11
	FrameMaxStreamsBidi     uint64 = 0x12
	FrameMaxStreamsUni      uint64 = 0x13
	FrameDataBlocked        uint64 = 0x14
	FrameStreamDataBlocked  uint64 = 0x15
	FrameStreamsBlockedBidi uint64 = 0x16
	FrameStreamsBlockedUni  uint64 = 0x17
	FrameNewConnectionID    uint64 = 0x18
	FrameRetireConnectionID uint64 = 0x19
	FramePathChallenge      uint64 = 0x1a
	FramePathResponse       uint64 = 0x1b
	FrameConnectionClose    uint64 = 0x1c
	FrameConnectionCloseApp uint64 = 0x1d
	FrameHandshakeDone      uint64 = 0x1e
)

Frame types (RFC 9000 §19 / §12.4). STREAM occupies 0x08–0x0f; the three low bits are flags (see streamFin/streamLen/streamOff).

View Source
const (
	// DefaultStreamRecvWindow is the per-stream receive window (initial
	// max_stream_data the client advertises, RFC 9000 §18.2).
	DefaultStreamRecvWindow uint64 = 256 << 10
	// DefaultConnRecvWindow is the connection-level receive window (initial
	// max_data).
	DefaultConnRecvWindow uint64 = 1 << 20
)

Receive-window sizes the client advertises and auto-tunes. They are the values NewConn seeds and that http3.Dial encodes in its transport parameters, keeping the advertised limits and the granting logic in one place.

View Source
const InitialDatagramMinSize = 1200

InitialDatagramMinSize is the minimum size of a datagram carrying a client Initial packet (RFC 9000 §14.1); the client pads to it so the server's anti-amplification limit is large enough to send its whole flight.

View Source
const QUICVersion1 uint32 = 0x00000001

QUICVersion1 is the QUIC v1 version number (RFC 9000 §15).

Variables

View Source
var ErrAEADLimit = errors.New("quic: AEAD usage limit reached")

ErrAEADLimit is returned when an AEAD usage limit is reached (RFC 9001 §6.6): the confidentiality limit on packets sealed with one key, or the integrity limit on packets that failed authentication. Because this client cannot initiate its own key update, it maps to a CONNECTION_CLOSE with the AEAD_LIMIT_REACHED (0x0f) transport error.

View Source
var ErrConnClosed = errors.New("quic: connection closed")

ErrConnClosed is returned by the send path when the connection is closed — by a received CONNECTION_CLOSE (draining), a local close, or an idle timeout — so no further packets may be sent (RFC 9000 §10.2).

View Source
var ErrConnectionIDLimit = errors.New("quic: peer exceeded the active connection ID limit")

ErrConnectionIDLimit is a connection error: the peer provided more active connection IDs than the active_connection_id_limit we advertised (RFC 9000 §5.1.1). It maps to CONNECTION_ID_LIMIT_ERROR (0x09).

View Source
var ErrCryptoBufferExceeded = errors.New("quic: crypto buffer exceeded")

ErrCryptoBufferExceeded is a connection error: buffering a received CRYPTO frame would push the out-of-order handshake reassembly buffer past a fixed bound. CRYPTO carries no flow control (RFC 9000 §7.5), so this cap is the only thing stopping a peer — or an on-path forger of Initial packets, whose keys derive from the observable connection ID — from exhausting memory with gapped CRYPTO before the handshake authenticates. It maps to CRYPTO_BUFFER_EXCEEDED (0x0d), which §7.5 permits in this case.

View Source
var ErrCryptoDecrypt = errors.New("quic: packet decryption failed")

ErrCryptoDecrypt is returned when AEAD authentication of a received packet fails (RFC 9001 §5.3) — a forged, corrupted, or wrong-key packet.

View Source
var ErrCryptoKey = errors.New("quic: invalid packet-protection key")

ErrCryptoKey is returned when packet-protection keys are the wrong size.

View Source
var ErrCryptoSample = errors.New("quic: packet too short for header protection sample")

ErrCryptoSample is returned when a packet is too short to sample header protection (RFC 9001 §5.4.2 requires 4 bytes of packet number plus a 16-byte sample of the protected payload).

View Source
var ErrCryptoSuite = errors.New("quic: unsupported cipher suite")

ErrCryptoSuite is returned when a negotiated TLS cipher suite is not yet supported for packet protection. The AES-GCM suites are supported; ChaCha20-Poly1305 header protection is deferred to a later phase.

View Source
var ErrFinalSize = errors.New("quic: stream final size error")

ErrFinalSize is a connection error: a stream's final size changed, or data was received past a declared final size, or a final size fell below data already received (FINAL_SIZE_ERROR, RFC 9000 §4.5).

View Source
var ErrFlowControl = errors.New("quic: peer exceeded a receive flow-control limit")

ErrFlowControl is a connection error: the peer sent stream data past a receive limit we advertised (FLOW_CONTROL_ERROR, RFC 9000 §4.1).

View Source
var ErrFrameEncoding = errors.New("quic: frame encoding error")

ErrFrameEncoding is returned by the frame parser when the input is malformed: a truncated field, a varint that runs past the buffer, a length that exceeds the remaining bytes, or an unknown frame type. It maps to the FRAME_ENCODING_ERROR (0x07) transport error (RFC 9000 §12.4).

View Source
var ErrHandshakeClosed = errors.New("quic: connection closed during handshake")

ErrHandshakeClosed is returned by Establish when the peer closes the connection (e.g. a CONNECTION_CLOSE for a TLS alert) before the handshake completes.

View Source
var ErrIdleTimeout = errors.New("quic: idle timeout")

ErrIdleTimeout is returned by the receive path when the connection has been idle longer than the negotiated max_idle_timeout and has been silently closed (RFC 9000 §10.1). No CONNECTION_CLOSE is sent; the state is discarded.

View Source
var ErrNoClientHello = errors.New("quic: handshake produced no ClientHello")

ErrNoClientHello is returned when the TLS handshake did not produce a ClientHello to send (an internal invariant failure).

View Source
var ErrNotEstablished = errors.New("quic: connection not established")

ErrNotEstablished is returned by Stream.Send before the 1-RTT keys are installed — application data may only be sent after the handshake completes.

View Source
var ErrNotInitial = errors.New("quic: datagram is not an Initial packet")

ErrNotInitial is returned by AcceptInitial when the datagram does not begin with a QUIC v1 Initial packet.

View Source
var ErrPacketEncoding = errors.New("quic: packet encoding error")

ErrPacketEncoding is returned by the packet-header parser when the input is too short or malformed: a truncated header, a connection ID length above 20, or a Length field that runs past the datagram (RFC 9000 §17).

View Source
var ErrProtocolViolation = errors.New("quic: protocol violation")

ErrProtocolViolation is a connection error for a peer that violates the protocol in a way with no more specific code — here, a packet whose header reserved bits are non-zero after protection is removed (RFC 9000 §17.2, §17.3.1). It maps to PROTOCOL_VIOLATION (0x0a).

View Source
var ErrServerBidiStream = errors.New("quic: server opened a bidirectional stream")

ErrServerBidiStream is a connection error: the server opened a bidirectional stream, which an HTTP/3 client never permits (STREAM_LIMIT_ERROR, RFC 9000 §4.6 — the client advertises no bidirectional stream credit to the server).

View Source
var ErrStatelessReset = errors.New("quic: stateless reset received")

ErrStatelessReset is returned when the peer signals a stateless reset — an unprocessable datagram ending in a reset token the peer gave us — so the connection is torn down (RFC 9000 §10.3). No CONNECTION_CLOSE is sent.

View Source
var ErrStreamFinished = errors.New("quic: stream already finished")

ErrStreamFinished is returned by Stream.Send once the stream's FIN has been sent; the final size is fixed and no further data may be sent (RFC 9000 §4.5).

View Source
var ErrStreamReset = errors.New("quic: stream reset")

ErrStreamReset is returned by Stream.Send after the send side has been reset with RESET_STREAM; no further data may be sent (RFC 9000 §3.2).

View Source
var ErrStreamState = errors.New("quic: stream state error")

ErrStreamState is a peer frame that violates a stream's directionality — a STREAM or RESET_STREAM on a send-only stream, or a STOP_SENDING on a receive-only stream — which is a STREAM_STATE_ERROR (RFC 9000 §19.4, §19.5, §19.8).

View Source
var ErrTooManyBidiStreams = errors.New("quic: peer exceeded bidirectional stream limit")

ErrTooManyBidiStreams is a connection error: the peer opened a bidirectional stream beyond the limit we advertised (STREAM_LIMIT_ERROR, RFC 9000 §4.6). A server sees this when a client opens too many request streams.

View Source
var ErrTooManyStreams = errors.New("quic: too many streams")

ErrTooManyStreams is returned by OpenStream when opening another stream would exceed the peer's advertised stream limit (RFC 9000 §4.6).

View Source
var ErrTooManyUniStreams = errors.New("quic: peer exceeded unidirectional stream limit")

ErrTooManyUniStreams is a connection error: the peer opened a unidirectional stream beyond the limit we advertised (STREAM_LIMIT_ERROR, RFC 9000 §4.6).

View Source
var ErrTransportParameter = errors.New("quic: transport parameter error")

ErrTransportParameter is returned when the peer's transport parameters are malformed, contain a duplicate identifier, or carry an invalid value. It maps to the TRANSPORT_PARAMETER_ERROR (0x08) transport error (RFC 9000 §7.4).

View Source
var ErrVersionNegotiation = errors.New("quic: server does not support QUIC v1 (version negotiation)")

ErrVersionNegotiation is returned when the server answers with a Version Negotiation packet: this client supports only QUIC v1, so it abandons the connection attempt (RFC 9000 §6.2).

Functions

func AppendAck

func AppendAck(dst []byte, largestAcked, ackDelay, firstAckRange uint64, ranges []AckRange) []byte

AppendAck appends an ACK frame (RFC 9000 §19.3). ranges are the additional ranges after First ACK Range, in wire order.

func AppendConnectionClose

func AppendConnectionClose(dst []byte, app bool, errCode, frameType uint64, reason []byte) []byte

AppendConnectionClose appends a CONNECTION_CLOSE frame. When app is true it is the application variant (0x1d, no Frame Type field); otherwise the transport variant (0x1c) with frameType.

func AppendCrypto

func AppendCrypto(dst []byte, offset uint64, data []byte) []byte

AppendCrypto appends a CRYPTO frame carrying data at the given stream offset.

func AppendDataBlocked

func AppendDataBlocked(dst []byte, limit uint64) []byte

AppendDataBlocked appends a DATA_BLOCKED frame.

func AppendHandshakeDone

func AppendHandshakeDone(dst []byte) []byte

AppendHandshakeDone appends a HANDSHAKE_DONE frame (server-only in practice).

func AppendLongHeader

func AppendLongHeader(dst []byte, typ PacketType, version uint32, dcid, scid, token []byte, pnLen int, length uint64) ([]byte, int)

AppendLongHeader appends an Initial, 0-RTT, or Handshake long header through the Length field (RFC 9000 §17.2). pnLen is the packet-number length in bytes (1–4), encoded into the low two bits of the first byte (header-protected on the wire). length is the byte count of the packet number plus payload that the caller will append next. It returns the extended slice and the offset at which the caller appends the packet number. Retry / Version Negotiation are server-only and have no writer.

func AppendMaxData

func AppendMaxData(dst []byte, maximum uint64) []byte

AppendMaxData appends a MAX_DATA frame.

func AppendMaxStreamData

func AppendMaxStreamData(dst []byte, streamID, maximum uint64) []byte

AppendMaxStreamData appends a MAX_STREAM_DATA frame.

func AppendMaxStreams

func AppendMaxStreams(dst []byte, uni bool, maximum uint64) []byte

AppendMaxStreams appends a MAX_STREAMS frame (bidirectional unless uni).

func AppendNewToken

func AppendNewToken(dst []byte, token []byte) []byte

AppendNewToken appends a NEW_TOKEN frame.

func AppendPadding

func AppendPadding(dst []byte, n int) []byte

AppendPadding appends n PADDING frames (n zero bytes).

func AppendPathResponse

func AppendPathResponse(dst []byte, data [8]byte) []byte

AppendPathResponse appends a PATH_RESPONSE frame echoing an 8-byte challenge.

func AppendPing

func AppendPing(dst []byte) []byte

AppendPing appends a PING frame.

func AppendResetStream

func AppendResetStream(dst []byte, streamID, errCode, finalSize uint64) []byte

AppendResetStream appends a RESET_STREAM frame.

func AppendRetireConnectionID

func AppendRetireConnectionID(dst []byte, seq uint64) []byte

AppendRetireConnectionID appends a RETIRE_CONNECTION_ID frame.

func AppendServerTransportParams added in v0.10.0

func AppendServerTransportParams(dst []byte, p ServerTransportParams, scid, origDCID []byte) []byte

AppendServerTransportParams encodes a server's transport parameters into the wire form carried in the TLS quic_transport_parameters extension.

scid is the Source Connection ID the server chose for this connection and origDCID the Destination Connection ID the client used in its first Initial. The client authenticates both (§7.3), so they are per-connection and cannot be hoisted into ServerTransportParams.

func AppendShortHeader

func AppendShortHeader(dst []byte, dcid []byte, pnLen int, keyPhase bool) ([]byte, int)

AppendShortHeader appends a short (1-RTT) header (RFC 9000 §17.3): the first byte (with the header-protected key-phase and packet-number-length bits) then the destination connection ID. It returns the extended slice and the packet-number offset.

func AppendStopSending

func AppendStopSending(dst []byte, streamID, errCode uint64) []byte

AppendStopSending appends a STOP_SENDING frame.

func AppendStream

func AppendStream(dst []byte, streamID, offset uint64, fin bool, data []byte) []byte

AppendStream appends a STREAM frame with an explicit Length field. The Offset field is emitted only when offset != 0 (offset 0 is the default). fin sets the FIN bit.

func AppendStreamDataBlocked

func AppendStreamDataBlocked(dst []byte, streamID, limit uint64) []byte

AppendStreamDataBlocked appends a STREAM_DATA_BLOCKED frame.

func AppendStreamsBlocked

func AppendStreamsBlocked(dst []byte, uni bool, limit uint64) []byte

AppendStreamsBlocked appends a STREAMS_BLOCKED frame (bidirectional unless uni).

func AppendTransportParams

func AppendTransportParams(dst []byte, p LocalTransportParams) []byte

AppendTransportParams encodes the client's transport parameters (RFC 9000 §18) into the wire form carried in the TLS quic_transport_parameters extension.

func BuildInitialPacket

func BuildInitialPacket(dst []byte, s *Sealer, dcid, scid, token []byte, pn uint64, pnLen int, cryptoOffset uint64, cryptoData []byte, minSize int) ([]byte, error)

BuildInitialPacket assembles and protects a client Initial packet carrying cryptoData (the TLS ClientHello) in a CRYPTO frame, padded with PADDING frames so the datagram is at least minSize bytes (use InitialDatagramMinSize for the first flight). dcid is the server's connection ID (the client's random original DCID before Retry), scid the client's chosen source ID (may be empty). token is the Retry/NEW_TOKEN token (nil for a first Initial). pn is the Initial packet number, pnLen its on-wire length (1–4). It appends the protected packet to dst.

func EnableGRO added in v0.10.0

func EnableGRO(rc syscall.RawConn) error

EnableGRO turns on UDP_GRO for the socket behind rc, asking the kernel to coalesce consecutive same-size datagrams into one recvmsg and report the segment size out of band. It is best-effort: a kernel or socket that rejects the option simply never attaches a GRO control message, so RecvGRO reports segSize 0 and the receive path processes one datagram per read, exactly as before. A nil rc (no raw fd) is a no-op.

func ParseFrames

func ParseFrames(payload []byte, h FrameHandler) error

ParseFrames parses every frame in a decrypted packet payload, dispatching each to h in order. It returns ErrFrameEncoding on any malformed frame (truncated field, varint past end, length past end, unknown type) — a connection error per RFC 9000 §12.4 — or the handler's error.

func RecvGRO added in v0.10.0

func RecvGRO(rc syscall.RawConn, fallback io.Reader, buf, oob []byte) (n, segSize int, err error)

RecvGRO reads one datagram, or one GRO-coalesced burst of datagrams, from the socket behind rc using the raw file descriptor, parsing the ancillary data for the UDP_GRO segment size. It returns n bytes read into buf and segSize: 0 (or >= n) for a single datagram, or 0 < segSize < n when buf[:n] holds several coalesced datagrams of segSize bytes (the last may be shorter). oob is a reused scratch buffer that receives the control message.

It degrades gracefully to a single plain Read (segSize 0) when rc is nil or a recvmsg quirk occurs, so a GRO bug can never mis-frame or drop a datagram — correctness over the syscall saving. A read-deadline expiry is surfaced verbatim: RawConn.Read honors the socket deadline through the runtime poller, so the receive path's PTO/idle timing and its non-blocking drain are unchanged.

stdlib-only: the datagram is read with syscall.Recvmsg and the control message parsed with syscall.ParseSocketControlMessage; no golang.org/x/sys.

func SealPacket added in v0.10.0

func SealPacket(dst []byte, s *Sealer, typ PacketType, dcid, scid, token []byte, pn uint64, pnLen int, payload []byte) ([]byte, error)

SealPacket builds and protects a single QUIC packet carrying payload (already framed) and appends it to dst. typ selects the form: PacketInitial, PacketHandshake, and PacketZeroRTT use a long header (dcid, scid, and — for Initial only — token); PacketShort uses a 1-RTT short header (dcid only). pn is the full packet number and pnLen its on-wire length (1–4).

The payload is padded with PADDING frames when it is too short for header protection to sample (RFC 9001 §5.4.2). Unlike BuildInitialPacket it does not pad to the 1200-byte anti-amplification floor — a server bounds its early flights to 3× the bytes it has received, so that padding is the caller's call. It is the send-side counterpart to AcceptInitial, used to assemble the server's response flights.

func SendGSO added in v0.10.0

func SendGSO(rc syscall.RawConn, fallback io.Writer, buf []byte, segSize int) (int, error)

SendGSO writes buf as consecutive UDP datagrams of segSize bytes (the last may be shorter) in ONE sendmsg via the UDP_SEGMENT offload, using the raw file descriptor behind rc. It is the Linux transport primitive the udpConn's WriteGSO delegates to. It degrades gracefully to a per-datagram loop over fallback when:

  • rc is nil or there is nothing to segment (a single datagram);
  • the kernel/NIC rejects the offload at send time (e.g. EIO on a path that cannot segment) — every datagram still goes out, correctness over the syscall saving.

stdlib-only: the control message is built by hand (gsoControl) and handed to syscall.SendmsgN; no golang.org/x/sys.

Types

type AckRange

type AckRange struct{ Gap, Length uint64 }

AckRange is one additional (post-first) ACK range in an ACK frame.

type ClientInitial added in v0.10.0

type ClientInitial struct {
	// DCID is the Destination Connection ID the client chose. The server derives
	// Initial keys from it (RFC 9001 §5.2) and echoes it back as the
	// original_destination_connection_id transport parameter (RFC 9000 §7.3).
	DCID []byte
	// SCID is the client's Source Connection ID; the server uses it as the
	// Destination Connection ID on the packets it sends back.
	SCID []byte
	// Token is the address-validation token: empty on a first flight, non-empty
	// when the client echoes a Retry or NEW_TOKEN token (RFC 9000 §8.1).
	Token []byte
	// CryptoData is the reassembled CRYPTO stream (the TLS ClientHello) carried by
	// this Initial.
	CryptoData []byte
}

ClientInitial is what a server extracts from a client's first Initial packet (RFC 9000 §17.2.2): the client's connection IDs, any address-validation token, and the reassembled CRYPTO stream carrying the TLS ClientHello.

func AcceptInitial added in v0.10.0

func AcceptInitial(datagram []byte) (*ClientInitial, error)

AcceptInitial parses and decrypts the Initial packet at the front of datagram, returning the client's connection IDs and the ClientHello it carries. Initial keys are derived from the client's Destination Connection ID (RFC 9001 §5.2), so this needs no prior connection state — it is the first step a server takes for an inbound connection. It returns ErrNotInitial if the datagram is not an Initial, or a decoding/decryption error if the packet is malformed or inauthentic.

CRYPTO reassembly assumes the ClientHello starts at offset 0 and is gap-free within this datagram (true for a first Initial). Reassembling a ClientHello that spans multiple datagrams is the connection engine's responsibility.

type CongestionControlAlgorithm added in v0.10.0

type CongestionControlAlgorithm int

CongestionControlAlgorithm selects a connection's congestion controller.

const (
	// CCNewReno is the RFC 9002 §7 NewReno controller (cc.go). It is the zero value
	// and the default, so a connection created without WithCongestionControl behaves
	// exactly as before options existed.
	CCNewReno CongestionControlAlgorithm = iota
	// CCBBR is the opt-in BBR v1 controller implemented in this file.
	CCBBR
)

type Conn

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

Conn is a QUIC v1 client connection (RFC 9000). It drives the TLS handshake (RFC 9001) over its packet-number spaces and owns the per-space send state. The connection engine is built up across phases; this scaffolding wires the handshake to the send path and manages establishment. Not safe for concurrent use by multiple goroutines.

func NewConn

func NewConn(pc PacketConn, tlsConfig *tls.Config, transportParams []byte, opts ...ConnOption) (*Conn, error)

NewConn creates a client QUIC connection over pc. tlsConfig must set ServerName; transportParams is the serialized QUIC transport parameters (RFC 9000 §7.4). It chooses a random Destination Connection ID, derives the Initial keys from it (RFC 9001 §5.2), and prepares the TLS handshake — but does not send anything until Establish.

opts are optional functional configuration (e.g. WithCongestionControl); with none supplied the connection uses NewReno, byte-for-byte as before options existed.

func NewServerConn added in v0.10.0

func NewServerConn(pc PacketConn, f *ServerFlight, clientDCID, clientSCID []byte) (*Conn, error)

NewServerConn builds a connected server-role Conn from a completed server handshake (f.Complete must be true). pc is the per-connection datagram transport; clientDCID is the Destination Connection ID from the client's first Initial (RFC 9000 §7.3), and clientSCID is the client's Source Connection ID, which becomes this connection's Destination CID. The 1-RTT and Handshake keys, the peer transport parameters, and the connection IDs are seeded from f, so the connection is ready to send and receive 1-RTT packets.

Key update (RFC 9001 §6) is not yet armed on a server connection, and the handshake is assumed already acknowledged; see docs/QUIC_SERVER_DESIGN.md.

func (*Conn) AcceptBidiStream added in v0.10.0

func (c *Conn) AcceptBidiStream() *Stream

AcceptBidiStream returns the next accepted client-initiated bidirectional stream (a request), or nil if none is pending. Server connections only. It does not block; the caller drives the connection with Poll and drains newly accepted streams between polls.

func (*Conn) AcceptUniStream

func (c *Conn) AcceptUniStream() *Stream

AcceptUniStream returns the next accepted server-initiated unidirectional stream, or nil if none is pending. It does not block; the caller drives the connection with Poll and drains newly accepted streams between polls.

func (*Conn) Close

func (c *Conn) Close() error

Close terminates the connection gracefully, sending a transport NO_ERROR CONNECTION_CLOSE (RFC 9000 §10.2) before closing the transport.

func (*Conn) CloseWithError

func (c *Conn) CloseWithError(app bool, code uint64, reason string) error

CloseWithError terminates the connection by sending a single CONNECTION_CLOSE frame (RFC 9000 §10.2) and closing the underlying transport. app selects the application-error variant (0x1d) over the transport variant (0x1c); code is the error code and reason an optional diagnostic phrase. It is idempotent: a call after the connection is already closed (by us or by the peer) only closes the transport, without sending a second frame.

Per RFC 9000 §10.2.3 an application close requested before 1-RTT keys exist cannot be sent as an application frame, so it is downgraded to a transport CONNECTION_CLOSE with APPLICATION_ERROR in the highest available space.

func (*Conn) ConnectionState added in v0.10.0

func (c *Conn) ConnectionState() tls.ConnectionState

ConnectionState returns the TLS connection state once the handshake has progressed enough to populate it (ALPN, negotiated cipher suite, peer certificates). It is a snapshot of the underlying crypto/tls state.

func (*Conn) Establish

func (c *Conn) Establish(ctx context.Context) error

Establish sends the client's Initial flight, then reads datagrams and drives the handshake — feeding inbound CRYPTO to TLS, installing keys, and sending the client's responding flights and acknowledgements — until the handshake completes (RFC 9000 §7, RFC 9001 §4). The whole handshake is bounded by handshakeTimeout so an anti-deadlock PTO (§6.2.2.1) cannot probe a server that acknowledges but never completes forever; the caller's ctx deadline, if nearer, still applies.

func (*Conn) HandshakeComplete

func (c *Conn) HandshakeComplete() error

HandshakeComplete marks the TLS handshake finished (HandshakeSink).

func (*Conn) OpenStream

func (c *Conn) OpenStream() (*Stream, error)

OpenStream opens the next client-initiated bidirectional stream (RFC 9000 §2.1: IDs 0, 4, 8, … — the low two bits are zero). It returns ErrTooManyStreams if opening another stream would exceed the peer's advertised initial_max_streams_bidi limit (§4.6). It does not send anything until the caller writes to the stream.

func (*Conn) OpenStreamContext added in v0.10.0

func (c *Conn) OpenStreamContext(ctx context.Context) (*Stream, error)

OpenStreamContext opens the next client-initiated bidirectional stream, blocking on stream credit rather than failing fast (docs/HTTP3_DESIGN.md §5, PR 2d). When the cumulative initial_max_streams_bidi limit is reached (RFC 9000 §4.6) it parks on c.streamCredit until the peer raises the limit with MAX_STREAMS (signalStreamCredit), ctx is cancelled, or the connection terminates, then retries. This is the documented backpressure a concurrent Do may see: it can block on stream credit until the peer grants more or ctx fires. A caller that prefers fail-fast uses OpenStream (which returns ErrTooManyStreams immediately).

func (*Conn) OpenUniStream

func (c *Conn) OpenUniStream() (*Stream, error)

OpenUniStream opens the next unidirectional stream this endpoint initiates (RFC 9000 §2.1: IDs 2, 6, 10, … for a client; 3, 7, 11, … for a server) — the HTTP/3 control and QPACK streams, which are send-only from the opener. It returns ErrTooManyStreams if opening another would exceed the peer's advertised initial_max_streams_uni limit (§4.6).

func (*Conn) PeerTransportParameters

func (c *Conn) PeerTransportParameters(params []byte) error

PeerTransportParameters parses the peer's transport parameters and seeds the connection-level send limit (HandshakeSink). A malformed or invalid parameter set aborts the handshake as a TRANSPORT_PARAMETER_ERROR (RFC 9000 §7.4).

func (*Conn) Poll

func (c *Conn) Poll(ctx context.Context) error

Poll reads a datagram (or, on a GRO-capable transport, a whole coalesced burst) and processes it — dispatching frames to open streams and sending acknowledgements — for driving receive after the handshake completes (RFC 9000 §13). It runs on the connection's reader goroutine.

The one structural change from the single-goroutine era (docs/HTTP3_DESIGN.md §3.2): the blocking read (c.readPacket) runs with c.mu RELEASED, so a concurrent Send can seal and write a request while the reader is parked. The sequence is: lock → leading flush (retransmits) → publish+arm the read deadline → arm→recheck ctx → UNLOCK → blocking read → relock → timestamp after reacquiring → process. Every error return latches terminateLocked so a blocked Do wakes. leading flush (retransmits) → publish+arm the read deadline → arm→recheck ctx → UNLOCK → blocking read → relock → timestamp after reacquiring → process. Every error return latches terminateLocked so a blocked Do wakes.

POSTCONDITION: Poll never returns holding c.mu — serviceControl and the H3 control servicing on the reader goroutine rely on this.

func (*Conn) SetReadKeys

func (c *Conn) SetReadKeys(level tls.QUICEncryptionLevel, suite uint16, secret []byte) error

SetReadKeys installs the receive Opener for level (HandshakeSink).

func (*Conn) SetWriteKeys

func (c *Conn) SetWriteKeys(level tls.QUICEncryptionLevel, suite uint16, secret []byte) error

SetWriteKeys installs the send Sealer for level (HandshakeSink).

func (*Conn) WriteCrypto

func (c *Conn) WriteCrypto(level tls.QUICEncryptionLevel, data []byte) error

WriteCrypto buffers handshake bytes to send in CRYPTO frames at level's packet-number space (HandshakeSink).

type ConnOption added in v0.10.0

type ConnOption func(*Conn)

ConnOption configures a Conn at construction time. Options are additive and applied in order after the connection's defaults are set; passing none leaves every default in place (in particular, NewReno congestion control).

func WithCongestionControl added in v0.10.0

func WithCongestionControl(a CongestionControlAlgorithm) ConnOption

WithCongestionControl selects the congestion-control algorithm for a connection (default CCNewReno). Pass WithCongestionControl(CCBBR) to NewConn to opt into BBR.

type DatagramResult

type DatagramResult struct {
	Processed          int  // packets decrypted and dispatched to the handler
	Skipped            int  // packets skipped: keys unavailable, or decryption failed
	Retry              bool // a Retry packet was present
	VersionNegotiation bool
}

DatagramResult reports what ProcessDatagram did with one UDP datagram.

func ProcessDatagram

func ProcessDatagram(datagram []byte, dcidLen int, ks *KeySet, largestAcked func(PacketType) uint64, h FrameHandler) (DatagramResult, error)

ProcessDatagram parses every QUIC packet coalesced in a single UDP datagram (RFC 9000 §12.2), removes protection with the matching level's Opener, and dispatches each packet's frames to h in order. dcidLen is the local connection-ID length (to locate a short-header DCID). largestAcked returns the largest packet number already acknowledged in a packet type's number space, for reconstruction (§A.3).

Packets whose keys are not yet installed, or that fail to authenticate (for example a reordered packet from a discarded key phase), are skipped rather than failing the datagram — a datagram may legitimately mix readable and not-yet-readable packets. A structurally malformed header stops parsing and returns ErrPacketEncoding. Retry and Version Negotiation packets carry no protected payload and are only flagged for the caller to handle.

This helper does NOT enforce the RFC 9000 §12.4 frame-type-by-space rules; it parses with the caller's handler for any space. The live Conn receive path (recvDatagram) uses a space-aware handler that gates frames; this exported entry point is not on that path.

type FrameHandler

type FrameHandler interface {
	OnPadding(runLen int) error
	OnPing() error
	OnAck(largestAcked, ackDelay, firstAckRange uint64) error
	OnAckRange(gap, length uint64) error
	OnAckECN(ect0, ect1, ce uint64) error
	OnResetStream(streamID, errCode, finalSize uint64) error
	OnStopSending(streamID, errCode uint64) error
	OnCrypto(offset uint64, data []byte) error
	OnNewToken(token []byte) error
	OnStream(streamID, offset uint64, fin bool, data []byte) error
	OnMaxData(maximum uint64) error
	OnMaxStreamData(streamID, maximum uint64) error
	OnMaxStreams(uni bool, maximum uint64) error
	OnDataBlocked(limit uint64) error
	OnStreamDataBlocked(streamID, limit uint64) error
	OnStreamsBlocked(uni bool, limit uint64) error
	OnNewConnectionID(seq, retirePriorTo uint64, connID []byte, resetToken *[16]byte) error
	OnRetireConnectionID(seq uint64) error
	OnPathChallenge(data *[8]byte) error
	OnPathResponse(data *[8]byte) error
	OnConnectionClose(app bool, errCode, frameType uint64, reason []byte) error
	OnHandshakeDone() error
}

FrameHandler receives each frame parsed from a packet payload. Byte slices and array pointers alias the input payload and are valid only for the call; copy to retain. Any non-nil error aborts parsing and propagates out of ParseFrames. ACK ranges are delivered as OnAck followed by OnAckRange per additional range, then OnAckECN for the 0x03 variant.

type HandshakeSink

type HandshakeSink interface {
	// WriteCrypto is called with handshake bytes to send in CRYPTO frames at
	// the given encryption level.
	WriteCrypto(level tls.QUICEncryptionLevel, data []byte) error
	// SetReadKeys / SetWriteKeys deliver the traffic secret and cipher suite for
	// a level so the caller can install packet-protection keys.
	SetReadKeys(level tls.QUICEncryptionLevel, suite uint16, secret []byte) error
	SetWriteKeys(level tls.QUICEncryptionLevel, suite uint16, secret []byte) error
	// PeerTransportParameters delivers the peer's raw QUIC transport parameters.
	PeerTransportParameters(params []byte) error
	// HandshakeComplete is called once the TLS handshake finishes.
	HandshakeComplete() error
}

HandshakeSink receives TLS handshake progress from a TLSHandshake pump. The byte slices passed in alias crypto/tls-owned memory valid only until the next pump step — copy to retain. Any non-nil error aborts the pump.

type Header struct {
	Type      PacketType
	Version   uint32
	DCID      []byte
	SCID      []byte // long headers only
	Token     []byte // Initial packets (Token) and Retry packets (Retry Token)
	Length    uint64 // Initial/0-RTT/Handshake: byte count of packet number + payload
	PNOffset  int
	PacketLen int
}

Header is a parsed QUIC packet header. Byte slices alias the input packet. PNOffset is the offset of the (still header-protected) packet number, or 0 when the packet carries none (Retry, Version Negotiation). PacketLen is the total bytes this packet occupies within a (possibly coalesced) datagram.

func ParseHeader

func ParseHeader(pkt []byte, dcidLen int) (Header, error)

ParseHeader parses the header of the QUIC packet at the front of pkt. dcidLen is the length of the local (client-chosen) connection ID, needed to locate the implicit-length DCID in a short header; it is ignored for long headers. It parses only the unprotected header fields — the packet number and payload remain header-protected / AEAD-encrypted (removed by the RFC 9001 layer).

type KeySet

type KeySet struct {
	Initial   *Opener
	Handshake *Opener
	OneRTT    *Opener
}

KeySet holds the receive-side Openers for each encryption level. The connection installs each as the TLS handshake surfaces the corresponding read secret (RFC 9001 §4). A nil Opener means keys for that level are not yet available, so packets at that level are buffered/skipped.

type Listener added in v0.10.0

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

Listener accepts server-role QUIC connections on a single UDP socket. It reads datagrams, routes them to the connection named by their Destination Connection ID, and drives the handshake for peers it has not seen. Accept hands back established connections; the caller drives each one with Poll, exactly as a client drives its own.

Not yet implemented (see docs/QUIC_SERVER_DESIGN.md): Retry / address validation, so this listener answers every Initial; connection migration; and per-peer rate limiting. Each new Initial starts one goroutine for its handshake, which an unvalidated peer can use to make the server do TLS work — front it with a rate limiter before exposing it to the internet.

func Listen added in v0.10.0

func Listen(addr string, cfg *tls.Config, p ServerTransportParams) (*Listener, error)

Listen starts a QUIC server on addr ("host:port"; port 0 picks one). cfg must carry the server's certificate(s); p is the limits the server grants a client. The per-connection transport parameters — which also carry the connection IDs a client authenticates — are built per handshake from p.

func (*Listener) Accept added in v0.10.0

func (l *Listener) Accept(ctx context.Context) (*Conn, error)

Accept returns the next established connection. It blocks until one is ready, ctx is done, or the listener is closed (net.ErrClosed). The caller owns the returned Conn and must drive it with Poll.

func (*Listener) Addr added in v0.10.0

func (l *Listener) Addr() net.Addr

Addr returns the address the listener is bound to.

func (*Listener) Close added in v0.10.0

func (l *Listener) Close() error

Close stops the listener and its demux loop, and abandons any handshake still in flight. Connections already handed out by Accept are untouched — they belong to the caller — so Close does not interrupt in-flight requests; it only stops new ones from arriving. It returns once the demux loop and every pending handshake have stopped.

type LocalTransportParams

type LocalTransportParams struct {
	// InitialMaxData is the connection-level receive limit (0x04).
	InitialMaxData uint64
	// InitialMaxStreamDataBidiLocal is the per-stream receive limit for streams
	// the client opens — the response body arrives here (0x05).
	InitialMaxStreamDataBidiLocal uint64
	// InitialMaxStreamDataUni is the per-stream receive limit for server-opened
	// unidirectional streams — the server control and QPACK streams (0x07).
	InitialMaxStreamDataUni uint64
	// InitialMaxStreamsUni is how many unidirectional streams the server may
	// open (0x09); HTTP/3 needs at least three (control + QPACK encoder/decoder).
	InitialMaxStreamsUni uint64
	// SourceConnectionID is the client's chosen source connection ID (0x0f),
	// which the server verifies (§7.3). It may be zero-length.
	SourceConnectionID []byte
	// MaxIdleTimeout is the idle timeout the client advertises, in milliseconds
	// (0x01, §10.1). Zero omits the parameter, leaving the effective timeout to the
	// server's advertised value.
	MaxIdleTimeout uint64
}

LocalTransportParams are the transport parameters a client advertises to the server (RFC 9000 §18.2, client role). They tell the server the limits within which it may send — most importantly the credit for the response, which arrives on a client-initiated bidirectional stream and so is governed by the client's own initial_max_stream_data_bidi_local (0x05).

type Opener

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

Opener removes QUIC packet protection on the receive path.

func NewOpener

func NewOpener(k PacketKeys) (*Opener, error)

NewOpener builds an Opener from a set of PacketKeys.

func (*Opener) Open

func (o *Opener) Open(pkt []byte, pnOffset int, largestAcked uint64) (pn uint64, pnLen int, payload []byte, err error)

Open removes header protection and AEAD-decrypts a received packet in place. pkt is the full protected packet (its first byte and packet number are unprotected in place); pnOffset is the packet-number offset; largestAcked is the largest packet number already acknowledged in this packet-number space (for reconstruction, RFC 9000 §A.3). It returns the full packet number, the packet-number length, and the decrypted payload, or an error.

type PacketConn

type PacketConn interface {
	Write(p []byte) (int, error)
	Read(p []byte) (int, error)
	Close() error
}

PacketConn is the datagram transport a Conn sends and receives on — typically a connected *net.UDPConn. Read and Write operate on whole QUIC datagrams.

type PacketKeys

type PacketKeys struct {
	Key   []byte
	IV    []byte
	HP    []byte
	Suite uint16
}

PacketKeys holds the packet-protection material derived from one traffic secret (RFC 9001 §5.1): the AEAD key, the 12-byte IV, and the header-protection key. Suite is the negotiated TLS cipher suite the keys are for; it selects the AEAD and header-protection algorithm when a Sealer/Opener is built (a zero Suite is treated as AEAD_AES_128_GCM for hand-built keys).

func InitialKeys

func InitialKeys(dcid []byte) (client, server PacketKeys)

InitialKeys derives the client and server Initial packet keys from the client's original Destination Connection ID (RFC 9001 §5.2). Initial packets always use AEAD_AES_128_GCM with SHA-256, regardless of the TLS suite later negotiated.

func KeysFromSecret

func KeysFromSecret(suite uint16, secret []byte) (PacketKeys, error)

KeysFromSecret derives packet-protection keys (key/iv/hp) from a TLS traffic secret and the negotiated cipher suite (RFC 9001 §5.1). The suite selects the KDF hash and key length and is stamped onto the returned keys so the Sealer / Opener built from them uses the matching AEAD and header-protection algorithm. The AES-GCM suites and ChaCha20-Poly1305 are supported; any other suite returns ErrCryptoSuite.

type PacketType

type PacketType uint8

PacketType identifies a QUIC packet (RFC 9000 §17).

const (
	PacketInitial            PacketType = iota // long header, type bits 00
	PacketZeroRTT                              // long header, type bits 01
	PacketHandshake                            // long header, type bits 10
	PacketRetry                                // long header, type bits 11
	PacketShort                                // short header (1-RTT)
	PacketVersionNegotiation                   // long form, Version == 0
)

type PeerClosedError

type PeerClosedError struct {
	App    bool
	Code   uint64
	Reason string
}

PeerClosedError reports that the peer terminated the connection with a CONNECTION_CLOSE frame (RFC 9000 §10.2). App is true for an application-level close (frame type 0x1d); Code is the error code and Reason the diagnostic phrase. Poll returns it once a CONNECTION_CLOSE has been received.

func (*PeerClosedError) Error

func (e *PeerClosedError) Error() string

Error implements error.

type Sealer

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

Sealer applies QUIC packet protection on the send path (RFC 9001 §5): AEAD encryption of the frame payload, then header protection of the first byte and packet-number field. One Sealer is built per encryption level from that level's PacketKeys. Not safe for concurrent use.

func NewSealer

func NewSealer(k PacketKeys) (*Sealer, error)

NewSealer builds a Sealer for the packet-protection suite named by k.Suite (AES-GCM or ChaCha20-Poly1305) from a set of PacketKeys.

func (*Sealer) Seal

func (s *Sealer) Seal(dst, hdr []byte, pnOffset, pnLen int, pn uint64, payload []byte) ([]byte, error)

Seal protects a packet and appends it to dst. hdr is the unprotected header including the pnLen-byte packet number (it is the AEAD associated data); pnOffset is the packet-number offset within hdr; pn is the full packet number; payload is the frame bytes. The returned packet is hdr followed by the AEAD ciphertext+tag, with the first byte and packet number header-protected. The payload plus tag must be long enough to sample header protection (RFC 9001 §5.4.2); Initial packets are padded to satisfy this.

type ServerFlight added in v0.10.0

type ServerFlight struct {
	// Datagrams are the sealed packets to send to the client, each its own
	// datagram: the Initial (ServerHello) and the Handshake flight
	// (EncryptedExtensions .. Finished).
	Datagrams [][]byte
	// Handshake is the in-progress server handshake, driven further via
	// HandleClientHandshake as the client's CRYPTO arrives.
	Handshake *TLSHandshake
	// HandshakeSealer / HandshakeOpener protect Handshake-level packets sent and
	// received. AppSealer / AppOpener protect 1-RTT packets and are non-nil once
	// application keys are installed (AppSealer after the server's Finished,
	// AppOpener once the handshake completes).
	HandshakeSealer *Sealer
	HandshakeOpener *Opener
	AppSealer       *Sealer
	AppOpener       *Opener
	// Complete reports whether the TLS handshake has finished.
	Complete bool
	// PeerTransportParams is the client's raw QUIC transport parameters, delivered
	// during the handshake (RFC 9000 §7.4); a server connection parses them for the
	// client's flow-control and stream limits.
	PeerTransportParams []byte
	// LocalTransportParams is the server's own serialized transport parameters
	// (the tp passed to StartServerHandshake); a server connection parses them for
	// the stream and idle limits it advertised.
	LocalTransportParams []byte
	// SCID is the server's source connection ID used on the response packets.
	SCID []byte
	// contains filtered or unexported fields
}

ServerFlight is the server's response to a client Initial and the state to carry the connection forward: the sealed datagram(s) to send, the handshake, and the send/receive keys as they are installed.

func StartServerHandshake added in v0.10.0

func StartServerHandshake(ci *ClientInitial, cfg *tls.Config, tp, scid []byte) (*ServerFlight, error)

StartServerHandshake processes a client's first Initial (as returned by AcceptInitial), drives the TLS server through its first flight, and returns the sealed response datagrams plus the state to continue the connection. cfg carries the server certificate(s); tp is the server's serialized transport parameters; scid is the server's chosen source connection ID, which the client adopts as its destination CID. Reply packets are addressed to the client's source connection ID (ci.SCID).

It produces only the server's first flight; the handshake completes later, once the client's Handshake-level Finished is fed to the returned Handshake.

func (*ServerFlight) HandleClientHandshake added in v0.10.0

func (f *ServerFlight) HandleClientHandshake(cryptoData []byte) error

HandleClientHandshake feeds the client's Handshake-level CRYPTO (its Finished) into the server handshake and pumps it. On success the TLS handshake is complete and 1-RTT keys are installed: Complete reports true, and AppSealer / AppOpener protect application (1-RTT) packets sent and received.

type ServerTransportParams added in v0.10.0

type ServerTransportParams struct {
	// MaxStreamsBidi is how many bidirectional streams a client may open (0x08) —
	// one per in-flight request.
	MaxStreamsBidi uint64
	// MaxStreamsUni is how many unidirectional streams a client may open (0x09).
	// HTTP/3 needs at least three: the control stream and the two QPACK streams.
	MaxStreamsUni uint64
	// MaxIdleTimeout is the idle timeout the server advertises, in milliseconds
	// (0x01, §10.1). Zero omits the parameter.
	MaxIdleTimeout uint64
}

ServerTransportParams are the transport parameters a server advertises (RFC 9000 §18.2, server role): the limits within which a client may send.

The receive windows are deliberately not settable here. AppendServerTransportParams advertises exactly the windows a server connection enforces (DefaultConnRecvWindow and DefaultStreamRecvWindow, see NewServerConn and acceptPeerBidiStream), so a client is never told it may send more than the connection will accept.

type Stream

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

Stream is a QUIC stream. For an HTTP/3 client the relevant streams are client-initiated bidirectional streams (one request/response exchange each).

func (*Stream) Finished

func (s *Stream) Finished() bool

Finished reports whether the receive side is complete — either the FIN has arrived with every byte contiguous (RFC 9000 §2.2), or the peer aborted the stream with RESET_STREAM (§3.5).

func (*Stream) ID

func (s *Stream) ID() uint64

ID returns the stream's QUIC stream identifier.

func (*Stream) Recv

func (s *Stream) Recv() []byte

Recv returns the stream bytes that have become contiguous since the previous Recv call (the newly available prefix of the received data), for reading a response. It returns an empty slice when nothing new is available. The result aliases the stream's buffer and should be consumed before the next Recv. Consuming bytes frees receive-flow-control window, which may queue MAX_STREAM_DATA / MAX_DATA to grant the peer more credit (RFC 9000 §4.1).

func (*Stream) RecvState added in v0.10.0

func (s *Stream) RecvState() (finished, reset bool, code uint64)

RecvState returns a locked snapshot of the receive side (docs/HTTP3_DESIGN.md §3.3, fix F5): finished mirrors Finished (a clean complete FIN or a peer RESET_STREAM), reset mirrors ResetReceived, and code is the reset's application error code. The reader mutates recv.fin/finalSize/data and recvReset under conn.mu in OnStream/OnResetStream, so a consumer must read them under the same lock rather than via the individual lock-free accessors. Added in PR 2b for the future Do read loop; not yet called by Do.

func (*Stream) Reset

func (s *Stream) Reset(errCode uint64) error

Reset abruptly terminates the send side of the stream with an application error code, sending a RESET_STREAM frame (RFC 9000 §3.2, §19.4). No further data may be sent (Send returns ErrStreamReset). It is idempotent and a no-op once the stream's FIN has been sent — the peer already has the whole request.

func (*Stream) ResetCode

func (s *Stream) ResetCode() uint64

ResetCode returns the application error code the peer carried in its RESET_STREAM (RFC 9000 §19.4); it is meaningful only when ResetReceived is true. An HTTP/3 caller maps it to a request-level error (RFC 9114 §8.1).

func (*Stream) ResetReceived

func (s *Stream) ResetReceived() bool

ResetReceived reports whether the peer aborted its send side with RESET_STREAM (RFC 9000 §3.5) — an abrupt end that, unlike a clean FIN, may fall in the middle of a frame. Callers distinguish it from a clean, complete receive.

func (*Stream) Send

func (s *Stream) Send(data []byte, fin bool) (int, error)

Send writes data on the stream as one or more STREAM frames over the 1-RTT packet space, honoring the peer's per-stream and connection send limits (RFC 9000 §4.1). It consumes a PREFIX of data: on a flow-control block it returns the number of bytes admitted with a nil error, having emitted a STREAM_DATA_BLOCKED or DATA_BLOCKED frame; the caller retries with data advanced by the returned count once a MAX_STREAM_DATA / MAX_DATA frame raises the limit. fin marks the end of the stream; the FIN rides on the frame with the last byte (or a zero-length frame for empty data) and consumes no flow-control credit, so it is never blocked. Send after the FIN returns ErrStreamFinished.

func (*Stream) StopSending

func (s *Stream) StopSending(errCode uint64) error

StopSending requests that the peer abort the send side of the stream (its data flow toward us) with an application error code, sending a STOP_SENDING frame (RFC 9000 §3.5, §19.5). Used to abandon a response the caller no longer wants, so the peer stops filling our receive buffer. Idempotent.

func (*Stream) WaitReadable added in v0.10.0

func (s *Stream) WaitReadable(ctx context.Context) error

WaitReadable blocks until the stream may have more response data to read, its context is cancelled, or the connection terminates (docs/HTTP3_DESIGN.md §3.3). It is level-triggered: a woken caller re-reads its predicate (Recv / RecvState) under conn.mu before deciding to block again, so the cap-1 s.ready buffer plus that recheck close the check-then-block lost-wakeup window. Added in PR 2b but not yet driven by Do (the request path still inline-polls).

func (*Stream) WaitSendable added in v0.10.0

func (s *Stream) WaitSendable(ctx context.Context) error

WaitSendable blocks until the stream may admit more send data, its context is cancelled, or the connection terminates (docs/HTTP3_DESIGN.md §3.3). It owns the pacing timer: blockCong/blockStream/blockConn all resolve to an inbound-event wake via s.ready, but blockPace refills on wall-clock time with no inbound event, so only that case arms a time.After(pacingRefillDelay). The sendBlock read and the timer are taken under conn.mu, released before the select, so the lock is never held across the wait. Added in PR 2b but not yet driven by Do.

type TLSHandshake

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

TLSHandshake drives the TLS 1.3 handshake for a QUIC connection via crypto/tls.QUICConn (RFC 9001 §4). The transport engine feeds it inbound CRYPTO bytes and pumps events into its own HandshakeSink; crypto/tls owns the TLS state machine and surfaces the secrets and handshake bytes QUIC needs.

func NewClientHandshake

func NewClientHandshake(cfg *tls.Config, tp []byte) *TLSHandshake

NewClientHandshake creates a client-side QUIC TLS handshake. cfg must set ServerName; it is cloned and forced to TLS 1.3 with ALPN "h3" when unset. tp is the client's serialized QUIC transport parameters (RFC 9000 §7.4).

func NewServerHandshake added in v0.10.0

func NewServerHandshake(cfg *tls.Config, tp []byte) *TLSHandshake

NewServerHandshake creates a server-side QUIC TLS handshake. cfg must carry the server's certificate(s); it is cloned and forced to TLS 1.3, with ALPN "h3" filled in when unset. tp is the server's serialized QUIC transport parameters (RFC 9000 §7.4), which crypto/tls emits in the EncryptedExtensions. It is the server-role counterpart to NewClientHandshake: the transport engine feeds it the client's Initial CRYPTO and pumps events the same way.

func (*TLSHandshake) Close

func (h *TLSHandshake) Close() error

Close releases the TLS handshake state.

func (*TLSHandshake) ConnectionState

func (h *TLSHandshake) ConnectionState() tls.ConnectionState

ConnectionState returns the TLS connection state (ALPN, cipher suite, peer certificates) once the handshake has progressed enough to populate it.

func (*TLSHandshake) HandleCrypto

func (h *TLSHandshake) HandleCrypto(level tls.QUICEncryptionLevel, data []byte) error

HandleCrypto feeds inbound CRYPTO-frame bytes for an encryption level into the TLS state machine (RFC 9001 §4.1.3).

func (*TLSHandshake) Pump

func (h *TLSHandshake) Pump(sink HandshakeSink) error

Pump drains all currently-available TLS events into sink, installing this side's transport parameters when TLS asks for them.

func (*TLSHandshake) Start

func (h *TLSHandshake) Start(ctx context.Context) error

Start begins the handshake (the client generates its ClientHello). Pump must be called afterwards to drain the resulting events.

type TransportParams

type TransportParams struct {
	// InitialMaxData is the connection-level send limit: the maximum total
	// bytes the client may send in STREAM frames across all streams (0x04).
	InitialMaxData uint64
	// InitialMaxStreamDataBidiRemote is the per-stream send limit for streams
	// the client initiates — client-initiated bidirectional "request" streams
	// (0x06). The peer names the parameter from its own perspective, so
	// "remote" means opened by the receiver of the parameter (the client).
	InitialMaxStreamDataBidiRemote uint64
	// InitialMaxStreamDataBidiLocal is the per-stream receive limit the peer
	// applies to bidirectional streams the peer itself initiates (0x05). A server
	// uses it as its send limit on a client-initiated request stream. The client
	// send path does not consume it; it is retained for the server role.
	InitialMaxStreamDataBidiLocal uint64
	// InitialMaxStreamsBidi is the maximum number of bidirectional streams the
	// client is permitted to open (0x08).
	InitialMaxStreamsBidi uint64
	// InitialMaxStreamDataUni is the per-stream send limit for unidirectional
	// streams the client opens — the HTTP/3 control and QPACK streams (0x07).
	InitialMaxStreamDataUni uint64
	// InitialMaxStreamsUni is the maximum number of unidirectional streams the
	// client is permitted to open (0x09).
	InitialMaxStreamsUni uint64
	// InitialSourceConnectionID is the server's source connection ID as echoed in
	// its transport parameters (0x0f). The client authenticates it against the
	// server's actual SCID (§7.3). HaveInitialSourceConnectionID records whether
	// the parameter was present, so an absent one (a §7.3 error) is distinguished
	// from a zero-length connection ID.
	InitialSourceConnectionID     []byte
	HaveInitialSourceConnectionID bool
	// OriginalDestinationConnectionID (0x00) is the Destination Connection ID of the
	// client's first Initial; the client authenticates it against the value it chose
	// (§7.3). RetrySourceConnectionID (0x10) must be present exactly when a Retry was
	// received and must equal that Retry's Source Connection ID (§7.3). The Have*
	// flags distinguish an absent parameter (a §7.3 error) from a zero-length CID.
	OriginalDestinationConnectionID     []byte
	HaveOriginalDestinationConnectionID bool
	RetrySourceConnectionID             []byte
	HaveRetrySourceConnectionID         bool
	// AckDelayExponent is the peer's ack_delay_exponent (0x0a): the ACK Delay
	// field the peer sends is scaled by 2^AckDelayExponent microseconds. Default 3.
	AckDelayExponent uint64
	// MaxAckDelay is the peer's max_ack_delay (0x0b): the longest it will delay an
	// acknowledgement. It bounds the ack delay used in RTT estimation and feeds the
	// PTO. Default 25 ms.
	MaxAckDelay time.Duration
	// MaxIdleTimeout is the peer's max_idle_timeout (0x01): after this much idle
	// time the peer will close the connection. The effective idle timeout is the
	// minimum of the two endpoints' non-zero values (§10.1). Zero (absent or
	// explicit) means the peer imposes no idle timeout.
	MaxIdleTimeout time.Duration
	// StatelessResetToken is the peer's stateless_reset_token (0x02), the 16-byte
	// token tied to the connection ID it used during the handshake. A datagram
	// ending in it signals a stateless reset (§10.3). HaveStatelessResetToken
	// records whether the parameter was present.
	StatelessResetToken     [16]byte
	HaveStatelessResetToken bool
}

TransportParams holds the peer's QUIC transport parameters that a client needs to bound its own sending (RFC 9000 §18.2). Only the parameters the send path consumes are retained; others are parsed for validation but discarded.

func ParseTransportParams

func ParseTransportParams(raw []byte) (TransportParams, error)

ParseTransportParams decodes the peer's transport parameters (RFC 9000 §18). Each parameter is an identifier, a length, and that many value bytes; the integer-valued parameters encode their value as one QUIC varint whose encoded length must equal the declared length. A duplicate identifier, a truncated or over-long field, a malformed integer encoding, or an invalid value is a TRANSPORT_PARAMETER_ERROR (§7.4). Unknown identifiers (including GREASE) are ignored.

Jump to

Keyboard shortcuts

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