跳转到内容

规范覆盖

quicz 的目标是在 Zig 中实现 IETF QUIC 传输协议,标准参考:https://quicwg.org/

初期主要参考文档:

  • RFC 8999: Version-Independent Properties of QUIC
  • RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport
  • RFC 9001: Using TLS to Secure QUIC
  • RFC 9002: QUIC Loss Detection and Congestion Control
  • RFC 9369: QUIC Version 2

可验证的 transport 实现任务矩阵维护在 quic_transport_tasks.md。该文档是第一轮实现范围 的工作清单;本文描述当前实现形态。

第 1 阶段:最小但语义正确的 QUIC v1 子集

Section titled “第 1 阶段:最小但语义正确的 QUIC v1 子集”
  • 单路径,IPv4-only
  • 默认 QUIC v1 连接行为(v1: 0x00000001),并在已注明范围按配置支持 QUIC v2 protected long-packet / Retry version 使用、v2 packet/key/token 原语与 RFC 9000 reserved-version greasing 识别
  • 基础包头/包体解析与序列化(Initial / Handshake / 0-RTT / 1-RTT)
  • 每个 UDP 四元组对应一个连接
  • 基础流支持、发送侧 PING 发出、STREAM 与按 packet number space 隔离的 CRYPTO 分片、入站乱序 CRYPTO 缓冲与重复重传丢弃、乱序 STREAM 接收重组与重复重传丢弃、本端 RESET_STREAM 与 STOP_SENDING 发出、入站 RESET_STREAM 与 STOP_SENDING 处理、PATH_CHALLENGE 响应排队、outbound PATH_CHALLENGE 跟踪、PTO 驱动重试、失败计数、匹配 PATH_RESPONSE 校验与调用方提交的 endpoint route path update、建模的 server anti-amplification 发送限制、显式 peer-address validation、server 侧 Retry datagram 签发、首个 client Initial DCID 长度校验、server Initial token 校验、RFC 9000 Initial UDP datagram 1200 字节扩展/丢弃检查、HMAC-SHA256 地址绑定、带过期时间并绑定 originating version 的 token 生成/校验、多 secret 轮换校验、有界 token replay filtering、客户端侧 Retry 处理与 Retry token 消费、handshake CID transport-parameter 校验/导出、本端与对端签发 connection ID 生命周期处理、server 侧 HANDSHAKE_DONE 和 NEW_TOKEN 签发、客户端侧 NEW_TOKEN 存储、HANDSHAKE_DONE 接收校验、handshake confirmation、RFC 9001 client 发送 Handshake / server 接收 Handshake 后的 Initial discard,以及有效 client 侧 HANDSHAKE_DONE、server 侧 sendHandshakeDone 或 backend-confirmed no-output Handshake drive 后的 Handshake-space discard、流量控制和 stream-count 限制、严格 stream 方向校验、max_idle_timeout 处理、本端/对端 CONNECTION_CLOSE 处理与关闭状态处理
  • 针对格式错误的内存态 frame payload 做事务化处理,失败时回滚本次部分 receive、recovery、流控和关闭状态更新
  • 简化的丢包检测和拥塞控制,含自动 ACK 生成、ACK range 处理、未发送 packet 的 ACK 拒绝、sent-packet tracking、ACK 驱动的 STREAM/CRYPTO retransmission requeue、frame-payload packet-number-space ACK/recovery 隔离、跨 packet number space 的 connection-level bytes-in-flight 拥塞发送准入、RTT 估计共享与 PTO backoff、建模的 Initial/Handshake packet number space discard cleanup(含 ECN 状态清理)、Initial/Handshake RTT ACK-delay suppression、Application ACK delay exponent 与 handshake confirmed 后 max_ack_delay 截断、带确定性 timeout 处理的 packet-threshold 和 time-threshold loss detection、NewReno slow-start/congestion-avoidance 增长、NewReno-style recovery period 处理、新拥塞事件后一次性 recovery probe、含 min-RTT refresh 的 persistent congestion 响应、ACK_ECN CE 拥塞响应、packet-number-space PTO PING/new-data/in-flight-STREAM probe timeout 处理,以及 frame-payload ACK_ECN counter 校验

当前实现状态(Current Implementation Status)

