std/capsule
import std/capsule to use it. Source: src/std/capsule.iyi at v0.18.0.
Answers the protocol architecture requirements:
1. QUIC Variable-Length Integer Encoding (RFC 9000 Section 16 & Appendix A):
A 2-bit-prefix variable-length integer encoding scheme supporting 1, 2, 4,
or 8 byte representations for values up to 2^62 - 1 (4,611,686,018,427,387,903).
Exported as `VarInt` so wave two's HTTP/3 and QUIC implementations can reuse
this exact encoding rather than writing a second one.
Supports canonical minimum-byte encoding, explicit-length multi-byte encoding
(as tested in RFC 9000 Appendix A), and strict decoding that rejects overlong
encodings and reserved/out-of-range values.2. HTTP Datagrams (RFC 9297 Section 2):
Quarter Stream IDs map datagrams to HTTP/3 request streams:
quarter_stream_id = stream_id // 4
stream_id = quarter_stream_id * 4
A stream ID is at most 2^62 - 1, so a Quarter Stream ID is at most
2^60 - 1 (Section 2.1); a larger one is refused, not carried.
Provides `HttpDatagram` with encoding and decoding over byte slices.3. The Capsule Protocol (RFC 9297 Section 3):
Framing for datagrams and control messages on reliable streams.
Each capsule consists of:
Capsule Type (i)
Capsule Length (i)
Capsule Value (..)
Follows RFC 9297 Section 3.2 extensibility rule: unknown capsule types
MUST be skipped rather than erroring.
Refuses truncated capsules, lengths that overrun buffer boundaries,
and invalid varint encodings.4. Extended CONNECT (RFC 8441 and RFC 9220):
Protocol negotiation via `:protocol` pseudo-header and the
SETTINGS_ENABLE_CONNECT_PROTOCOL (0x08) setting for HTTP/2 and HTTP/3.5. Capsule-Protocol Header (RFC 9297 Section 3.3):
Item Structured Field `Capsule-Protocol: ?1` enabling capsule framing.Capsulestruct
pub struct Capsule
def capsule_type : UInt64
def close_webtransport_session? : Bool
def datagram? : Bool
# Decodes a single capsule from bytes starting at offset, raising on error.
def self.decode(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(Capsule, Int32)
# Decodes a single capsule from bytes starting at offset.
# Returns {Capsule, total_bytes_consumed}.
# Refuses truncated capsules or lengths that overrun the buffer.
# Decodes a single capsule, returning nil if truncated or length overruns buffer.
def self.decode?(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(Capsule, Int32) | ::Nil
def drain_webtransport_session? : Bool
# Encodes the capsule to stream wire format:
# Capsule Type (i)
# Capsule Length (i)
# Capsule Value (..)
def encode : Bytes
def initialize(capsule_type : UInt64, value : Bytes)
# Returns true if this capsule type is unrecognized by base specifications.
def unknown? : Bool
def value : Bytes
endCapsuleProtocolstruct
pub struct CapsuleProtocol
HEADER_NAME = "Capsule-Protocol"
HEADER_VALUE = "?1"
def self.header_name : String
def self.header_value : String
def initialize
# True when the value, parsed as an Item Structured Field (RFC 8941
# Section 4.2), is the Boolean true. Its parameters are parsed and then
# ignored, as RFC 9297 Section 3.4 requires of unknown ones; a value that
# does not parse as an Item is as if the field were absent.
def self.is_capsule_protocol_header?(value : String) : Bool
# Checks if a header key-value pair enables the Capsule Protocol.
def self.valid_header?(name : String, value : String) : Bool
endCapsuleReaderstruct
Capsule stream processing enforcing RFC 9297 Section 3.2 rules:
- Unknown capsule types are skipped rather than erroring (extensibility).
- Truncated capsules and length overruns are strictly refused.
pub struct CapsuleReader
def initialize
# Decodes all capsules in buffer into an array.
# Preserves unknown capsules in the list for inspection.
def self.read_all(bytes : Bytes, strict : Bool = false) : Array(Capsule)
# Decodes capsules from stream buffer, actively skipping unknown types
# and returning only recognized capsules, demonstrating RFC 9297 extensibility.
def self.read_known(bytes : Bytes, strict : Bool = false) : Array(Capsule)
# Decodes capsules and reports both known capsules and the count of skipped unknown types.
def self.read_with_skip_count(bytes : Bytes, strict : Bool = false) : ::Tuple(Array(Capsule), Int32)
endCapsuleTypestruct
Standard and extended capsule types.
pub struct CapsuleType
DATAGRAM = 0_u64
CLOSE_WEBTRANSPORT_SESSION = 10307_u64
DRAIN_WEBTRANSPORT_SESSION = 30894_u64
def initialize
endExtendedConnectstruct
pub struct ExtendedConnect
SETTINGS_ENABLE_CONNECT_PROTOCOL = 8_u64
HEADER_METHOD = ":method"
HEADER_PROTOCOL = ":protocol"
HEADER_SCHEME = ":scheme"
HEADER_AUTHORITY = ":authority"
HEADER_PATH = ":path"
HEADER_STATUS = ":status"
METHOD_CONNECT = "CONNECT"
PROTOCOL_WEBTRANSPORT = "webtransport"
PROTOCOL_WEBSOCKET = "websocket"
def initialize
def self.settings_id : UInt64
# Validates whether the given parameters form a valid Extended CONNECT request.
def self.valid_request?(method : String, protocol : String | ::Nil, scheme : String | ::Nil, authority : String | ::Nil, path : String | ::Nil) : Bool
# Validates whether a response status code establishes the Extended CONNECT tunnel (2xx).
def self.valid_response?(status : Int32) : Bool
endHttpDatagramstruct
pub struct HttpDatagram
MAX_QUARTER_STREAM_ID = 1152921504606846975_u64
# Decodes an HTTP Datagram from a byte slice, raising on truncation or on
# a Quarter Stream ID past 2^60 - 1.
# Overlong quarter-stream IDs in strict mode raise the varint overlong
# sentence rather than pretending the buffer was truncated.
def self.decode(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(HttpDatagram, Int32)
# Decodes an HTTP Datagram from a byte slice.
# Decodes an HTTP Datagram, returning nil if truncated or if the Quarter
# Stream ID is past 2^60 - 1, which RFC 9297 Section 2.1 makes an error.
def self.decode?(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(HttpDatagram, Int32) | ::Nil
# Encodes this datagram into the wire format:
# Quarter Stream ID (i)
# HTTP Datagram Payload (..)
def encode : Bytes
# Creates an HTTP Datagram associated with the given HTTP/3 stream ID,
# which is a client-initiated bidirectional stream: RFC 9297 Section 2.1
# associates a datagram with no other kind, and that is why the division
# by four always comes out even. Any other stream ID is refused. It was
# divided down instead, so `from_stream_id(19_u64, payload)` was a
# datagram for stream 16 - another request's - and `stream_id` answered
# 16. A stream ID past 2^62 - 1 is left to `new`, whose sentence names
# the quarter it would be.
def self.from_stream_id(stream_id : UInt64, payload : Bytes) : self
def initialize(quarter_stream_id : UInt64, payload : Bytes)
def payload : Bytes
def quarter_stream_id : UInt64
# Maps the quarter stream ID back to the associated HTTP/3 stream ID.
# Stream ID = Quarter Stream ID * 4.
def stream_id : UInt64
endVarIntstruct
pub struct VarInt
MAX = 4611686018427387903_u64
MAX_1BYTE = 63_u64
MAX_2BYTE = 16383_u64
MAX_4BYTE = 1073741823_u64
MAX_8BYTE = 4611686018427387903_u64
# Decodes a QUIC variable-length integer from *bytes* starting at *offset*.
# Raises on truncated buffers, reserved values, or overlong encodings (if strict).
def self.decode(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(UInt64, Int32)
# Decodes a QUIC variable-length integer, returning nil if truncated, overlong, or reserved.
def self.decode?(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(UInt64, Int32) | ::Nil
# Strict decoding shorthand: refuses overlong and reserved encodings.
def self.decode_strict(bytes : Bytes, offset : Int32 = 0) : ::Tuple(UInt64, Int32)
def self.decode_strict?(bytes : Bytes, offset : Int32 = 0) : ::Tuple(UInt64, Int32) | ::Nil
# Encodes value using an explicit length (1, 2, 4, or 8 bytes).
# Used to produce canonical encodings or explicit multi-byte encodings as
# specified in RFC 9000 Appendix A (e.g. 37 encoded in 2, 4, or 8 bytes).
def self.encode(value : UInt64, length : Int32) : Bytes
# Encodes value into its canonical (minimum-length) QUIC varint representation.
def self.encode(value : UInt64) : Bytes
# Calculates the minimum number of bytes required to canonically encode value.
def self.encoded_size(value : UInt64) : Int32
def initialize
# Checks if value can be represented as a 62-bit QUIC varint.
def self.valid_value?(value : UInt64) : Bool
end
HTTP Datagrams and the Capsule Protocol — RFC 9297, RFC 8441, RFC 9220, RFC 9000.