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
- Variables
- func AppendAck(dst []byte, largestAcked, ackDelay, firstAckRange uint64, ranges []AckRange) []byte
- func AppendConnectionClose(dst []byte, app bool, errCode, frameType uint64, reason []byte) []byte
- func AppendCrypto(dst []byte, offset uint64, data []byte) []byte
- func AppendDataBlocked(dst []byte, limit uint64) []byte
- func AppendHandshakeDone(dst []byte) []byte
- func AppendLongHeader(dst []byte, typ PacketType, version uint32, dcid, scid, token []byte, ...) ([]byte, int)
- func AppendMaxData(dst []byte, maximum uint64) []byte
- func AppendMaxStreamData(dst []byte, streamID, maximum uint64) []byte
- func AppendMaxStreams(dst []byte, uni bool, maximum uint64) []byte
- func AppendNewToken(dst []byte, token []byte) []byte
- func AppendPadding(dst []byte, n int) []byte
- func AppendPathResponse(dst []byte, data [8]byte) []byte
- func AppendPing(dst []byte) []byte
- func AppendResetStream(dst []byte, streamID, errCode, finalSize uint64) []byte
- func AppendRetireConnectionID(dst []byte, seq uint64) []byte
- func AppendServerTransportParams(dst []byte, p ServerTransportParams, scid, origDCID []byte) []byte
- func AppendShortHeader(dst []byte, dcid []byte, pnLen int, keyPhase bool) ([]byte, int)
- func AppendStopSending(dst []byte, streamID, errCode uint64) []byte
- func AppendStream(dst []byte, streamID, offset uint64, fin bool, data []byte) []byte
- func AppendStreamDataBlocked(dst []byte, streamID, limit uint64) []byte
- func AppendStreamsBlocked(dst []byte, uni bool, limit uint64) []byte
- func AppendTransportParams(dst []byte, p LocalTransportParams) []byte
- func BuildInitialPacket(dst []byte, s *Sealer, dcid, scid, token []byte, pn uint64, pnLen int, ...) ([]byte, error)
- func EnableGRO(rc syscall.RawConn) error
- func ParseFrames(payload []byte, h FrameHandler) error
- func RecvGRO(rc syscall.RawConn, fallback io.Reader, buf, oob []byte) (n, segSize int, err error)
- func SealPacket(dst []byte, s *Sealer, typ PacketType, dcid, scid, token []byte, pn uint64, ...) ([]byte, error)
- func SendGSO(rc syscall.RawConn, fallback io.Writer, buf []byte, segSize int) (int, error)
- type AckRange
- type ClientInitial
- type CongestionControlAlgorithm
- type Conn
- func (c *Conn) AcceptBidiStream() *Stream
- func (c *Conn) AcceptUniStream() *Stream
- func (c *Conn) Close() error
- func (c *Conn) CloseWithError(app bool, code uint64, reason string) error
- func (c *Conn) ConnectionState() tls.ConnectionState
- func (c *Conn) Establish(ctx context.Context) error
- func (c *Conn) HandshakeComplete() error
- func (c *Conn) OpenStream() (*Stream, error)
- func (c *Conn) OpenStreamContext(ctx context.Context) (*Stream, error)
- func (c *Conn) OpenUniStream() (*Stream, error)
- func (c *Conn) PeerTransportParameters(params []byte) error
- func (c *Conn) Poll(ctx context.Context) error
- func (c *Conn) SetReadKeys(level tls.QUICEncryptionLevel, suite uint16, secret []byte) error
- func (c *Conn) SetWriteKeys(level tls.QUICEncryptionLevel, suite uint16, secret []byte) error
- func (c *Conn) WriteCrypto(level tls.QUICEncryptionLevel, data []byte) error
- type ConnOption
- type DatagramResult
- type FrameHandler
- type HandshakeSink
- type Header
- type KeySet
- type Listener
- type LocalTransportParams
- type Opener
- type PacketConn
- type PacketKeys
- type PacketType
- type PeerClosedError
- type Sealer
- type ServerFlight
- type ServerTransportParams
- type Stream
- func (s *Stream) Finished() bool
- func (s *Stream) ID() uint64
- func (s *Stream) Recv() []byte
- func (s *Stream) RecvState() (finished, reset bool, code uint64)
- func (s *Stream) Reset(errCode uint64) error
- func (s *Stream) ResetCode() uint64
- func (s *Stream) ResetReceived() bool
- func (s *Stream) Send(data []byte, fin bool) (int, error)
- func (s *Stream) StopSending(errCode uint64) error
- func (s *Stream) WaitReadable(ctx context.Context) error
- func (s *Stream) WaitSendable(ctx context.Context) error
- type TLSHandshake
- type TransportParams
Constants ¶
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.
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).
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.
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.
const QUICVersion1 uint32 = 0x00000001
QUICVersion1 is the QUIC v1 version number (RFC 9000 §15).
Variables ¶
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.
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).
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).
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.
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.
var ErrCryptoKey = errors.New("quic: invalid packet-protection key")
ErrCryptoKey is returned when packet-protection keys are the wrong size.
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).
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.
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).
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).
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).
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.
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.
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).
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.
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.
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).
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).
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).
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.
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).
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).
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).
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.
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).
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).
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).
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 ¶
AppendAck appends an ACK frame (RFC 9000 §19.3). ranges are the additional ranges after First ACK Range, in wire order.
func AppendConnectionClose ¶
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 ¶
AppendCrypto appends a CRYPTO frame carrying data at the given stream offset.
func AppendDataBlocked ¶
AppendDataBlocked appends a DATA_BLOCKED frame.
func AppendHandshakeDone ¶
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 ¶
AppendMaxData appends a MAX_DATA frame.
func AppendMaxStreamData ¶
AppendMaxStreamData appends a MAX_STREAM_DATA frame.
func AppendMaxStreams ¶
AppendMaxStreams appends a MAX_STREAMS frame (bidirectional unless uni).
func AppendNewToken ¶
AppendNewToken appends a NEW_TOKEN frame.
func AppendPadding ¶
AppendPadding appends n PADDING frames (n zero bytes).
func AppendPathResponse ¶
AppendPathResponse appends a PATH_RESPONSE frame echoing an 8-byte challenge.
func AppendResetStream ¶
AppendResetStream appends a RESET_STREAM frame.
func AppendRetireConnectionID ¶
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 ¶
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 ¶
AppendStopSending appends a STOP_SENDING frame.
func AppendStream ¶
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 ¶
AppendStreamDataBlocked appends a STREAM_DATA_BLOCKED frame.
func AppendStreamsBlocked ¶
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
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
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
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
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 ¶
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 ¶
Close terminates the connection gracefully, sending a transport NO_ERROR CONNECTION_CLOSE (RFC 9000 §10.2) before closing the transport.
func (*Conn) CloseWithError ¶
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 ¶
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 ¶
HandshakeComplete marks the TLS handshake finished (HandshakeSink).
func (*Conn) OpenStream ¶
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
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 ¶
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 ¶
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 ¶
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 ¶
SetReadKeys installs the receive Opener for level (HandshakeSink).
func (*Conn) SetWriteKeys ¶
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 ¶
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 ¶
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 ¶
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
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
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) Close ¶ added in v0.10.0
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 ¶
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 ¶
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.
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 ¶
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) Recv ¶
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
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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
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
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.
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.
Source Files
¶
- ack_tracker.go
- bbr.go
- cc.go
- close.go
- conn.go
- conn_crypto.go
- conn_recv.go
- conn_seal.go
- connid.go
- crypto.go
- crypto_keyupdate.go
- crypto_protect.go
- doc.go
- errors.go
- frame.go
- frame_write.go
- gro.go
- gro_linux.go
- gso.go
- gso_linux.go
- handshake.go
- initial.go
- listener.go
- loss.go
- packet.go
- packet_write.go
- pto.go
- receive.go
- recvflow.go
- retry.go
- rtt.go
- send.go
- server.go
- stream.go
- transport_params.go