XQUIC Documentation

repository·main·Indexed 24 days ago

https://github.com/alibaba/xquic

A high-performance, cross-platform implementation of QUIC and HTTP/3 protocols for client and server use cases. XQUIC is OS-agnostic, supporting Android, iOS, HarmonyOS, Linux, macOS, and Windows (since v1.2.0). It conforms to RFC 9000, 9001, 9002, 9114, and 9204, featuring pluggable congestion control (NewReno, Cubic, BBR, BBRv2) and cryptography (BoringSSL, BabaSSL). The library provides a comprehensive API for managing engine lifecycles, ALPN registration, and connection-layer callbacks.

Tokens
90.8K
Snippets
127
Records
379
Agent score
80%

What's inside XQUIC

  1. Overview of XQUIC

    main

    XQUIC is a client and server implementation of QUIC and HTTP/3 as specified by the IETF. It is OS and platform agnostic, supporting Android, iOS, HarmonyOS, Linux, macOS, and Windows (since v1.2.0).

    Key features include:

    • Standardized Compliance: Conforms to RFC 9000, RFC 9001, RFC 9002, RFC 9114, and RFC 9204, including 0-RTT connection establishment, HTTP/3, and QPACK.
    • Pluggable Congestion Control: Supports NewReno, Cubic, BBR, and BBRv2.
    • Pluggable Cryptography: Integrates with BoringSSL and BabaSSL (Tongsuo).
    • Advanced Features: Supports Multipath QUIC and QUIC-LB (draft versions).
  2. Overview of the QPACK compression process

    main

    QPACK is designed for HTTP/3 to handle out-of-order delivery in QUIC while balancing compression efficiency and minimizing Head-of-Line (HoL) blocking. It uses two tables to associate indices with header content:

    1. Static Table: Pre-defined and contains common header fields. References to the static table or literal text representations do not require dynamic table support and never cause HoL blocking.
    2. Dynamic Table: Built during the connection process to allow the encoder to index header content. References to the dynamic table carry a risk of HoL blocking if the encoder references an entry that the decoder has not yet received.

    An encoder must send field representations in the same order as the input field area to preserve ordering.

  3. Manage QPACK Decoder State Synchronization

    main

    The decoder uses the decoder stream to notify the encoder of several events to manage the dynamic table lifecycle:

    • Section Acknowledgement: After successfully decoding a field section that uses dynamic table references, the decoder MUST send a Section acknowledgement instruction. This allows the encoder to safely remove unreferenced entries from the dynamic table.
    • Stream Cancellation: If a stream is reset or terminated before all encoded field sections are processed, the decoder generates a Stream Cancellation instruction. Note that an encoder cannot assume all dynamic table updates were received from this instruction alone.
    • Insert Count Increment: The decoder sends this instruction after inserting new entries into the dynamic table. Timely feedback is important for decompression efficiency; delaying this instruction too long may force the encoder to wait for confirmations, reducing efficiency.
  4. How CANCEL_PUSH works in HTTP/3

    main

    The CANCEL_PUSH frame (type 0x3) is used to cancel a server push before the push stream is created. It uses a variable-length integer Push ID to identify the push.

    Usage Scenarios:

    • Client side: Sends CANCEL_PUSH to indicate it does not want to receive a promised resource. If a push stream is already open, the client SHOULD terminate the stream reading with an H3_REQUEST_CANCELLED error.
    • Server side: Receives CANCEL_PUSH to stop sending a server push.
      • If the push stream hasn't been created: The server does not create it.
      • If the stream is already open: The server should abruptly terminate the stream.

    Constraints:

    • Must be sent on the control stream. Sending it on other streams results in an H3_FRAME_UNEXPECTED stream error.
    • If the Push ID is larger than allowed in the current connection, it MUST be treated as an H3_ID_ERROR connection error.
    • If the Push ID was not mentioned in a PUSH_PROMISE frame, it MUST be treated as an H3_ID_ERROR connection error.
    CANCEL_PUSH Frame {
      Type (i) = 0x3,
      Length (i),
      Push ID (i),
    }
  5. How QUIC Data Flow Control Works

    main

    QUIC uses a credit-based flow control mechanism to prevent a sender from overwhelming a receiver's buffers. There are two hierarchical levels of flow control:

    1. Stream-level Flow Control: Limits the amount of data that can be sent on a single stream to prevent one stream from consuming the entire connection buffer.
    2. Connection-level Flow Control: Limits the total bytes sent via STREAM frames across all streams to prevent the sender from exceeding the receiver's total connection buffer capacity.

    Rules for Senders:

    • A sender MUST NOT send data exceeding either the stream-level or connection-level limit.
    • If a sender reaches a limit, it becomes blocked. It SHOULD send STREAM_DATA_BLOCKED or DATA_BLOCKED frames to inform the receiver that it has data to write but is being throttled.
    • If a sender is blocked longer than the idle timeout, the receiver may close the connection.

    Rules for Receivers:

    • Initial limits are set via transport parameters during the handshake.
    • Receivers increase limits by sending MAX_STREAM_DATA frames (for specific streams) or MAX_DATA frames (for the entire connection).
    • MAX_STREAM_DATA specifies an absolute byte offset limit for a specific stream.
    • MAX_DATA specifies an absolute byte offset limit for the sum of all data across all streams.
    • If a sender violates a limit, the receiver MUST close the connection with a FLOW_CONTROL_ERROR.
  6. Interaction between Client Migration and Preferred Address

    main

    If a client needs to perform its own connection migration (e.g., changing networks) at the same time the server suggests a preferred address, the client SHOULD perform path validation for both the client's new address and the server's preferred address simultaneously.

    Resolution Logic:

    • Success on both: The client must abandon validation of the server's initial address and migrate to the server's preferred address.
    • Success on server initial, failure on preferred: The client may switch to its own new address but continue sending packets to the server's initial IP address.

    Security Note: Servers must protect against attacks if packets received from the preferred address have a source address different from what was observed during the handshake (unless it is an intentional simultaneous migration).

  7. Requirements for the XQUIC Coverage Gate

    main

    When making production behavior changes, you must satisfy the Coverage Gate requirements. A change is not considered validated unless the diff includes:

    1. Unit Coverage: Tests tied to the changed path.
    2. Paired Unit Tests: At least one 'happy-path' unit test AND at least one 'abnormal-path' unit test (covering rejection, boundary, or error branches).
    3. Paired Case Tests: A client-to-server happy-path case and an abnormal-path case in scripts/case_test.sh that proves expected rejection, error, close, or recovery behavior.
    4. Case ID Management:
      • Each new client-to-server case must have a distinct, never-before-used, and currently unreserved ID.
      • IDs must be allocated from the owning layer or module namespace as defined in the validation specification.
      • New IDs must be recorded in the namespace registry within the same change.
    5. Test Assertions: Case tests must use distinct case_print_result names and assert observable client/server results. Simply asserting that a process exited is insufficient.
  8. Retransmission rules for QUIC frames

    main

    When a QUIC packet is determined to be lost, the sender may retransmit the information contained within its frames. Different frame types follow specific retransmission rules:

    • CRYPTO frames: Retransmitted according to [QUIC-RECOVERY] rules until all data is acknowledged. Data in Initial and Handshake CRYPTO frames is discarded when the corresponding packet number space keys are dropped.
    • STREAM frames: Application data is retransmitted in new STREAM frames unless a RESET_STREAM is sent. Once RESET_STREAM is sent, no further STREAM frames are sent.
    • ACK frames: Carry recent acknowledgments and delay information. Retransmitting old ACK frames can lead to incorrect RTT calculations.
    • RESET_STREAM frames: Carries stream cancellation. Must be retransmitted until acknowledged or the stream reaches Reset Recvd or Data Recvd states. MUST NOT change the content during retransmission.
    • STOP_SENDING frames: Carries stream cancellation. Retransmitted until the receiver enters Data Recvd or Reset Recvd states.
    • CONNECTION_CLOSE frames: Signals connection closure. Do not require retransmission upon packet loss.
    • MAX_DATA / MAX_STREAM_DATA / MAX_STREAMS frames: Used for flow control limits. Retransmitted if the packet containing the latest value is lost or if the value needs updating. Avoid frequent updates to prevent unnecessary traffic.
    • DATA_BLOCKED / STREAM_DATA_BLOCKED / STREAMS_BLOCKED frames: Carry blocking signals. Retransmitted if the packet containing the latest blocking info is lost.
    • PATH_CHALLENGE frames: Sent periodically for path validation. Each transmission MUST include a different payload. Retransmitted until a matching PATH_RESPONSE is received.
    • PATH_RESPONSE frames: Sent only once in response to a challenge.
    • NEW_CONNECTION_ID / RETIRE_CONNECTION_ID frames: Retransmitted with the same sequence number if lost.
    • NEW_TOKEN frames: Retransmitted if lost. Handled via direct comparison to manage out-of-order or duplicate tokens.
    • PING / PADDING frames: Contain no information; do not require recovery/retransmission.
    • HANDSHAKE_DONE frames: MUST be retransmitted until acknowledged.
  9. Handle Request Cancellation and Rejection

    main

    Both clients and servers can cancel an open request stream.

    Server-side Rejection

    If a server cancels a request without performing application-level processing, it is considered "rejected".

    • Error Code: The server SHOULD abort its response stream with the H3_REQUEST_REJECTED error code.
    • Constraint: The server MUST NOT return H3_REQUEST_REJECTED for a request that has already been partially or fully processed. For partially processed requests, the server SHOULD use H3_REQUEST_CANCELLED.
    • Client Action: A client may treat a rejected request as if it were never sent, allowing for retries.

    Client-side Cancellation

    • Error Code: A client SHOULD use the H3_REQUEST_CANCELLED error code to cancel a request.
    • Retries: If a client cancels a stream after receiving a partial response, it should not use that response. Only idempotent methods (e.g., GET, PUT, DELETE) can be safely retried automatically.
  10. Understand Stream Final Size and Error Handling

    main

    The Final Size is the total amount of flow control credit consumed by a stream. It is the total number of bytes sent (or the maximum offset + 1).

    Key properties of Final Size:

    • Reliability: The sender always reliably communicates the final size to the receiver, either via the Offset and Length fields in a STREAM frame with the FIN flag, or via the Final Size field in a RESET_STREAM frame.
    • Connection-level Calculation: Once the receiver knows the stream's final size (when entering Size Known or Reset Recvd states), it MUST use this value to calculate the total bytes received for connection-level flow control.
    • Immutability: Once the final size is known, it cannot change.
    • Error Handling:
      • If a RESET_STREAM or STREAM frame indicates a change to an already known final size, the receiver SHOULD respond with a FINAL_SIZE_ERROR.
      • A receiver SHOULD treat receiving data that is greater than or equal to the known final size as a FINAL_SIZE_ERROR (even if the data arrives after the stream is closed).