953869714b
Round 12 — six smaller follow-ups across visibility, perf, and secret-handling. * QuicStream.receiveDirtyForFlowControl gains @Volatile. The parser's read-loop writes the flag and the writer's send-loop reads it WITHOUT holding the same lock; without volatile the writer could miss the parser's update for an unbounded time on JVM (the field is hot in a drain loop where the JIT might cache it), suppressing MAX_STREAM_DATA emissions until something else triggered a fresh load. * SendBuffer.readableBytes runs in O(1) instead of O(R) by maintaining a cached `retransmitTotalBytes` counter. Updated in lockstep with every retransmit deque mutation: addLast in [requeueAllInflight] + the two paths in [removeOverlap] (RETRANSMIT zero-length + main range), and add/removeFirst in [takeChunk]. Pre-flight "anything to send?" check on the writer's hot path was previously walking the deque per-stream per-drain. * SendBuffer.requeueAllInflight coalesces adjacent ranges on insert. Pre-fix the PTO probe path appended each in-flight range as a separate retransmit entry, so on the next drain takeChunk emitted one tiny STREAM frame per original-packet boundary. With coalescing, contiguous bytes get replayed as one chunk + one AEAD seal. FIN-bearing ranges stay separate (merging across a FIN changes the implicit final-size invariant). * TlsResumptionState dropped `data class`. The auto-generated equals/hashCode used reference equality on its ByteArray fields (PSK / ticket / peerTransportParameters), so two byte-identical states compared unequal — almost never useful and a footgun for caller-side caches. The auto-toString would dump PSK contents into any log. Replaced with hand-written equals/hashCode using contentEquals on the byte fields and a redacted toString that reveals only sizes. * QuicConnectionParser RESET_STREAM handler bounds finalSize at [0, 2^62-1] per RFC 9000 §16 (the QUIC offset ceiling). Pre-fix we accepted any varint, including values that could overflow downstream Long math. * PathChallenge/PathResponse IAE leak: re-traced and verified non-issue. The decoder calls `r.readBytes(8)`, which either throws QuicCodecException (short read) or returns exactly 8 bytes — the constructor's `require(data.size == 8)` is unreachable from the decode path. The audit was over-cautious; no code change. All 269 :quic:jvmTest tests pass. BUILD SUCCESSFUL in 39s. https://claude.ai/code/session_01AhGvbMV8uPRse3TmAGaddM