Files
amethyst/quic/src
Claude 8f5280c9c3 fix(quic): WebTransport peer-stream demux + flow-control + perf cleanup
Round-2 audit found that without prefix-stripping on peer-initiated streams,
the server's HTTP/3 control stream would deliver its SETTINGS frame to the
application, breaking MoQ framing. Plus several flow-control + perf items
the audit flagged.

WtPeerStreamDemux:
  Spawned per WT session in QuicWebTransportSessionState.init. Routes peer
  streams by their leading varint(s):
    - HTTP/3 CONTROL stream (0x00) → drains internally, captures peer
      SETTINGS frame for inspection (peerSettings property).
    - QPACK encoder/decoder streams (0x02/0x03) → drained to /dev/null
      (we run with QPACK_MAX_TABLE_CAPACITY=0).
    - WebTransport unidirectional (0x54) + matching quarter session id →
      surfaces to app as a StrippedWtStream with the prefix stripped.
    - WebTransport bidi signal (0x41) + matching quarter session id → same.
    - Anything else → dropped per RFC 9114 §9.

  pollIncomingPeerStream is now @Deprecated; the recommended path is the
  incomingStrippedStreams flow on the session state. The nestsClient adapter
  now consumes the stripped flow and emits StrippedWtReadStreamAdapter
  through the existing WebTransportSession.incomingUniStreams API.

WT_CLOSE_SESSION decoder + CapsuleReader:
  Added stateful capsule reader that yields parsed WtCloseSession
  (errorCode + reason) from the CONNECT bidi. Wires into the future close
  notification path; not yet bound to status flip but the parser is in
  place.

Flow-control fixes from the audit:
  - Per-direction receive window in getOrCreatePeerStreamLocked. SERVER_UNI
    streams now use initialMaxStreamDataUni instead of bidi-remote.
  - appendFlowControlUpdates picks per-direction window matching the
    stream's StreamId.kindOf.
  - MAX_DATA tracking via QuicConnection.advertisedMaxData high-water mark.
    Previously the writer emitted a fresh MAX_DATA on every outbound
    packet once the threshold was crossed (spam). Now: only when the new
    value strictly exceeds advertisedMaxData.

Stream iteration round-robin:
  Writer was iterating streamsLocked() in insertion order, starving streams
  created later under MTU pressure. Added streamRoundRobinStart counter on
  QuicConnection that advances after each drain.

SendBuffer alias defense:
  takeChunk's fast path used to return the head ByteArray reference directly
  when consuming the whole chunk — caller-owned byte arrays could be mutated
  in-place. Now always copies, since downstream encoders pass the bytes to
  AEAD.seal which assumes immutability.

UdpSocket cleanup:
  - readBuf shrunk from 65 KiB to 2 KiB. QUIC datagrams cap at MTU; the old
    65 KiB allocation was per-connection waste with no benefit.
  - Removed pointless synchronized(readBuf) — only the read loop touches it.

All :quic:jvmTest + :nestsClient:jvmTest pass.

https://claude.ai/code/session_01EC1tfXfap8k8GyKvrxkxZx
2026-04-25 22:49:32 +00:00
..