Section titled “当前实现状态(Current Implementation Status)”
  • Address-validation token 现在会序列化并认证 originating QUIC version;未带 version 的既有 helper 继续保持 QUIC v1 默认值,validateForVersion() 以及 connection/endpoint 的 *ForVersion() helper 可供 QUIC v2 调用方在 replay 状态变化前拒绝跨版本 token 复用。

  • RFC 9368 version_information 已作为类型化 transport parameter 表达。连接会导出 已配置的 chosen/available versions,按 endpoint 角色校验对端值,在 client 响应 Version Negotiation 后执行 server Version Information downgrade checks,只允许 reserved version 出现在 Available Versions 而不能作为 Chosen Version,把已解析的 version-negotiation 语义 close 失败分类为 VERSION_NEGOTIATION_ERROR,并暴露 该 transport error code,供后续 Compatible Version Negotiation 状态机使用。 VersionCompatibilityselectCompatibleVersion() 会用显式、directional first-flight compatibility 建模 compatible selection,不默认假设任意两个 QUIC version 兼容。Connection 在成功应用对端 transport parameter 后保存 peer Version Information,并提供 server-side compatible-version apply helper,要求 selected version 必须等于该连接配置的 chosen version。 driveCryptoBackendInSpaceWithCompatibleVersion()driveCryptoBackendInSpaceWithCompatibleVersionOrClose() wrapper 会把同一 compatible Version Information policy 接入 CryptoBackend 对端 transport-parameter bytes 处理,并在应用成功后才拉取 backend output,同时在 CryptoBackendProgress 中报告 selected version。

  • Client 连接现在可以校验一个 RFC 8999 Version Negotiation packet,忽略包含 Original Version 或 CID 不匹配的不安全 packet,从本端 available_versions 中选择非 reserved 的 mutual version,通过 Config.version_negotiation_selected_version 把该选择传入后续连接,并校验 server authenticated Version Information;Tls13ClientEndpoint 也可以替换 自己持有的 transport,并发出 route-bound follow-up Initial;如果 follow-up connection 创建或 Initial 发送在 route 注册后失败,follow-up route 会被退役。

  • Tls13ServerEndpoint 可以在已切换的 route 上验证 Retry follow-up Initial, 只在 packet 认证后消费一次性地址验证 token,并把后续 Initial/Handshake TLS output 与已提交 UDP route 一起返回。

  • 纯 Zig TLS 1.3 handshake 会始终发送 QUIC quic_transport_parameters extension(即使参数列表为空),并拒绝缺失该 extension 的 ClientHello 或 EncryptedExtensions。

  • 纯 Zig TLS client 会拒绝未请求的 ServerHello extension,不再静默继续 handshake transcript。

  • 纯 Zig TLS client 会拒绝未请求的 EncryptedExtensions extension,同时仍强制要求 QUIC transport parameters。

  • 纯 Zig TLS certificate 路径会拒绝空 CertificateEntry,测试构造器也不再生成空 certificate entry。

  • 纯 Zig TLS certificate 路径会拒绝 per-entry Certificate extension;当前 ClientHello 未请求 OCSP/SCT 数据。

  • 纯 Zig TLS ClientHello 只有在已有 session ticket、可以同时发送匹配 pre_shared_key 时才发送 early_data

  • pollProtectedShortDatagram() / processProtectedShortDatagram() 支持使用调用方提供的 key 收发 protected 1-RTT short PING/ACK/CRYPTO/HANDSHAKE_DONE/NEW_TOKEN/NEW_CONNECTION_ID/PATH_CHALLENGE/PATH_RESPONSE/RETIRE_CONNECTION_ID/MAX_*/BLOCKED/STREAM/RESET_STREAM/STOP_SENDING/CONNECTION_CLOSE datagram,并把 plaintext 按 Application-space 1-RTT frame 规则处理;真实 TLS secret production 和 socket-backed endpoint DCID lookup wiring 仍待实现。

  • 已实现:QUIC varint 工具、支持 RFC 9369 QUIC v2 long-header packet type bit 映射、short-header spin-bit 保留且带显式 packet number 截断/重建 API 的最小 long/short header codec、protected short-packet spin-bit peeking、带 opaque payload 处理的 RFC 9000 long/short packet envelope codec、RFC 9000 packet number 编码选择/重建、Retry packet codec、RFC 8999 Version Negotiation packet codec、reserved-version greasing detection、client-side selection state 和 RFC 9368 downgrade-validation state、带 constant-time token matching 和 NEW_CONNECTION_ID token uniqueness checks 的 stateless reset datagram helper、带 snapshot 导出/恢复的 HMAC-SHA256 address-validation token replay-filter helper、带 secret 轮换、AddressValidationSecretSet 导出/恢复和 replay-filter snapshot 导出/恢复的内存态 endpoint AddressValidationPolicy、endpoint peer-address binding、内存态 endpoint DCID/IPv4 四元组 routing(含 unsupported-version Version Negotiation response generation、client Initial Source CID route registration、supported-version unknown-DCID Initial accept classification、accepted Initial Original DCID/server Initial SCID route registration、zero-length CID tuple routing、sequence/retire-prior-to/connection-handle route retirement)、endpoint replacement-CID registration、inactive-CID stateless reset token lookup/datagram construction helper、route/version-negotiation/reset/drop/accept receive classification 和 per-UDP-path endpoint ECN state、带 reserved-parameter greasing helper/ignore、连接层导出/应用 helper 和 TLS extension byte 编码/应用 helper 的类型化 RFC 9000/RFC 9368 transport parameter codec、用于 CRYPTO byte-stream bridge、transport-parameter extension byte handoff 和 mock Handshake/0-RTT/1-RTT traffic-secret handoff 的可插拔 CryptoBackend callback contract、类型化 RFC 9000/RFC 9368 transport error code helper、RFC 9001 QUIC v1 和 RFC 9369 QUIC v2 Initial secret/key/IV/header-protection key 派生、RFC 9001 quic ku key-update secret/key/IV 派生、调用方持有和连接已安装的 short-packet key-phase 状态与 selection、AEAD_AES_128_GCM payload protection helper、会按配置使用 v2 wire version 和 type bits 的 protected long-packet seal/open helper、按配置执行 v2 Retry issue/process 校验、v1/v2 Retry Integrity Tag helper 与 AES header-protection mask 应用 helper,以及带 end-offset 校验的 STREAM/CRYPTO、PADDING、PING、ACK/ACK_ECN 多区间、RESET_STREAM、STOP_SENDING、MAX_DATA、MAX_STREAM_DATA、带 stream-count 上限校验的 MAX_STREAMS_BIDI/UNI、DATA_BLOCKED、STREAM_DATA_BLOCKED、带 stream-count 上限校验的 STREAMS_BLOCKED_BIDI/UNI、带非空 token 校验的 NEW_TOKEN、NEW_CONNECTION_ID、RETIRE_CONNECTION_ID、PATH_CHALLENGE/PATH_RESPONSE、HANDSHAKE_DONE 与 connection-close 变体的基础 frame codec。

  • Connection 实现了内存态 stream 发送/接收骨架,包含发送侧 PING 发出、STREAM 分片、本端 resetStream() 发出 RESET_STREAM、本端 stopSending() 发出 STOP_SENDING、通过 sendCryptoInSpace() / recvCryptoInSpace() / pollTxInSpace() / driveCryptoBackendInSpace() 流转的按 packet number space 隔离 CRYPTO 收发字节流与 backend 驱动(含本端/对端 transport-parameter byte exchange 和 mock Handshake/0-RTT/1-RTT traffic-secret 安装)、通过 pollProtectedLongCryptoDatagramInSpace() / processProtectedLongDatagramInSpace() 进行的 protected Initial/Handshake CRYPTO long-packet bridge(含首个 client Initial DCID 长度校验、server Initial token 校验与 RFC 9000 Initial UDP datagram 1200 字节扩展/丢弃检查)、通过 pollProtectedHandshakeDatagramWithInstalledKeys() / processProtectedHandshakeDatagramWithInstalledKeys() 进行的 installed-key Handshake long-packet CRYPTO/ACK/PING bridge、通过 pollProtectedLongDatagram() / processProtectedLongDatagram() 进行的 coalesced protected Initial/Handshake CRYPTO/ACK/PING 与使用调用方 key 或连接已安装 key 的 0-RTT STREAM/RESET_STREAM/STOP_SENDING long-datagram 发送与接收路由,并可通过 pollProtectedZeroRttDatagram() / processProtectedZeroRttDatagram() / pollProtectedZeroRttDatagramWithInstalledKeys() / processProtectedZeroRttDatagramWithInstalledKeys() 单独收发 0-RTT long datagram,使用调用方 key 或连接已安装 key 的 protected 1-RTT short PING/ACK/CRYPTO/HANDSHAKE_DONE/NEW_TOKEN/NEW_CONNECTION_ID/PATH_CHALLENGE/PATH_RESPONSE/RETIRE_CONNECTION_ID/MAX_*/BLOCKED/STREAM/RESET_STREAM/STOP_SENDING/CONNECTION_CLOSE transmit 和 receive(含 pollProtectedShortDatagramWithInstalledKeys() / processProtectedShortDatagramWithInstalledKeys()),以及通过 pollProtectedShortDatagramWithKeyPhase() / processProtectedShortDatagramWithKeyUpdate() 执行显式 short-packet key-phase 收发、默认 Application-space sendCrypto() / recvCrypto() wrapper、通过 localTransportParameters()encodeLocalTransportParameters() 导出本端 transport parameter(含配置的本端 ack_delay_exponent/max_ack_delay、首个 client Initial 后的 server original_destination_connection_idissueRetryDatagram() 后的 server retry_source_connection_id、已知时的首个本端 Initial SCID initial_source_connection_id、配置的 server-only stateless_reset_tokenpreferred_address)、通过 applyPeerTransportParameters()applyPeerTransportParameterBytes() 应用对端 transport parameter(含 Original DCID、Retry CID、对端 Initial SCID 校验、peer max_udp_payload_size 驱动的 recovery max_datagram_size/initial-cwnd resync,以及用于 recovery 的对端 ACK delay policy),并通过 peerActiveMigrationDisabled() 暴露对端 disable_active_migration、通过 peerStatelessResetToken() 暴露对端 transport-parameter stateless reset token、通过 peerPreferredAddress() 暴露固定存储的对端 server preferred address、入站乱序 STREAM range 缓存、重复重传丢弃与连续重组、入站 RESET_STREAM 处理、STOP_SENDING 到 RESET_STREAM 的响应处理、PATH_CHALLENGE 响应排队、sendPathChallenge() outbound challenge 跟踪、通过 checkPathValidationTimeouts() 执行 PTO 驱动重试、通过 failedPathValidationCount() 暴露重试耗尽、匹配 PATH_RESPONSE 校验、通过 recordPeerAddressBytesReceived()antiAmplificationLimitRemaining()validatePeerAddress() 建模 RFC 9000 server anti-amplification 发送限制、通过 issueRetryDatagram() 执行 server 侧 Retry datagram 签发、通过 issueAddressValidationToken() / validateAddressValidationToken() 执行 HMAC-SHA256 address-validation token 生成/校验,可配合 endpoint.Udp4Tuple.peerAddressValidationBinding()endpoint.AddressValidationPolicy 执行 endpoint peer-address binding 与内存态 token lifecycle、通过 processRetryDatagram() 执行客户端侧 Retry datagram 处理并用 latestRetryToken() / originalDestinationConnectionId() / retrySourceConnectionId() 保存状态、通过 issueRetryToken() / validateRetryToken() 执行 server 侧一次性 Retry token 消费、对端签发 NEW_CONNECTION_ID 跟踪与 retire_prior_to 驱动的 RETIRE_CONNECTION_ID 排队、通过 issueConnectionId() 签发本端 NEW_CONNECTION_ID 并遵守对端 active CID limit、入站 RETIRE_CONNECTION_ID 标记本端 CID retired、通过 detectStatelessReset() 执行只读 stateless reset token 检测、server 侧通过 sendHandshakeDone()issueNewToken() 签发 HANDSHAKE_DONE 与 NEW_TOKEN、客户端侧 NEW_TOKEN 存储、HANDSHAKE_DONE 角色校验、通过 handshakeState() 暴露显式 handshake progress 状态与 handshake confirmation、有效 client 侧 HANDSHAKE_DONE 触发的 Handshake space/key discard、MAX_STREAM_DATA stream 状态校验、基础 connection 和 stream 流量控制,以及 outbound DATA_BLOCKED/STREAM_DATA_BLOCKED/STREAMS_BLOCKED 上报、接收侧 MAX_DATA/MAX_STREAM_DATA/MAX_STREAMS_BIDI/UNI credit 刷新与对端 BLOCKED 可观测状态、STREAM_DATA_BLOCKED 接收状态创建/校验、旧 receive limit 的 MAX 重发、双向与单向 stream-count 限制、对端 CONNECTION_CLOSE/APPLICATION_CLOSE 关闭状态处理、通过 closeConnection() / closeApplication() 发出/重发本端 close、通过 Config.max_idle_timeout_ms 导出/应用 max_idle_timeout、通过 effectiveIdleTimeoutMillis()idleTimeoutDeadlineMillis()checkIdleTimeouts() 建模 idle timeout 关闭、显式 ConnectionState 生命周期状态 active/closing/draining/closed 和 3x PTO close-state 过期、显式 HandshakeState 生命周期状态 initial/handshake/confirmed、显式 PacketNumberSpace 处理以隔离 Initial/Handshake/Application frame-payload ACK、显式 FramePacketType 校验 Initial、Handshake、0-RTT 与 1-RTT frame-payload packet type、通过 discardPacketNumberSpace() 清理 Initial/Handshake discard 状态并清理已安装 Handshake key、通过 discardZeroRttProtectionKeys() 清理已安装 0-RTT key、ACK_ECN validation/CE congestion response、ACK delay scaling/capping、packet-threshold/time-threshold loss detection、NewReno slow-start/congestion-avoidance 增长、NewReno-style recovery period 处理、persistent congestion 响应、packet-number-space PTO PING timeout 处理与 recovery 状态、针对 ACK-eliciting payload 的自动 ACK 生成、ACK-only 发送、空间允许时的 ACK 与 PING/CRYPTO/HANDSHAKE_DONE/NEW_TOKEN/STREAM/PATH_CHALLENGE/PATH_RESPONSE/NEW_CONNECTION_ID/RETIRE_CONNECTION_ID/RESET_STREAM/STOP_SENDING/MAX_DATA/MAX_STREAM_DATA/MAX_STREAMS_BIDI/UNI 合并、ACK 驱动的 sent-packet tracking、ACK 驱动的 frame-payload STREAM/CRYPTO retransmission requeue,以及简化 recovery / congestion 状态对象。

  • applyPeerTransportParameterBytesOrClose() 是对端 transport-parameter extension bytes 的显式 opt-in wrapper。它保留 applyPeerTransportParameterBytes() 的成功应用行为,但会在畸形 extension 或非法对端参数语义上,先排队带 TRANSPORT_PARAMETER_ERROR 和 CRYPTO frame_type 的 transport CONNECTION_CLOSE,再返回 InvalidPacket

  • peerClose() 会暴露已接受的对端 CONNECTION_CLOSE/APPLICATION_CLOSE 诊断,包括 error code、transport close 的触发 frame type,以及 reason phrase;无效多帧 payload 会回滚该诊断状态。

  • 当配置 receive_connection_windowreceive_stream_window 时,入站 DATA_BLOCKED 或 STREAM_DATA_BLOCKED 如果报告当前或更新的 receive limit,会把对应 MAX_DATA 或 MAX_STREAM_DATA 增长到 reported + window 并排队;旧报告仍只重发当前 limit,未配置时保持不增长。

  • Aes128KeyPhaseState 为单个 packet-protection 方向提供调用方持有的 current/next 1-RTT key-phase 状态。pollProtectedShortDatagramWithKeyPhaseState() 使用该状态的 current key 与 key-phase bit 发包,processProtectedShortDatagramWithKeyPhaseState() 只会在 packet 认证和 Application frame 处理成功后推进接收状态。连接已安装 key 的本端 key-update 发起要求建模 handshake confirmed;在 Application ACK 覆盖一个使用新 key phase 发送的 packet 前,会拒绝第二次本端 update;如果同一 payload 后续 frame 无效,会回滚该 ACK confirmation 状态。

  • Config.enable_spin_bit 会为 protected short packet 启用确定性的单路径 1-RTT spin-bit 模型。它默认关闭,因此 packetization 保持此前 false wire 值;启用后,server 反射 peer spin bit,client 对最新 server spin bit 取反,nextOutgoingSpinBit() 暴露下一次值,resetSpinBitForPath() 可在后续 path 或 destination-CID 切换时清空状态。

  • endpoint.EndpointRouter 会把 destination connection ID 映射到调用方持有的 connection handle 和 IPv4 UDP 四元组。它可按 long-header datagram 中编码的 DCID 路由,为 unsupported long-header version 写出 RFC 8999 Version Negotiation response,在 client 发送 Initial 前注册 client Initial Source CID,使 server Initial 和 Version Negotiation response 能路由回 client connection,为 supported-version unknown-DCID Initial datagram 返回后续 server accept loop 需要的 Original DCID、client Source CID、token、version 和 path metadata,并在 accept 后注册 Original DCID 与 server Initial SCID,且 invalid/duplicate 输入不会留下部分 route,按已注册 CID prefix 匹配 short-header datagram,按精确 UDP tuple 路由 zero-length CID,拒绝 unknown 或 ambiguous CID,拒绝不同 CID 复用同一个 stateless reset token,保存可选 NEW_CONNECTION_ID sequence number,在 Retry 后把 Initial route 从原 DCID 切换到 Retry Source CID,在调用方验证后把 route 迁移到 server preferred-address CID 和 UDP tuple,按 CID、sequence number、retire_prior_to threshold 或 caller connection handle retire route(含 path-specific zero-CID route),把 replacement CID 注册和 retire_prior_to threshold 应用组合为一个 endpoint policy 操作,在拒绝 stale path update 的前提下把 route 更新到调用方已验证的新路径,把 active_migration_disabled 作为确定性的 path-change 拒绝策略,为 inactive 或 retired CID 暴露 stateless reset token,同时对同一路径上的 active route 抑制 token,在 reset 小于触发 datagram 时使用调用方提供的 unpredictable bytes 写出 stateless reset datagram,把收到的 datagram 分类为 routed、version negotiation、stateless reset、accept_initial 或 dropped,并可为 address-validation token 派生稳定的远端 IPv4/UDP peer-address binding。endpoint.AddressValidationPolicy 持有一个 active token secret、有界 previous-secret 集和有界 replay filter,用于内存态 endpoint token 签发、轮换、校验与 replay 拒绝;它可以导出/恢复 AddressValidationSecretSet snapshot 和 replay-filter snapshot,供外部持久化或 worker 分发。endpoint.EcnPathPolicy 按 UDP tuple 保存 ECN validation state,使迁移后的路径从 unknown 开始,而不是继承其它路径的 capable 或 failed 状态。Socket I/O、socket-backed reset emission、自动 path validation、生产级 secret/replay 存储、真实 IP ECN marking 与 Connection ownership 仍不属于这些 helper。

  • 本地发起的 bidirectional stream 必须先通过 openStream() 创建,才能调用 sendOnStream()openStream() 会遵守对端 bidirectional stream limit,直到收到更大的 MAX_STREAMS_BIDI 帧。

  • 本地发起的 unidirectional stream 必须先通过 openUniStream() 创建,才能调用 sendOnStream()openUniStream() 会遵守对端 unidirectional stream limit,直到收到更大的 MAX_STREAMS_UNI 帧。

  • sendOnStream() 可用于回复已观察到的对端发起 bidirectional stream,当前内存态 echo 示例依赖这个行为;也可用于已打开的本地 unidirectional stream。它会拒绝未观察到的对端发起 stream、未打开的本地发起 stream、对端发起的 unidirectional stream ID、已经发送 FIN 的 stream,以及被流控阻塞的写入。connection、stream 与 stream-count 写入受阻时会先排队对应 BLOCKED frame,再返回 FlowControlBlocked;如果同一发送侧随后进入 FIN 或 reset,待发的 STREAM_DATA_BLOCKED 会在发送前丢弃。

  • resetStream() 会中止已打开本地 stream 的发送侧,或已观察到的对端发起 bidirectional stream 的回复发送侧。它会使用当前发送 offset 作为 final size 排队一个 RESET_STREAM,把发送侧标记为关闭,忽略重复本地 reset,并在 reset frame 发出后丢弃尚未发送的 STREAM 数据。

  • stopSending() 会要求对端停止发送到仍处于 Recv 或 Size Known 的已打开本地 bidirectional stream,或已观察到的对端发起接收 stream。它会排队一个 STOP_SENDING,拒绝 send-only 的本地 unidirectional stream 和未观察到的对端 stream;final size 前所有字节已经到达后会返回 StreamClosed,重复本地 stop 请求会被去重;如果发送前接收侧已进入 Data Recvd 或 Reset Recvd,待发 STOP_SENDING 会被丢弃;正常情况下由对端回发 RESET_STREAM。

  • 入站 DATA_BLOCKEDSTREAM_DATA_BLOCKEDSTREAMS_BLOCKED_* 会更新已观察到的对端最高 blocked limit,并通过 peerDataBlockedLimit()peerStreamDataBlockedLimit()peerStreamsBlockedBidiLimit()peerStreamsBlockedUniLimit() 暴露。STREAM_DATA_BLOCKED 会校验该 stream 在本端是否具有接收侧,合法时可在任何 STREAM 数据之前创建接收状态,并拒绝 send-only、未打开的本地 bidirectional 或超出接收 stream-count limit 的 stream ID。stream final size 已知后,STREAM_DATA_BLOCKED 会被丢弃,不再为该 stream 重排或增长 MAX_STREAM_DATA。对端报告的 limit 低于当前接收侧 credit 时,连接会重排对应 MAX_DATAMAX_STREAM_DATAMAX_STREAMS_*;已配置时,当前 limit 的 peer BLOCKED 报告会按 receive byte window 增长 MAX_DATA/MAX_STREAM_DATA,并按 receive_stream_count_window 增长 MAX_STREAMS_BIDI/UNI。无效多帧 payload 会回滚 blocked 报告、接收状态创建与新排队或增长的 MAX frame。

  • recvOnStream() 可读取 bidirectional stream 与对端发起的 unidirectional 接收流。本地发起的 unidirectional stream ID 只有发送侧语义,在接收 API 上会被拒绝。当入站 frame 打开更高编号的接收 stream 时,同类型低编号 stream 也会被创建,并在数据到达前按 idle stream 读取为空。成功读取会按已消费字节数增加接收侧 connection 和 stream credit,排队 MAX_DATAMAX_STREAM_DATA frame,并丢弃已排队但更小的过期 limit;如果发送前 stream 因学习到 final size 或 reset 而离开 Recv,已排队的 MAX_STREAM_DATA 也会被丢弃。对端发起且 FIN 已被完全消费的 stream 会释放一个接收侧 stream-count credit,并排队 MAX_STREAMS_BIDIMAX_STREAMS_UNI,通过接收 API 观察到的零长度 FIN stream 也会覆盖;对端发起的 reset stream 在应用通过 recvOnStream() 观察到 reset 后,也会释放同样的 stream-count credit。recvStreamFinalSize() 会暴露从 STREAM FIN 或 RESET_STREAM 学到的 final size;recvStreamFinished() 只在 FIN 终点前所有字节都已被消费后报告成功完成。

  • 入站 MAX_STREAM_DATA 只会更新本端拥有发送侧、且该发送侧尚未发送 FIN 的 stream credit。未创建的本地发起 stream 与 receive-only 的对端单向 stream 会被拒绝;对于对端发起 bidirectional stream,它可以在任何 STREAM 数据前同时创建接收状态和发送状态,让后续回复使用对端通告的 credit。

  • processDatagram() 在 Application packet number space 中处理已建模的 bidirectional STREAM/RESET_STREAM 接收状态,以及对端发起的 unidirectional STREAM/RESET_STREAM 接收状态;processDatagramInSpace() 可让同一 frame-payload 骨架在 Initial、Handshake 或 Application packet number space 中运行,processDatagramForPacketType() 则按 Initial、Handshake、0-RTT 或 1-RTT 校验 frame 合法性,并把 0-RTT 与 1-RTT 都映射到共享的 Application packet number space。Initial 与 Handshake packet type 会拒绝 RFC 9000 不允许的 frame type,包括 STREAM、流控、connection-ID、path-validation、NEW_TOKEN、application close 与 HANDSHAKE_DONE frame。0-RTT packet type 会接受 STREAM、RESET_STREAM、STOP_SENDING 等 application frame,拒绝 ACK、ACK_ECN、CRYPTO、HANDSHAKE_DONE、NEW_TOKEN、PATH_RESPONSE 与 RETIRE_CONNECTION_ID,同时保留 Application-space ACK/recovery 记账;无效多帧 payload 会回滚同一 payload 中更早的状态变更。processDatagramOrClose()processDatagramInSpaceOrClose()processDatagramForPacketTypeOrClose() 和 protected long/short *OrClose receive wrapper 是显式 opt-in API;成功路径保留同一回滚边界,已分类的 frame encoding(如非法 ACK/ACK_ECN range 和 STREAMS_BLOCKED limit)、packet-type violation(包含 0-RTT ACK/ACK_ECN frame)、ACK/ACK_ECN 确认未发送 packet number、冲突 STREAM data 或非法 stream-control frame 等语义 frame-processing failure 会先排队 transport CONNECTION_CLOSE,再返回 InvalidPacket。stream 接收处理会缓存非重叠乱序 STREAM range,直到缺口补齐后再连续重组;已存在于连续 buffer 或完全匹配 pending range 的相同重复重传会被忽略,已接收的连续前缀会先裁剪再追加新后缀;所有数据已接收后,final-size 范围内的 late STREAM 数据会被忽略;Size Known 且仍有缺口时,相同 final size 的 RESET_STREAM 会补记剩余 final-size 流控额度并关闭接收侧;RESET_STREAM 之后,如果后续 STREAM 数据仍落在已知 final size 内,也会被忽略;同时拒绝未打开的本地 bidirectional stream ID、入站本地 unidirectional stream ID、超过接收 stream-count limit 的对端发起 stream、Data Recvd 前冲突或当前无法判定的重叠 stream 数据、final size 之后的数据、会改变 reset final size 的 STREAM FIN、final size 不一致的 RESET_STREAM、超出大小限制的 frame payload,以及确认所选 packet number space 中从未发送 packet number 的 ACK/ACK_ECN。

  • EndpointConnectionLifecycle 为 protected Initial、long-header、0-RTT、short-header、显式 key-phase 和 installed-key receive helper 暴露 direct/routed *OrClose 变体。这些 helper 先按 endpoint CID/tuple 路由,再委托对应连接层 close-propagating receive API,旧的 lifecycle rollback-only receive API 保持不变。

  • ACK_ECN 帧会复用普通 ACK 的 range 处理和确认未发送 packet 的 close 分类来更新 sent-packet recovery。连接骨架也会按 packet number space,把已建模的 ECT(0)/ECT(1) 发送 packet 与累计 ACK_ECN counter 做校验。合法的 ECN-CE counter 增量会进入与 loss 共用的 NewReno congestion recovery period,但不会把已 ACK 的 packet 当作 lost;recovery start 之前发送 packet 的重复 CE 增量不会再次降低 congestion window。校验失败会记录 EcnValidationState.failed,但不会关闭连接,因为当前 frame-payload API 还不拥有真实 IP ECN 标记或 network path 身份。

  • discardPacketNumberSpace() 会建模 Initial 或 Handshake keys 被丢弃时的 recovery 副作用:清空该空间的待发送 ACK、largest acknowledged、sent-packet tracking、已排队/已接收的 CRYPTO 状态、bytes in flight、loss deadline、PTO backoff、ECN validation counter 与 ECN 状态,在丢弃 Handshake space 时同步清理已安装 Handshake packet-protection key,拒绝后续继续使用该 frame-payload 空间,并保持 Application data 空间不变。client 侧成功发送 Handshake packet 与 server 侧成功接收 Handshake packet 后,会且只会在发送/接收 commit 成功后触发 Initial-space discard;发送失败、无效 payload 和 packet 认证失败会保留 Initial 状态。server 侧 sendHandshakeDone() 会在保留 1-RTT HANDSHAKE_DONE 待发送 frame 的同时丢弃 Handshake 状态,backend-confirmed no-output Handshake drive 会丢弃同一状态,并拒绝在丢弃后重新安装 Handshake key。已安装的 peer 0-RTT receive key 需要先通过 acceptZeroRtt() 显式接受,processProtectedZeroRttDatagramWithInstalledKeys() 才会提交 early data;rejectZeroRtt() 会在使用前丢弃这些 key。discardZeroRttProtectionKeys() 会显式清理已安装 early-data key,但不丢弃共享的 Application packet number space;client 安装 1-RTT key 时也会清理已安装的 0-RTT key,server 只有在 1-RTT short packet 完成认证且 Application-frame payload 被接受后才会清理已安装的 0-RTT key。

  • Initial、Handshake 与 Application CRYPTO byte stream 拥有独立 offset、发送队列、接收缓冲、乱序 pending 缓冲、待发送 ACK、sent-packet tracking 与回滚行为。CRYPTO 接收处理会用 Config.max_crypto_buffer_size 约束可接受的 end offset,把缺口之后的数据先缓存到 pending,直到连续字节可被 recvCryptoInSpace() 读取;已经连续缓存或完全匹配 pending range 的相同重传会被忽略,冲突重叠会被拒绝。close-on-error receive API 会把配置的 CRYPTO 接收缓冲超限映射为 CRYPTO_BUFFER_EXCEEDEDdriveCryptoBackendInSpace() 可以把本端 transport-parameter extension bytes 交给调用方 CryptoBackend,应用 backend 返回的对端 transport-parameter extension bytes,安装返回的 Handshake、0-RTT 与 1-RTT traffic secrets,把连续 CRYPTO 字节交给 backend,把 backend 产出的字节重新排入对应 CRYPTO send stream,并在 backend 报告完成时标记建模 handshake confirmed。如果 Handshake-space backend drive 确认 handshake 且没有排队 outbound CRYPTO,会在同一调用中丢弃 Handshake 状态和已安装 Handshake key;如果已经排队 outbound Handshake CRYPTO,则保留该 space,让该 flight 先发送。这仍只是 frame-payload TLS bridge hook;尚未运行真实 TLS 1.3 backend。

  • RTT 更新会用对端 ack_delay_exponent 解码 ACK Delay。Initial 和 Handshake ACK Delay 会被忽略,每个合法 largest-acknowledged RTT sample 都会把 connection-level RTT 估计同步到未丢弃的 packet number space。HANDSHAKE_DONEconfirmHandshake() 标记 handshake confirmed 后,Application-space RTT 更新会把解码后的 ACK Delay 截到对端 max_ack_delay

  • ACK 处理会在所选 packet number space 中执行简化 packet-threshold 和 time-threshold loss detection。当某个未确认已发送 packet 至少比 largest newly acknowledged packet 旧 3 个 packet number,或它的 sent_time + 9/8 * max(latest_rtt, smoothed_rtt) deadline 已经过期且满足 1ms granularity 下限时,它会从 sent tracking 移除并作为 lost 上报给 recovery 状态。Recovery 状态会在 slow start 中按 newly acknowledged bytes 增长 congestion window,并在 congestion avoidance 中按 max_datagram_size * bytes_acked / congestion_window 且至少 1 字节增长。applyPeerTransportParameters() 现在会同步 recovery max_datagram_size;如果 peer max_udp_payload_size 降低了有效发送 datagram 大小,所有 packet-number-space recovery state 会把 congestion window 重置为较小 datagram size 下重新计算的 initial congestion window。NewReno-style recovery period 会避免对恢复开始前发送 packet 的 loss 或 ECN-CE congestion event 重复降窗,这些 packet 的 ACK 也不会增长 congestion window。当 loss 或 ECN-CE signal 启动新的 recovery period,且该 packet number space 已有待发送 ack-eliciting 数据时,连接会给该空间一次性 recovery probe 额度,让重传即使在 bytes in flight 已填满降窗后的 congestion window 时也能发出。如果第一次 RTT sample 之后发送的连续 lost packet 区间达到 RFC 9002 persistent congestion duration,congestion window 会降到 minimum window;如果这个 ACK 同时产生了最新 RTT sample,min_rtt 会刷新为该 sample 并重新同步到所有 packet number space。跨越同样时长但 packet number 不连续的 lost packet 会保持在普通 NewReno recovery 路径。lossDetectionDeadlineMillis() 会暴露下一个建模 loss deadline,checkLossDetectionTimeouts() 会在可控时钟下处理到期 deadline。ptoDeadlineMillis() 会暴露简化 PTO deadline,lossDetectionTimerDeadlineMillis() 会跨 packet number space 选择 loss-time-before-PTO aggregate timer deadline,serviceLossDetectionTimer() 是连接侧处理该到期 aggregate timer 的 helper。EndpointLossDetectionTimers 会在 endpoint/event-loop 层按 caller connection handle 管理调度,在 timer service 后刷新每个 handle,并已被 socket-backed UDP loss/PTO 示例用于 ACK 或 loss cleanup 后的 protected-packet timer arm、service 和 disarm。PTO 处理使用一个 connection-level backoff count,在 ack-eliciting packet 被 ACK(client Initial ACK 除外)或 Initial/Handshake space discard 后清零;server 被 anti-amplification limit 卡住且没有发送额度时会 disarm PTO;recordPeerAddressDatagramReceived() 因新收到的 datagram 获得发送额度后会重新 arm PTO,并在原 deadline 已过期时立即 service;probe 选择仍优先把已排队 ack-eliciting 数据作为 probe,没有这类待发数据时,会先复用 in-flight CRYPTO,再复用 Application STREAM,最后才在到期的 Initial、Handshake 或 Application packet number space 中排队 PING probe。完整 socket-owned connection lifecycle 与 TLS-owned protected-packet timer 集成仍待实现。

  • 入站 STOP_SENDING 只接受本端拥有发送侧的 stream。对于对端发起 bidirectional stream,它可以在任何 STREAM 数据前创建接收状态,只 reset 本端发送侧,并保持接收侧后续仍可接收对端数据。它复用 resetStream() 相同的发送侧关闭与 RESET_STREAM 排队行为。

  • 入站 NEW_TOKEN 只允许 client 连接接收,并按 Config.max_stored_new_tokens 上限保存为 opaque future address-validation 数据;server 可用 issueAddressValidationToken()issueAddressValidationTokenForVersion()validateAddressValidationToken()validateAddressValidationTokenForVersion()validateAddressValidationTokenWithSecrets() 生成并校验 HMAC-SHA256 地址绑定、originating-version-bound 且带过期时间的 NEW_TOKEN 值;endpoint 代码可用 AddressValidationPolicyUdp4Tuple.peerAddressValidationBinding() 签发 token、在 secret 轮换集合中校验 token,通过 AddressValidationSecretSet 导出/恢复 active/previous token secrets,通过 ReplayFilterSnapshot 导出/恢复 replay-filter 指纹,并在恢复后继续拒绝重复使用; server 侧收到该帧会把整个 payload 判为无效,并回滚本 payload 中更早的状态变更。

  • server 侧 sendHandshakeDone() 会标记 modeled handshake confirmed,丢弃 Handshake packet-number-space 状态和已安装 Handshake key,并保留 1-RTT HANDSHAKE_DONE frame,直到后续 pollTx() 或 protected short-packet send commit。入站 HANDSHAKE_DONE 只允许 client 连接接收,会标记 modeled handshake confirmed,并在整个 payload 被接受后丢弃同一 Handshake 状态;server 侧收到该帧会把整个 payload 判为无效,并回滚本 payload 中更早的状态变更。

  • 入站 DATA_BLOCKEDSTREAM_DATA_BLOCKEDSTREAMS_BLOCKED_* 会更新已观察到的对端最高 blocked limit,并通过 peerDataBlockedLimit()peerStreamDataBlockedLimit()peerStreamsBlockedBidiLimit()peerStreamsBlockedUniLimit() 暴露。如果对端报告的 limit 低于当前接收侧 credit,连接会重新排队对应的 MAX_DATAMAX_STREAM_DATAMAX_STREAMS_* frame;已配置时,当前 limit 报告会按 byte receive window 增长 MAX_DATA/MAX_STREAM_DATA,并按 receive_stream_count_window 增长 MAX_STREAMS_BIDI/UNI。无效多帧 payload 会回滚这些报告和新排队或增长的 MAX frame。

  • 无效的多帧 payload 会回滚本次 payload 中已经改变的状态,包括 handshake confirmation、CRYPTO 接收缓存和 pending 缓冲、stream 接收缓存、RESET_STREAM 状态、已保存 NEW_TOKEN 值、已排队 PATH_RESPONSE/RETIRE_CONNECTION_ID/RESET_STREAM 值、outstanding PATH_CHALLENGE 值与重试元数据、对端签发与本端签发 connection ID 的 retired 标记、MAX_DATA/MAX_STREAMS_BIDI/UNI 更新、所选空间的待发送 ACK 状态、所选空间的 sent-packet recovery、packet/time-threshold loss 与 ECN validation 状态,以及关闭状态。

  • 当前 pollTx / processDatagram 只流转未加密 QUIC frame payload 字节。pollTx 可能发送 ACK-only payload、已排队 CONNECTION_CLOSE/APPLICATION_CLOSE、PING、CRYPTO、HANDSHAKE_DONE、NEW_TOKEN、PATH_CHALLENGE、PATH_RESPONSE、NEW_CONNECTION_ID、RETIRE_CONNECTION_ID、MAX_/BLOCKED、RESET_STREAM 或 STREAM payload,或在空间允许时合并待发送 ACK。成功发送/接收会刷新建模的 idle timeout deadline;无效 frame payload 不会刷新。server 连接的 peer address 未验证时,pollTx()pollTxInSpace() 会在所有 packet number space 之间共享并执行建模的 3x anti-amplification 预算,直到直接调用 validatePeerAddress() 或消费匹配 Retry token。server 连接可通过 issueRetryDatagram() 签发 Retry datagram;该方法会记录 Retry Source Connection ID 供本端 transport parameter 导出,并注册 token 供一次性校验。issueAddressValidationToken() 会创建 HMAC-SHA256 地址绑定、originating-version-bound 且带过期时间的 Retry 或 NEW_TOKEN 值,AddressValidationPolicy 可为 Udp4Tuple.peerAddressValidationBinding() 签发这些 token,按 current/previous secrets 校验并通过自身 replay filter 拒绝已使用 token 指纹;validateAddressValidationToken() / validateAddressValidationTokenWithSecrets() 会先校验 token 类型、originating version、peer address、寿命和 MAC,再解除 anti-amplification 限制;Retry token 还会从 pending Retry-token 集合中一次性消费。client 连接可通过 processRetryDatagram() 校验 Retry datagram;当 explicit Initial token 为空时,protected Initial packetization 会复用已保存的 Retry token,并且 applyPeerTransportParameters() 会用保存的 Retry CID 校验 original_destination_connection_idretry_source_connection_id。首次成功发出 protected client Initial 会记录 Original Destination Connection ID,用于后续 server transport parameter 校验。首次成功发出 protected Initial 会把本端 Initial Source Connection ID 保存到 localInitialSourceConnectionId();当该值已知时,localTransportParameters() 会把它导出为 initial_source_connection_id。首次成功打开 protected client Initial 会记录 Original Destination Connection ID,用于 server localTransportParameters().original_destination_connection_id 导出。首次成功打开 protected Initial 会把对端 Initial Source Connection ID 保存到 peerInitialSourceConnectionId();当该值已知时,applyPeerTransportParameters() 会据此校验对端 initial_source_connection_id。Initial 与 Handshake CRYPTO、ACK-only 和 PING packet 也可作为 protected long packet 发出,通过 pollProtectedLongDatagram() coalesce,再通过 processProtectedLongDatagramInSpace() 单包接收,或通过 processProtectedLongDatagram() 作为 coalesced long datagram 接收;匹配的 *OrClose 变体会在 protected plaintext 认证后把已分类 frame 错误排队为 CONNECTION_CLOSE。Handshake packet 也可通过 pollProtectedHandshakeDatagramWithInstalledKeys() / processProtectedHandshakeDatagramWithInstalledKeys() 使用连接已安装 key 收发。使用调用方 key 或连接已安装 key 的 0-RTT STREAM/RESET_STREAM/STOP_SENDING packet 共享 Application packet number space,并通过 pollProtectedZeroRttDatagram() / processProtectedZeroRttDatagram()pollProtectedZeroRttDatagramWithInstalledKeys() / processProtectedZeroRttDatagramWithInstalledKeys(),或带 keys.zero_rttpollProtectedLongDatagram() / processProtectedLongDatagram() 收发;0-RTT receive helper 也提供 close-propagating *OrClose 变体。Protected 1-RTT short PING/ACK/CRYPTO/HANDSHAKE_DONE/NEW_TOKEN/NEW_CONNECTION_ID/PATH_CHALLENGE/PATH_RESPONSE/RETIRE_CONNECTION_ID/MAX_/BLOCKED/STREAM/RESET_STREAM/STOP_SENDING/CONNECTION_CLOSE datagram 可通过 pollProtectedShortDatagram() 发出,并使用调用方提供的 key 通过 processProtectedShortDatagram() 打开;processProtectedShortDatagramOrClose() 及 key-update/installed-key *OrClose 变体会为认证后的 protected plaintext frame 错误排队 CONNECTION_CLOSE。启用时,short-header spin bit 只会在 packet 认证且 Application frame 处理成功后更新。

  • 尚未实现:真实 TLS 1.3 backend 集成、当前使用调用方 key / installed key 的 protected long/short packet bridge 之外的 endpoint datagram API、完整 TLS-owned live 1-RTT key-update 调度与 old-key discard、完整 TLS 0-RTT replay policy、已实现 Initial/HANDSHAKE_DONE hook 之外剩余的 TLS 触发 key discard 调度、transport parameter 的完整 TLS backend transcript ownership、socket-owned protected-packet RFC 9002 loss/PTO timer lifecycle 集成、socket-backed UDP 四元组连接归属、socket-owned endpoint Retry issuance/accept loop、自动 socket-backed 迁移到 server preferred_address、围绕已导出 snapshot 的生产级 endpoint token-secret/replay 存储与分发、绑定真实 IP-header marking 的 ECN validation、自动 socket-backed path validation 与 endpoint path identity 绑定、完整地址验证策略、完整 DCID routing 生命周期、socket 拥有的 connection ID 替换策略、socket-backed unknown-CID stateless reset 发出、Initial key 派生、long-header type-bit 映射、按配置使用 protected long-packet/Retry version、Retry integrity helper、token version binding 和 RFC 9368 version-information primitives 之外的完整 QUIC v2 行为与完整路径迁移策略。

  • 0-RTT 恢复(RFC 8446 §2.2 + RFC 9001 §4.6):纯 Zig TLS 1.3 已实现完整 client 侧密钥链(resumption_master_secret → PSK → early_traffic_secret → binder_key → psk_binder),ClientHello 携带 pre_shared_key + early_data 扩展,CryptoBackend pull_zero_rtt_traffic_secrets hook 暴露 0-RTT 早期流量密钥,Connection 安装 local 0-RTT packet-protection key 并发送 early-data STREAM。server 侧会校验 ClientHello pre_shared_key identity 和 binder vector,且不截断 identity;随后用配置 PSK 派生 early_secret,constant-time 验证 psk_binder,仅验证通过才派生 client early traffic secret 并安装为 peer 0-RTT key;acceptZeroRtt() 显式接受后 processProtectedZeroRttDatagramWithInstalledKeys() 提交 early data。server 在握手完成后发送 post-handshake NewSessionTicket;client 会先校验 handshake length、ticket vector、必需 extensions vector 和本地存储上限,再派生 resumption PSK 并保存 ticket,闭合 0-RTT 恢复的 PSK 来源。rejectZeroRtt() 在使用前丢弃已安装 peer 0-RTT key;client 安装 1-RTT key 时也清理 0-RTT key。

后续阶段会逐步扩展,最终覆盖完整 RFC 范围。