UNPKG

rsocket-frames-ts

Version:

RSocket frame codec implemented with Uint8Array. Supports full spec, metadata extensions, and browser compatibility.

1 lines 185 kB
{"version":3,"sources":["../src/frame/FrameType.ts","../src/frame/FrameWriter.ts","../src/frame/context/Header.ts","../src/frame/context/Metadata.ts","../src/frame/context/Payload.ts","../src/frame/FrameFlag.ts","../src/frame/Frame.ts","../src/frame/FrameDeserializer.ts","../src/frame/wellknown/ReservedFrame.ts","../src/utils/index.ts","../src/mimetype/MimeType.ts","../src/frame/wellknown/SetupFrame.ts","../src/frame/wellknown/LeaseFrame.ts","../src/frame/wellknown/KeepaliveFrame.ts","../src/frame/wellknown/RequestResponseFrame.ts","../src/frame/wellknown/RequestFireAndForgetFrame.ts","../src/frame/wellknown/RequestStreamFrame.ts","../src/frame/wellknown/RequestChannelFrame.ts","../src/frame/wellknown/RequestNFrame.ts","../src/frame/wellknown/CancelFrame.ts","../src/frame/wellknown/PayloadFrame.ts","../src/frame/FrameErrorCode.ts","../src/frame/wellknown/ErrorFrame.ts","../src/frame/wellknown/MetadataPushFrame.ts","../src/frame/wellknown/ResumeFrame.ts","../src/frame/wellknown/ResumeOkFrame.ts","../src/frame/wellknown/ExtensionFrame.ts","../src/mimetype/message/RSocketMimeType.ts","../src/mimetype/message/RSocketRouting.ts","../src/mimetype/message/RSocketComposite.ts","../src/mimetype/message/RSocketTracingZipkin.ts","../src/mimetype/message/security/RSocketAuth.ts","../src/mimetype/message/security/AuthType.ts","../src/mimetype/message/security/SimpleAuthType.ts","../src/mimetype/message/security/BearerAuthType.ts","../src/mimetype/message/security/index.ts","../src/mimetype/index.ts"],"sourcesContent":["/**\n * Enumeration of all standard RSocket frame types.\n *\n * These frame types define the kind of protocol message being sent\n * over the RSocket connection. Each frame has a unique identifier byte\n * used for decoding and routing.\n */\nexport enum FrameType {\n /** `0x00` - Reserved for future use. */\n RESERVED = 0x00,\n /** `0x01` - Sent by the client to initiate the connection and negotiate setup parameters. */\n SETUP = 0x01,\n /** `0x02` - Sent by the responder to grant the requester permission to send requests. */\n LEASE = 0x02,\n /** `0x03` - Used to maintain liveness of the connection. */\n KEEPALIVE = 0x03,\n /** `0x04` - Request-Response interaction model (1 request, 1 response). */\n REQUEST_RESPONSE = 0x04,\n /** `0x05` - Fire-and-Forget: A one-way message with no response. */\n REQUEST_FNF = 0x05,\n /** `0x06` - Request a stream of responses (possibly infinite). */\n REQUEST_STREAM = 0x06,\n /** `0x07` - Bi-directional stream of messages between requester and responder. */\n REQUEST_CHANNEL = 0x07,\n /** `0x08` - Request N more items in a stream (backpressure mechanism). */\n REQUEST_N = 0x08,\n /** `0x09` - Cancel an ongoing request. */\n CANCEL = 0x09,\n /** `0x0A` - Used to transmit a payload on a stream. */\n PAYLOAD = 0x0A,\n /** `0x0B` - Represents an application or connection-level error. */\n ERROR = 0x0B,\n /** `0x0C` - Pushes metadata out-of-band to the peer. */\n METADATA_PUSH = 0x0C,\n /** `0x0D` - Sent to resume a connection (if supported). */\n RESUME = 0x0D,\n /** `0x0E` - Acknowledges a successful resume. */\n RESUME_OK = 0x0E,\n /** `0x3F` - Reserved for protocol extensions. */\n EXT = 0x3F\n}\n\nexport namespace FrameType {\n /**\n * Attempts to determine the `FrameType` based on the given byte value.\n *\n * This performs a best-match lookup by applying bitmask matching\n * in reverse order of definition, favoring the most specific match.\n *\n * @param {number} byte - The raw frame type byte value.\n * @returns {FrameType} The corresponding `FrameType` enum value.\n */\n export function fromByte(byte: number): FrameType {\n return Array.from(Object.entries(FrameType))\n .filter(([key, _]) => Number.isNaN(Number(key)))\n .filter(([_, value]) => (byte & value as number) == value)\n .map(([_, value]) => value)\n .reverse()\n .shift() as FrameType\n }\n}","import {ByteWriter} from \"bebyte\";\n\n/**\n * Abstract base class for writing frame content to a binary stream.\n *\n * Subclasses must implement the `write` method to serialize frame-specific\n * data using the provided `ByteWriter`.\n *\n * ### Example Usage:\n * ```ts\n * class SetupFrameWriter extends FrameWriter {\n * protected write(writer: ByteWriter): void {\n * writer.i32(this.version);\n * writer.write(this.payload);\n * }\n * }\n * ```\n */\nexport abstract class FrameWriter {\n /**\n * Writes the frame content to the provided `ByteWriter`.\n * This method must be implemented by subclasses to define\n * the serialization logic for the frame.\n *\n * @param {ByteWriter} writer - The writer used to output binary data.\n */\n protected abstract write(writer: ByteWriter): void;\n}","import {FrameType} from \"@/frame/FrameType\";\nimport {ByteReader, ByteWriter} from \"bebyte\";\nimport {FrameFlag} from \"@/frame/FrameFlag\";\nimport {FrameWriter} from \"@/frame/FrameWriter\";\n\n/**\n * ### Frame Header Format\n *\n * RSocket frames begin with a RSocket Frame Header. The general layout is given below.\n *\n * ```\n * 0 1 2 3\n * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n * |0| Stream ID |\n * +-----------+-+-+---------------+-------------------------------+\n * |Frame Type |I|M| Flags | Depends on Frame Type ...\n * +-------------------------------+\n * ```\n *\n * * __Stream ID__: (31 bits = max value 2^31-1 = 2,147,483,647) Unsigned 31-bit integer representing the stream Identifier for this frame or 0 to indicate the entire connection.\n * * Transport protocols that include demultiplexing, such as HTTP/2, MAY omit the Stream ID field if all parties agree. The means of negotiation and agreement is left to the transport protocol.\n * * __Frame Type__: (6 bits = max value 63) Type of Frame.\n * * [__Flags__: (10 bits)]{@link FrameFlag} Any Flag bit not specifically indicated in the frame type should be set to 0 when sent and not interpreted on\n * reception. Flags generally depend on Frame Type, but all frame types MUST provide space for the following flags:\n * * (__I__)gnore: Ignore frame if not understood\n * * (__M__)etadata: Metadata present\n *\n * @see [Official documentation]{@link https://github.com/rsocket/rsocket/blob/master/Protocol.md#frame-header-format}\n */\nexport class Header extends FrameWriter {\n /**\n * Creates a new RSocket `Header` instance.\n *\n * @param {FrameType} frameType - Type of the frame (6 bits).\n * @param {number} streamId - Stream ID this frame is associated with (must be a 31-bit unsigned int).\n * @param {FrameFlag} flags - Bitmask of frame flags (10 bits max).\n */\n constructor(\n public readonly frameType: FrameType, // (31 bits = max value 2^31-1 = 2,147,483,647) Unsigned 31-bit integer representing the stream Identifier for this frame or 0 to indicate the entire connection.\n public readonly streamId: number, // 6 bits = max value 63) Type of Frame.\n public readonly flags: FrameFlag // flags (10 bits) Any Flag bit not specifically indicated in the frame type should be set to 0 when sent and not interpreted on reception. Flags generally depend on Frame Type, but all frame types MUST provide space for the following flags\n ) {\n super()\n }\n\n /**\n * Deserializes a `Header` from the binary reader.\n *\n * @param {ByteReader} reader - Byte stream reader positioned at the start of the frame.\n * @returns {Header} Parsed frame header.\n */\n public static from(reader: ByteReader): Header {\n const streamId = reader.i32()\n const frameTypeAndFlagsByte = reader.i16()\n const frameType = FrameType.fromByte(frameTypeAndFlagsByte >> 10)\n const flags = frameTypeAndFlagsByte & 0x03FF\n return new Header(frameType, streamId, flags)\n }\n\n /**\n * Checks if a specific flag is set.\n *\n * @param {FrameFlag} flag - Flag to check.\n * @returns {boolean} True if the flag is set.\n */\n public isFlagSet(flag: FrameFlag): boolean {\n return (this.flags & flag) == flag\n }\n\n /**\n * Serializes this header to the binary writer.\n *\n * @param {ByteWriter} writer - Writer to serialize to.\n */\n public write(writer: ByteWriter): void {\n writer.i31(this.streamId) // streamId\n writer.i16(\n this.frameType << 10 |\n this.flags\n )\n }\n}","import {ByteWriter} from \"bebyte\";\nimport {MimeType} from \"@/mimetype\";\nimport {FrameWriter} from \"@/frame/FrameWriter\";\n\n/**\n * Represents a metadata frame in RSocket protocol.\n *\n * This class encapsulates metadata information, including its MIME type and payload,\n * and provides methods to serialize it into binary format for transmission.\n *\n * @template T - The type of the metadata payload, defaults to `Uint8Array`.\n */\nexport class Metadata<T = Uint8Array> extends FrameWriter {\n /**\n * Creates a new [Metadata]{@link Metadata} instance.\n *\n * @param mimeType - The MIME type describing the format of the metadata.\n * @param payload - The actual metadata payload.\n */\n public constructor(public readonly mimeType: MimeType<T>, public readonly payload: T) {\n super()\n }\n\n /**\n * Converts the metadata payload to a [Uint8Array]{@link Uint8Array}.\n *\n * @returns The metadata payload as a [Uint8Array]{@link Uint8Array}.\n */\n public toUint8Array(): Uint8Array {\n return this.payload as Uint8Array\n }\n\n /**\n * Serializes the metadata and writes it to the given [ByteWriter]{@link ByteWriter}.\n *\n * @param writer - The byte writer to which the metadata will be written.\n * @param hasPayload - Indicates whether to write the payload length prefix (defaults to `true`).\n */\n public write(writer: ByteWriter, hasPayload: boolean = true) {\n const array = this.toUint8Array()\n if (hasPayload) writer.i24(array.length)\n writer.write(array)\n }\n}","import {ByteWriter} from \"bebyte\";\nimport {MimeType} from \"@/mimetype\";\nimport {FrameWriter} from \"@/frame/FrameWriter\";\n\n/**\n * Represents a payload frame in the RSocket protocol.\n *\n * @template T - The type of the payload, defaults to `Uint8Array`.\n */\nexport class Payload<T = Uint8Array> extends FrameWriter {\n /**\n * Creates a new [Payload]{@link Payload} instance.\n *\n * @param mimeType - The MIME type indicating the format of the payload data.\n * @param payload - The binary payload content.\n */\n public constructor(public readonly mimeType: MimeType<T>, public readonly payload: T) {\n super()\n }\n\n /**\n * Converts the payload to a [Uint8Array]{@link Uint8Array}.\n *\n * @returns The payload data as a [Uint8Array]{@link Uint8Array}.\n */\n public toUint8Array(): Uint8Array {\n return this.payload as Uint8Array\n }\n\n /**\n * Serializes the payload and writes it to the given [ByteWriter]{@link ByteWriter}.\n *\n * @param writer - The byte writer to which the payload will be written.\n */\n public write(writer: ByteWriter) {\n writer.write(this.toUint8Array())\n }\n}","/**\n * Internal enum defining individual frame-level flags used in RSocket.\n * These flags modify frame behavior.\n */\nenum _FrameFlag {\n /**\n * No flags are set.\n */\n NONE = 0,\n /**\n * Indicates that the frame can be safely ignored if not understood.\n */\n IGNORE = 512,\n /**\n * Indicates that the frame contains metadata.\n */\n METADATA = 256\n}\n\n/**\n * RSocket frame flags, including utility to combine multiple flags.\n */\nexport const FrameFlag = Object.assign({\n /**\n * Combines multiple frame flags into a single numeric bitmask.\n *\n * @param flags List of frame flags to combine.\n * @returns Combined numeric flag.\n * @example\n * ```ts\n * const flags = FrameFlag.combine(FrameFlag.METADATA, FrameFlag.IGNORE);\n * ```\n */\n combine: (...flags: FrameFlag[]) => flags.reduce((prev, curr) => prev | curr, 0)\n}, _FrameFlag);\nexport type FrameFlag = number | _FrameFlag;\n\n/**\n * Internal enum for keepalive-specific flags.\n */\nenum _KeepaliveFlag {\n /**\n * If set, the receiver must respond with a KEEPALIVE frame.\n */\n RESPOND = 128\n}\n\n/**\n * Flags applicable to KEEPALIVE frames.\n */\nexport const KeepaliveFlag = Object.assign({}, _KeepaliveFlag, FrameFlag);\nexport type KeepaliveFlag = FrameFlag | _KeepaliveFlag;\n\n/**\n * Enum for custom [extension frame]{@link ExtensionFrame} flags.\n */\nenum _ExtensionFlag {\n EXT_1 = 128,\n EXT_2 = 64,\n EXT_3 = 32,\n EXT_4 = 16,\n EXT_5 = 8,\n EXT_6 = 4,\n EXT_7 = 2,\n EXT_8 = 1\n}\n\n/**\n * Flags applicable to EXT (extension) frames.\n */\nexport const ExtensionFlag = Object.assign({}, _ExtensionFlag, FrameFlag);\nexport type ExtensionFlag = FrameFlag | _ExtensionFlag;\n\n/**\n * Internal enum for setup frame flags.\n */\nenum _SetupFlag {\n /**\n * Enables session resumption support.\n */\n RESUME = 128,\n /**\n * Enables lease-based flow control.\n */\n LEASE = 64,\n}\n\n/**\n * Flags applicable to SETUP frames.\n */\nexport const SetupFlag = Object.assign({}, _SetupFlag, FrameFlag);\nexport type SetupFlag = FrameFlag | _SetupFlag;\n\n/**\n * Enum for the `FOLLOWS` flag, indicating that more fragments follow.\n */\nenum FollowsFlag {\n /**\n * Indicates that this frame is followed by more fragments.\n */\n FOLLOWS = 128\n}\n\n/**\n * Flags applicable to [FIRE_AND_FORGET]{@link RequestFireAndForgetFrame} frames.\n */\nexport const FireAndForgetFlag = Object.assign({}, FollowsFlag, FrameFlag);\nexport type FireAndForgetFlag = FrameFlag | FollowsFlag;\n\n/**\n * Flags applicable to [REQUEST_RESPONSE]{@link RequestResponseFrame} frames (same as [FIRE_AND_FORGET]{@link RequestFireAndForgetFrame}.\n */\nexport const RequestResponseFlag = FireAndForgetFlag;\nexport type RequestResponseFlag = FireAndForgetFlag\n\n/**\n * Flags applicable to [REQUEST_STREAM]{@link RequestStreamFrame} frames (same as [REQUEST_RESPONSE]{@link RequestResponseFrame}).\n */\nexport const RequestStreamFlag = RequestResponseFlag;\nexport type RequestStreamFlag = RequestResponseFlag;\n\n/**\n * Enum representing the `COMPLETE` flag, indicating stream completion.\n */\nenum CompleteFlag {\n COMPLETE = 64\n}\n\n/**\n * Enum for payload-specific flags.\n */\nenum _PayloadFlag {\n /**\n * Indicates the presence of the next payload fragment.\n */\n NEXT = 32\n}\n\n/**\n * Flags applicable to [REQUEST_CHANNEL]{@link RequestChannelFrame} frames.\n */\nexport const RequestChannelFlag = Object.assign({}, FollowsFlag, CompleteFlag, FrameFlag);\nexport type RequestChannelFlag = FrameFlag | FollowsFlag | CompleteFlag;\n\n/**\n * Flags applicable to [PAYLOAD]{@link PayloadFrame} frames.\n */\nexport const PayloadFlag = Object.assign({}, _PayloadFlag, RequestChannelFlag);\nexport type PayloadFlag = _PayloadFlag | RequestChannelFlag;","import bebyte from \"bebyte\";\nimport {Header} from \"@/frame/context/Header\";\nimport {Payload} from \"@/frame/context/Payload\";\nimport {FrameType} from \"@/frame/FrameType\";\nimport {FrameFlag} from \"@/frame/FrameFlag\";\nimport {FrameWriter} from \"@/frame/FrameWriter\";\nimport {Metadata} from \"@/frame/context/Metadata\";\n\n/**\n * Abstract base class representing an RSocket frame.\n *\n * All specific frame types (e.g., `SetupFrame`, `RequestFrame`, etc.)\n * must extend this class to implement their frame-specific logic.\n *\n * Each frame has:\n * - A header that includes frame type, stream ID, and flags.\n * - Optional metadata and payload sections.\n *\n * This class also implements encoding logic to convert a frame into a binary buffer.\n */\nexport abstract class Frame extends FrameWriter {\n /**\n * The internal frame header (type, flags, stream ID).\n * @protected\n */\n public readonly header: Header\n\n /**\n * Constructs a new frame instance.\n *\n * @param {FrameType} type - The RSocket frame type (e.g. SETUP, REQUEST_RESPONSE).\n * @param {number} streamId - The stream identifier associated with the frame.\n * @param {FrameFlag} [flags=FrameFlag.NONE] - Initial frame flags.\n * @param {Metadata<any>} [metadata] - Optional metadata section.\n * @param {Payload<any>} [payload] - Optional payload section.\n */\n protected constructor(\n type: FrameType,\n streamId: number,\n flags: FrameFlag = FrameFlag.NONE,\n public readonly metadata?: Metadata<any>,\n public readonly payload?: Payload<any>\n ) {\n super()\n this.header = new Header(\n type,\n streamId,\n FrameFlag.combine(flags, this.metadata != null ? FrameFlag.METADATA : FrameFlag.NONE)\n )\n }\n\n /**\n * Gets the frame type.\n *\n * @returns {FrameType} The frame type value.\n */\n public get type() {\n return this.header.frameType\n }\n\n /**\n * Checks if a specific flag is set on the frame.\n *\n * @param {FrameFlag} flag - The flag to check.\n * @returns {boolean} `true` if the flag is set, otherwise `false`.\n */\n public isFlagSet(flag: FrameFlag): boolean {\n return this.header.isFlagSet(flag)\n }\n\n /**\n * Indicates whether the frame can be safely ignored by the peer.\n * Relies on the `IGNORE` flag being set.\n *\n * @returns {boolean} `true` if frame has IGNORE flag, otherwise `false`.\n */\n public canBeIgnored(): boolean {\n return this.isFlagSet(FrameFlag.IGNORE)\n }\n\n /**\n * Indicates whether the frame contains metadata.\n * Relies on the `METADATA` flag being set.\n *\n * @returns {boolean} `true` if the METADATA flag is set, otherwise `false`.\n */\n public hasMetadata(): boolean {\n return this.isFlagSet(FrameFlag.METADATA)\n }\n /**\n * Serializes the frame into a `Uint8Array` for transmission.\n *\n * Frame is written as:\n * - Header\n * - Frame-specific body (`write()` method implemented in subclass)\n * - Metadata (if present)\n * - Payload (if present)\n *\n * @returns {Uint8Array} Serialized binary representation of the frame.\n *\n * @remarks\n * Implementations may impose frame size limits (e.g. 65535 bytes in Java).\n * This method does **not** enforce length limits; check before sending.\n */\n public toUint8Array(): Uint8Array {\n const writer = bebyte.writer()\n this.header.write(writer)\n this.write(writer)\n this.metadata?.write?.(writer, this.type != FrameType.LEASE && this.type != FrameType.METADATA_PUSH)\n this.payload?.write?.(writer)\n // TODO в каждой реализации может быть разный frame length limit, в java например это 65535, нужно проверить длину перед отправкой\n return writer.toUint8Array()\n }\n}","import bebyte from \"bebyte\";\nimport {Frame} from \"@/frame/Frame\";\nimport {FrameType} from \"@/frame/FrameType\";\nimport {ReservedFrame} from \"@/frame/wellknown/ReservedFrame\";\nimport {SetupFrame} from \"@/frame/wellknown/SetupFrame\";\nimport {LeaseFrame} from \"@/frame/wellknown/LeaseFrame\";\nimport {KeepaliveFrame} from \"@/frame/wellknown/KeepaliveFrame\";\nimport {RequestResponseFrame} from \"@/frame/wellknown/RequestResponseFrame\";\nimport {RequestFireAndForgetFrame} from \"@/frame/wellknown/RequestFireAndForgetFrame\";\nimport {RequestStreamFrame} from \"@/frame/wellknown/RequestStreamFrame\";\nimport {RequestChannelFrame} from \"@/frame/wellknown/RequestChannelFrame\";\nimport {RequestNFrame} from \"@/frame/wellknown/RequestNFrame\";\nimport {CancelFrame} from \"@/frame/wellknown/CancelFrame\";\nimport {PayloadFrame} from \"@/frame/wellknown/PayloadFrame\";\nimport {ErrorFrame} from \"@/frame/wellknown/ErrorFrame\";\nimport {MetadataPushFrame} from \"@/frame/wellknown/MetadataPushFrame\";\nimport {ResumeFrame} from \"@/frame/wellknown/ResumeFrame\";\nimport {ResumeOkFrame} from \"@/frame/wellknown/ResumeOkFrame\";\nimport {ExtensionFrame} from \"@/frame/wellknown/ExtensionFrame\";\nimport {Header} from \"@/frame/context/Header\";\nimport {MimeType} from \"@/mimetype/MimeType\";\n\n/**\n * Deserializes a raw RSocket frame buffer into a strongly typed `Frame` object.\n *\n * This function reads the header to determine the `FrameType`, then delegates\n * to the appropriate frame-specific parser.\n *\n * @param {Uint8Array} buffer - The raw frame buffer to deserialize.\n * @param {MimeType<any>} metadataType - The metadata MIME type to use during decoding.\n * @param {MimeType<any>} payloadType - The payload MIME type to use during decoding.\n * @returns {Frame} A fully deserialized RSocket frame.\n * @throws {Error} If the frame type is unknown or unsupported.\n */\nfunction deserialize(buffer: Uint8Array, metadataType: MimeType<any>, payloadType: MimeType<any>): Frame {\n const reader = bebyte.reader(buffer)\n const header = Header.from(reader)\n switch (header.frameType) {\n case FrameType.RESERVED:\n return ReservedFrame.from(header, reader, metadataType, payloadType)\n case FrameType.SETUP:\n return SetupFrame.from(header, reader, metadataType, payloadType)\n case FrameType.LEASE:\n return LeaseFrame.from(header, reader, metadataType, payloadType)\n case FrameType.KEEPALIVE:\n return KeepaliveFrame.from(header, reader, metadataType, payloadType)\n case FrameType.REQUEST_RESPONSE:\n return RequestResponseFrame.from(header, reader, metadataType, payloadType)\n case FrameType.REQUEST_FNF:\n return RequestFireAndForgetFrame.from(header, reader, metadataType, payloadType)\n case FrameType.REQUEST_STREAM:\n return RequestStreamFrame.from(header, reader, metadataType, payloadType)\n case FrameType.REQUEST_CHANNEL:\n return RequestChannelFrame.from(header, reader, metadataType, payloadType)\n case FrameType.REQUEST_N:\n return RequestNFrame.from(header, reader, metadataType, payloadType)\n case FrameType.CANCEL:\n return CancelFrame.from(header, reader, metadataType, payloadType)\n case FrameType.PAYLOAD:\n return PayloadFrame.from(header, reader, metadataType, payloadType)\n case FrameType.ERROR:\n return ErrorFrame.from(header, reader, metadataType, payloadType)\n case FrameType.METADATA_PUSH:\n return MetadataPushFrame.from(header, reader, metadataType, payloadType)\n case FrameType.RESUME:\n return ResumeFrame.from(header, reader, metadataType, payloadType)\n case FrameType.RESUME_OK:\n return ResumeOkFrame.from(header, reader, metadataType, payloadType)\n case FrameType.EXT:\n return ExtensionFrame.from(header, reader, metadataType, payloadType)\n default:\n throw new Error('Unknown frame type')\n }\n}\n\n/**\n * A utility for deserializing raw RSocket frames from binary format.\n *\n * Adds a `toUint8Array()` method to the result for potential re-serialization\n * or caching of original bytes.\n */\nexport const FrameDeserializer = {\n /**\n * Deserializes the given buffer into a `Frame` and attaches `toUint8Array()`\n * that returns the original input buffer.\n *\n * @param {Uint8Array} buffer - The raw frame bytes to deserialize.\n * @param {MimeType<any>} metadataType - MIME type for metadata decoding.\n * @param {MimeType<any>} payloadType - MIME type for payload decoding.\n * @returns {Frame} Deserialized frame with `toUint8Array()` method.\n */\n deserialize: (buffer: Uint8Array, metadataType: MimeType<any>, payloadType: MimeType<any>): Frame => {\n return Object.assign(deserialize(buffer, metadataType, payloadType), {\n toUint8Array: () => buffer\n })\n }\n}","import {ByteReader, ByteWriter} from \"bebyte\";\nimport {Frame} from \"@/frame/Frame\";\nimport {FrameType} from \"@/frame/FrameType\";\nimport {Header} from \"@/frame/context/Header\";\nimport {FrameFlag} from \"@/frame/FrameFlag\";\nimport {MimeType} from \"@/mimetype/MimeType\";\n\n/**\n * Represents a `RESERVED` frame (`FrameType.RESERVED`, 0x00).\n *\n * This frame type is **not valid for use** and is reserved for future extension\n * or protocol evolution. Any attempt to construct or transmit such a frame\n * should be treated as a protocol violation.\n *\n * The class exists solely to support deserialization in edge cases\n * (e.g., for debugging, protocol fuzzing, or error reporting).\n *\n * ### Important:\n * - **Must not be constructed** at runtime intentionally.\n * - Always uses `FrameFlag.IGNORE`.\n *\n * @throws {Error} Always throws on construction to prevent misuse.\n *\n * @see [RSocket Protocol - Frame Types](https://github.com/rsocket/rsocket/blob/master/Protocol.md#frame-types)\n */\nexport class ReservedFrame extends Frame {\n /**\n * Constructs a `ReservedFrame`. Throws immediately.\n *\n * @param {number} streamId - The stream ID associated with the frame.\n * @throws {Error} Always throws to prevent use of reserved frames.\n */\n public constructor(streamId: number) {\n super(FrameType.RESERVED, streamId, FrameFlag.IGNORE)\n throw new Error(\"Reserved frame could not be created!\")\n }\n\n /**\n * Deserializes a `ReservedFrame` from a stream.\n *\n * This method exists only to allow the parser to safely return\n * a `ReservedFrame` when the frame type byte equals `0x00`.\n *\n * @param {Header} header - Frame header.\n * @param {ByteReader} _ - Unused.\n * @param {MimeType} __ - Unused.\n * @param {MimeType} ___ - Unused.\n * @returns {ReservedFrame} A reserved frame instance (throws).\n * @throws {Error} Always throws on instantiation.\n */\n public static from(header: Header, _: ByteReader, __: MimeType, ___: MimeType): ReservedFrame {\n return new ReservedFrame(header.streamId)\n }\n\n /**\n * Reserved frame has no body and should never be written.\n *\n * @param {ByteWriter} _ - Unused.\n */\n protected write(_: ByteWriter) {\n // No-op: reserved frames should never be serialized.\n }\n}","/**\n * A `TextEncoder` instance used to convert strings to UTF-8 encoded `Uint8Array`.\n * Defaults to UTF-8 encoding.\n * @const\n */\nconst encoder = new TextEncoder()\n/**\n * A `TextDecoder` instance used to convert UTF-8 encoded `Uint8Array` back to strings.\n * Defaults to UTF-8 decoding.\n * @const\n */\nconst decoder = new TextDecoder()\n\n/**\n * Encodes a string into a UTF-8 `Uint8Array`.\n * If the value is `undefined`, the string `\"undefined\"` will be encoded.\n *\n * @param {string} [value] - The string to encode.\n * @returns {Uint8Array} The encoded byte array.\n */\nexport function encode(value?: string): Uint8Array {\n return encoder.encode(value)\n}\n\n/**\n * Decodes a UTF-8 `Uint8Array` back into a string.\n * If `value` is `undefined`, this will throw a `TypeError`.\n *\n * @param {Uint8Array} [value] - The byte array to decode.\n * @returns {string} The decoded string.\n */\nexport function decode(value?: Uint8Array): string {\n return decoder.decode(value)\n}","import bebyte, {ByteReader} from \"bebyte\";\nimport {Metadata} from \"@/frame/context/Metadata\";\nimport {Payload} from \"@/frame/context/Payload\";\n\n/**\n * Represents a MIME type and provides serialization/deserialization\n * logic for metadata and payloads associated with that type.\n *\n * @template T The payload type, defaults to `Uint8Array`.\n */\nexport class MimeType<T = Uint8Array> {\n /**\n * Internal registry of all known MIME types.\n * Maps MIME type strings to their corresponding `MimeType` instances.\n * @private\n */\n private static _values: Map<string, MimeType<any>> = new Map();\n\n /**\n * Creates a new `MimeType` instance and registers it in the internal map.\n *\n * @param {string} mimeType - The MIME type string (e.g. \"application/json\").\n * @param {number} [identifier] - Optional numeric identifier for well-known types.\n */\n public constructor(public readonly mimeType: string, public readonly identifier?: number) {\n MimeType._values.set(mimeType, this)\n }\n\n /**\n * Indicates whether the MIME type is well-known (has an associated identifier).\n *\n * @returns {boolean} `true` if the type has an identifier, otherwise `false`.\n */\n public get isWellKnown() {\n return this.identifier != null\n }\n\n /**\n * Serializes the given payload into a `Metadata` object.\n *\n * @param {T} payload - The payload to wrap.\n * @returns {Metadata<T>} The resulting `Metadata` instance.\n * @protected\n */\n protected serializeMetadata(payload: T): Metadata<T> {\n return new Metadata(this, payload)\n }\n\n /**\n * Deserializes a `Metadata` object from a `ByteReader` stream.\n *\n * @param {ByteReader} payload - The byte stream to read from.\n * @param {boolean} [hasPayload=true] - Whether a length-prefixed payload is expected.\n * @returns {Metadata<T>} The deserialized metadata.\n * @protected\n */\n protected deserializeMetadata(payload: ByteReader, hasPayload: boolean = true): Metadata<T> {\n return new Metadata(this, (hasPayload ? payload.read(payload.i24()) : payload.readRemaining()) as T)\n }\n\n /**\n * Converts a payload or byte stream into a `Metadata` object.\n * Automatically chooses between serialization and deserialization based on input type.\n *\n * @param {ByteReader | T} payload - Either raw data or a reader to deserialize from.\n * @param {boolean} [hasPayload=true] - Indicates if the reader contains a length-prefixed payload.\n * @returns {Metadata<T>} A `Metadata` instance.\n */\n public toMetadata(payload: ByteReader | T, hasPayload: boolean = true): Metadata<T> {\n if(payload instanceof Uint8Array) return this.deserializeMetadata(bebyte.reader(payload), hasPayload)\n if (typeof (payload as ByteReader)['i8'] == 'function') return this.deserializeMetadata(payload as ByteReader, hasPayload)\n return this.serializeMetadata(payload as T)\n }\n\n /**\n * Serializes the given payload into a `Payload` object.\n *\n * @param {T} payload - The payload to wrap.\n * @returns {Payload<T>} The resulting `Payload` instance.\n * @protected\n */\n protected serializePayload(payload: T): Payload<T> {\n return new Payload(this, payload)\n }\n\n /**\n * Deserializes a `Payload` object from a `ByteReader` stream.\n *\n * @param {ByteReader} payload - The byte stream to read from.\n * @returns {Payload<T>} The deserialized payload.\n * @protected\n */\n protected deserializePayload(payload: ByteReader): Payload<T> {\n return new Payload(this, payload.readRemaining() as T)\n }\n\n /**\n * Converts a payload or byte stream into a `Payload` object.\n * Automatically chooses between serialization and deserialization based on input type.\n *\n * @param {ByteReader | T} payload - Either raw data or a reader to deserialize from.\n * @returns {Payload<T>} A `Payload` instance.\n */\n public toPayload(payload: ByteReader | T): Payload<T> {\n if(payload instanceof Uint8Array) return this.deserializePayload(bebyte.reader(payload))\n if (typeof (payload as ByteReader)['i8'] == 'function') return this.deserializePayload(payload as ByteReader)\n return this.serializePayload(payload as T)\n }\n\n /**\n * Retrieves a registered `MimeType` by string or identifier.\n * If no match is found, returns a generic `UnknownMimeType` instance and logs a warning.\n *\n * @param {string | number} mimeType - MIME type string or numeric identifier.\n * @returns {MimeType} A matching or unknown `MimeType` instance.\n */\n public static valueOf(mimeType: string | number): MimeType {\n return Array.from(MimeType._values.values())\n .find(v => mimeType == (typeof mimeType == \"string\" ? v.mimeType : v.identifier)) ||\n new class UnknownMimeType extends MimeType {\n public constructor(mimeType: string) {\n super(mimeType);\n console.warn(`An unknown MimeType#${mimeType} has been detected. Please register it before using it`)\n }\n }(String(mimeType));\n }\n}","import {ByteReader, ByteWriter} from \"bebyte\";\nimport {decode, encode} from \"@/utils\";\nimport {Frame} from \"@/frame/Frame\";\nimport {Payload} from \"@/frame/context/Payload\";\nimport {FrameType} from \"@/frame/FrameType\";\nimport {MimeType} from \"@/mimetype/MimeType\";\nimport {SetupFlag} from \"@/frame/FrameFlag\";\nimport {Header} from \"@/frame/context/Header\";\nimport {Metadata} from \"@/frame/context/Metadata\";\n\n/**\n * ### SETUP Frame (0x01)\n *\n * Setup frames MUST always use Stream ID 0 as they pertain to the connection.\n *\n * The SETUP frame is sent by the client to inform the server of the parameters under which it desires\n * to operate. The usage and message sequence used is shown in [Connection Establishment]{@link https://github.com/rsocket/rsocket/blob/master/Protocol.md#connection-establishment}.\n *\n * One of the important parameters for a connection is the format, layout, and any schema of the data and metadata for\n * frames. This is, for lack of a better term, referred to here as \"MIME Type\". An implementation MAY use typical MIME type\n * values or MAY decide to use specific non-MIME type values to indicate format, layout, and any schema\n * for data and metadata. The protocol implementation MUST NOT interpret the MIME type itself. This is an application\n * concern only.\n *\n * The encoding format for Data and Metadata are included separately in the SETUP.\n *\n * Frame Contents\n *\n * ```\n * 0 1 2 3\n * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n * |0| Stream ID = 0 |\n * +-----------+-+-+-+-+-----------+-------------------------------+\n * |Frame Type |0|M|R|L| Flags |\n * +-----------+-+-+-+-+-----------+-------------------------------+\n * | Major Version | Minor Version |\n * +-------------------------------+-------------------------------+\n * |0| Time Between KEEPALIVE Frames |\n * +---------------------------------------------------------------+\n * |0| Max Lifetime |\n * +---------------------------------------------------------------+\n * | Token Length | Resume Identification Token ...\n * +---------------+-----------------------------------------------+\n * | MIME Length | Metadata Encoding MIME Type ...\n * +---------------+-----------------------------------------------+\n * | MIME Length | Data Encoding MIME Type ...\n * +---------------+-----------------------------------------------+\n * Metadata & Setup Payload\n * ```\n *\n * * [__Frame Type__: (6 bits) 0x01]{@link FrameType#SETUP}\n * * [__Flags__: (10 bits)]{@link SetupFlag}\n * * (__M__)etadata: Metadata present\n * * (__R__)esume Enable: Client requests resume capability if possible. Resume Identification Token present.\n * * (__L__)ease: Will honor LEASE (or not).\n * * __Major Version__: (16 bits = max value 65,535) Unsigned 16-bit integer of Major version number of the protocol.\n * * __Minor Version__: (16 bits = max value 65,535) Unsigned 16-bit integer of Minor version number of the protocol.\n * * __Time Between KEEPALIVE Frames__: (31 bits = max value 2^31-1 = 2,147,483,647) Unsigned 31-bit integer of Time (in milliseconds) between KEEPALIVE frames that the client will send. Value MUST be > 0.\n * * For server-to-server connections, a reasonable time interval between client KEEPALIVE frames is 500ms.\n * * For mobile-to-server connections, the time interval between client KEEPALIVE frames is often > 30,000ms.\n * * __Max Lifetime__: (31 bits = max value 2^31-1 = 2,147,483,647) Unsigned 31-bit integer of Time (in milliseconds) that a client will allow a server to not respond to a KEEPALIVE before it is assumed to be dead. Value MUST be > 0.\n * * __Resume Identification Token Length__: (16 bits = max value 65,535) Unsigned 16-bit integer of Resume Identification Token Length in bytes. (Not present if R flag is not set)\n * * __Resume Identification Token__: Token used for client resume identification (Not present if R flag is not set)\n * * __MIME Length__: Encoding MIME Type Length in bytes.\n * * __Encoding MIME Type__: MIME Type for encoding of Data and Metadata. This SHOULD be a US-ASCII string\n * that includes the [Internet media type](https://en.wikipedia.org/wiki/Internet_media_type) specified\n * in [RFC 2045](https://tools.ietf.org/html/rfc2045). Many are registered with\n * [IANA](https://www.iana.org/assignments/media-types/media-types.xhtml) such as\n * [CBOR](https://www.iana.org/assignments/media-types/application/cbor).\n * [Suffix](http://www.iana.org/assignments/media-type-structured-suffix/media-type-structured-suffix.xml)\n * rules MAY be used for handling layout. For example, `application/x.netflix+cbor` or\n * `application/x.reactivesocket+cbor` or `application/x.netflix+json`. The string MUST NOT be null terminated.\n * * __Setup Data__: includes payload describing connection capabilities of the endpoint sending the\n * Setup header.\n *\n * __NOTE__: A server that receives a SETUP frame that has (__R__)esume Enabled set, but does not support resuming operation, MUST reject the SETUP with an ERROR[REJECTED_SETUP].\n *\n * @description Sent by client to initiate protocol processing.\n * @see [Official documentation]{@link https://github.com/rsocket/rsocket/blob/master/Protocol.md#frame-setup}\n */\nexport class SetupFrame extends Frame {\n /**\n * Constructs a `SetupFrame` instance.\n *\n * @param {number} keepalive - Interval (ms) between client KEEPALIVE frames.\n * @param {number} lifetime - Max time (ms) the server allows no KEEPALIVE.\n * @param {MimeType<any>} metadataType - MIME type for metadata encoding.\n * @param {MimeType<any>} dataType - MIME type for data encoding.\n * @param {string} [resumeToken] - Optional resume token (if `RESUME` flag is set).\n * @param {number} [majorVersion=1] - Major protocol version.\n * @param {number} [minorVersion=0] - Minor protocol version.\n * @param {SetupFlag} [flags=SetupFlag.NONE] - Initial flags (LEASE, METADATA, etc).\n * @param {Metadata<any>} [metadata] - Optional metadata block.\n * @param {Payload<any>} [payload] - Optional setup payload (application-specific).\n */\n public constructor(\n public readonly keepalive: number,\n public readonly lifetime: number,\n public readonly metadataType: MimeType<any>,\n public readonly dataType: MimeType<any>,\n public readonly resumeToken?: string,\n public readonly majorVersion: number = 1,\n public readonly minorVersion: number = 0,\n flags: SetupFlag = SetupFlag.NONE,\n metadata?: Metadata<any>,\n payload?: Payload<any>\n ) {\n flags = SetupFlag.combine(flags, resumeToken != undefined ? SetupFlag.RESUME : SetupFlag.NONE)\n super(FrameType.SETUP, 0, flags, metadata, payload)\n }\n\n /**\n * Deserializes a `SetupFrame` from binary.\n *\n * @param {Header} header - Frame header containing flags and type.\n * @param {ByteReader} reader - Reader instance pointing to frame body.\n * @param {MimeType<any>} _ - Ignored metadata type from deserialization context.\n * @param {MimeType<any>} __ - Ignored payload type from deserialization context.\n * @returns {SetupFrame} Parsed setup frame.\n */\n public static from(header: Header, reader: ByteReader, _: MimeType, __: MimeType): SetupFrame {\n const major = reader.i16()\n const minor = reader.i16()\n const keepalive = reader.i32()\n const lifetime = reader.i32()\n const resumeToken = header.isFlagSet(SetupFlag.RESUME) ? decode(reader.read(reader.i16())) : undefined\n const metadataType = MimeType.valueOf(decode(reader.read(reader.i8())))\n const dataType = MimeType.valueOf(decode(reader.read(reader.i8())))\n const metadata = header.isFlagSet(SetupFlag.METADATA) ? metadataType.toMetadata(reader) : undefined\n const payload = dataType.toPayload(reader)\n return new SetupFrame(keepalive, lifetime, metadataType, dataType, resumeToken, major, minor, header.flags, metadata, payload)\n }\n\n /**\n * Writes the frame-specific portion of the `SetupFrame` to the output.\n *\n * This includes protocol version, keepalive, lifetime, resume token (if any),\n * and MIME types for metadata and data.\n *\n * @param {ByteWriter} writer - Writer to output binary data.\n */\n protected write(writer: ByteWriter) {\n writer.i16(this.majorVersion)\n writer.i16(this.minorVersion)\n writer.i31(this.keepalive)\n writer.i31(this.lifetime)\n if (this.hasResume()) {\n const resumeToken = encode(this.resumeToken)\n writer.i16(resumeToken.length)\n writer.write(resumeToken)\n }\n const metadataType = encode(this.metadataType.mimeType)\n writer.i8(metadataType.length)\n writer.write(metadataType)\n const dataType = encode(this.dataType.mimeType)\n writer.i8(dataType.length)\n writer.write(dataType)\n }\n\n /**\n * SETUP frames must never be ignored, regardless of the IGNORE flag.\n *\n * @returns {false}\n */\n public canBeIgnored(): boolean {\n return false\n }\n\n /**\n * Indicates whether this frame has resume support enabled.\n * Based on the `RESUME` flag.\n *\n * @returns {boolean} `true` if resume token is present and `RESUME` flag is set.\n */\n public hasResume(): boolean {\n return this.isFlagSet(SetupFlag.RESUME)\n }\n\n /**\n * Indicates whether this frame honors LEASE semantics.\n * Based on the `LEASE` flag.\n *\n * @returns {boolean} `true` if the LEASE flag is set.\n */\n public isRespectLease(): boolean {\n return this.isFlagSet(SetupFlag.LEASE)\n }\n}","import {ByteReader, ByteWriter} from \"bebyte\";\nimport {Frame} from \"@/frame/Frame\";\nimport {FrameType} from \"@/frame/FrameType\";\nimport {FrameFlag} from \"@/frame/FrameFlag\";\nimport {Header} from \"@/frame/context/Header\";\nimport {MimeType} from \"@/mimetype/MimeType\";\nimport {Metadata} from \"@/frame/context/Metadata\";\n\n/**\n * ### LEASE Frame (0x02)\n *\n * Lease frames MAY be sent by the client-side or server-side Responders and inform the\n * Requester that it may send Requests for a period of time and how many it may send during that duration.\n * See [Lease Semantics](#lease-semantics) for more information.\n *\n * The last received LEASE frame overrides all previous LEASE frame values.\n *\n * Lease frames MUST always use Stream ID 0 as they pertain to the Connection.\n *\n * Frame Contents\n *\n * ```\n * 0 1 2 3\n * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n * |0| Stream ID = 0 |\n * +-----------+-+-+---------------+-------------------------------+\n * |Frame Type |0|M| Flags |\n * +-----------+-+-+---------------+-------------------------------+\n * |0| Time-To-Live |\n * +---------------------------------------------------------------+\n * |0| Number of Requests |\n * +---------------------------------------------------------------+\n * Metadata\n * ```\n *\n * * [__Frame Type__: (6 bits) 0x02]{@link FrameType#LEASE}\n * * [__Flags__: (10 bits)]{@link FrameFlag}\n * * (__M__)etadata: Metadata present\n * * __Time-To-Live (TTL)__: (31 bits = max value 2^31-1 = 2,147,483,647) Unsigned 31-bit integer of Time (in milliseconds) for validity of LEASE from time of reception. Value MUST be > 0.\n * * __Number of Requests__: (31 bits = max value 2^31-1 = 2,147,483,647) Unsigned 31-bit integer of Number of Requests that may be sent until next LEASE. Value MUST be > 0.\n *\n * A Responder implementation MAY stop all further requests by sending a LEASE with a value of 0 for __Number of Requests__ or __Time-To-Live__.\n *\n * When a LEASE expires due to time, the value of the __Number of Requests__ that a Requester may make is implicitly 0.\n *\n * This frame only supports Metadata, so the Metadata Length header MUST NOT be included, even if the (M)etadata flag is set true.\n *\n * @description Sent by Responder to grant the ability to send requests.\n * @see [Official documentation]{@link https://github.com/rsocket/rsocket/blob/master/Protocol.md#frame-lease}\n */\nexport class LeaseFrame extends Frame {\n /**\n * Constructs a `LeaseFrame` instance.\n *\n * @param {number} ttl - Time-To-Live (in milliseconds) for which the lease is valid. Must be > 0.\n * @param {number} requestLimit - Maximum number of requests allowed under this lease. Must be > 0.\n * @param {Metadata<any>} [metadata] - Optional metadata (without length prefix).\n */\n public constructor(\n public readonly ttl: number,\n public readonly requestLimit: number,\n metadata?: Metadata<any>\n ) {\n super(FrameType.LEASE, 0, FrameFlag.NONE, metadata, undefined);\n }\n\n /**\n * Parses a `LeaseFrame` from a byte stream.\n *\n * @param {Header} header - Frame header (must have `Stream ID = 0`).\n * @param {ByteReader} reader - Reader positioned at TTL.\n * @param {MimeType} metadataType - MIME type to decode metadata.\n * @param {MimeType} _ - Payload MIME type (ignored).\n * @returns {LeaseFrame} Parsed instance.\n */\n public static from(header: Header, reader: ByteReader, metadataType: MimeType, _: MimeType): LeaseFrame {\n return new LeaseFrame(\n reader.i32(),\n reader.i32(),\n header.isFlagSet(FrameFlag.METADATA) ? metadataType.toMetadata(reader, false) : undefined\n )\n }\n\n /**\n * Writes the TTL and request limit, followed by optional metadata (without metadata length prefix).\n *\n * @param {ByteWriter} writer - Writer for binary serialization.\n */\n protected write(writer: ByteWriter) {\n writer.i31(this.ttl)\n writer.i31(this.requestLimit)\n }\n\n /**\n * `LEASE` frames must never be ignored, as they control permission to send requests.\n *\n * @returns {false}\n */\n public canBeIgnored(): boolean {\n return false\n }\n}","import {ByteReader, ByteWriter} from \"bebyte\";\nimport {Frame} from \"@/frame/Frame\";\nimport {FrameType} from \"@/frame/FrameType\";\nimport {Payload} from \"@/frame/context/Payload\";\nimport {Header} from \"@/frame/context/Header\";\nimport {KeepaliveFlag} from \"@/frame/FrameFlag\";\nimport {MimeType} from \"@/mimetype/MimeType\";\n\n/**\n * ### KEEPALIVE Frame (0x03)\n *\n * KEEPALIVE frames MUST always use Stream ID 0 as they pertain to the Connection.\n *\n * KEEPALIVE frames MUST be initiated by the client and sent periodically with the (__R__)espond flag set.\n *\n * KEEPALIVE frames MAY be initiated by the server and sent upon application request with the (__R__)espond flag set.\n *\n * Reception of a KEEPALIVE frame with the (__R__)espond flag set MUST cause a client or server to send\n * back a KEEPALIVE with the (__R__)espond flag __NOT__ set. The data in the received KEEPALIVE MUST be\n * echoed back in the generated KEEPALIVE.\n *\n * Reception of a KEEPALIVE by a server indicates to the server that the client is alive.\n *\n * Reception of a KEEPALIVE by a client indicates to the client that the server is alive.\n *\n * Frame Contents\n *\n * ```\n * 0 1 2 3\n * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n * |0| Stream ID = 0 |\n * +-----------+-+-+-+-------------+-------------------------------+\n * |Frame Type |0|0|R| Flags |\n * +----------