iyi

Standard library, 0.18.0

std/capsule

HTTP Datagrams and the Capsule Protocol — RFC 9297, RFC 8441, RFC 9220, RFC 9000.

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

std/capsuleiyi doc src/std/capsule.iyi
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
end

CapsuleProtocolstruct

std/capsuleiyi doc src/std/capsule.iyi
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
end

CapsuleReaderstruct

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.
std/capsuleiyi doc src/std/capsule.iyi
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)
end

CapsuleTypestruct

Standard and extended capsule types.

std/capsuleiyi doc src/std/capsule.iyi
pub struct CapsuleType
  DATAGRAM = 0_u64
  CLOSE_WEBTRANSPORT_SESSION = 10307_u64
  DRAIN_WEBTRANSPORT_SESSION = 30894_u64
  def initialize
end

ExtendedConnectstruct

std/capsuleiyi doc src/std/capsule.iyi
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
end

HttpDatagramstruct

std/capsuleiyi doc src/std/capsule.iyi
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
end

VarIntstruct

std/capsuleiyi doc src/std/capsule.iyi
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