Opening a higher-numbered stream implicitly opens lower streams of the
same type, even when no Stream object exists. Accept peer frames for
those local streams while rejecting frames for streams not yet opened.
Apply this check to STOP_SENDING, STREAM, RESET_STREAM, and
MAX_STREAM_DATA, and cover the behavior with transport tests.
Track unreported STOP_SENDING errors independently of writable queue
membership. A stopped stream can be popped from the queue before the
application tries to send and still needs to return StreamStopped.
Keep stopped streams visible to writable polling when connection send
capacity is zero. Collect completed streams after reporting the error.
Also collect completed streams when the receive side finishes without
an application read, such as after stream_shutdown(Read). The send
side is only complete once its FIN is acknowledged, so a queued FIN is
still delivered first.
Co-Authored-By: vendemiat <vicky@geeks.net.np>
SendBuf::is_complete() used acknowledged byte ranges as a proxy for FIN acknowledgement. A zero-length FIN contributes no byte range, so an ACK for an earlier data-only frame is indistinguishable from an ACK for the FIN.
Fixes#2525.
Prevent additions to HTTP/3 driver settings from breaking downstream
crates. Update integration tests to configure settings through a default
value because external crates can no longer use struct literals.
A zero-length non-fin STREAM frame advances the peer's largest received
offset for the stream (RFC 9000 Section 19.8). The receive path charges
connection-level flow control from that offset, but RecvBuf::write()
discarded empty non-fin buffers without recording the new high-water
mark, so the gap up to the frame's offset was charged a second time
when the in-flight data covering it arrived.
The sender produces exactly such frames: send_single encoded a STREAM
frame even when emit() returned zero bytes without fin. That happens
when packet packing leaves exactly header-sized space, which requires
the header to exceed MAX_STREAM_OVERHEAD — a two-byte stream id varint
plus an eight-byte offset varint — so it surfaces on long-running bulk
streams past 1 GiB of transfer.
Between two quiche endpoints the receiver-side inflation accumulates
silently until a legal, contiguous frame appears to exceed MAX_DATA and
the receiver closes the connection with FLOW_CONTROL_ERROR, while the
sender has consumed exactly the credit it was granted. Observed in
long-running bulk transfers over a lossy path at 1-3 spurious
connection kills per hour; instrumented captures showed rx_data
exceeding the sum of per-stream high-water marks by exact whole-frame
multiples.
Fix both halves:
- RecvBuf::write() advances the high-water mark for empty non-fin
buffers before the early return, keeping the flow-control charge and
the stream state in lockstep. This is the load-bearing fix and also
defends against any other sender emitting such frames.
- send_single skips zero-length non-fin frames entirely. The bytes such
a frame references were already charged to tx_data when buffered, so
this is defensive/efficiency behavior: the frame is pure overhead
plus a consistency hazard for receivers. The skip is excluded from
the on_app_limited() transition via stream_data_skipped, since data
is pending and the sender is size-limited on that packet, not
application-limited.
Tests:
- empty_stream_frame expectations updated to the consistent semantics;
the old sequence also accepted a final size below the largest
received offset, which RFC 9000 Section 4.5 forbids and which no real
quiche sender can produce (fin_off >= off_front).
- New flow_control_empty_stream_frame_not_double_counted pipe test
injects an empty forward frame and verifies the charge lands exactly
once; it fails on the previous code with a spurious Error::FlowControl.
- New zero_length_stream_frame_not_sent test seeds a stream's send
state at 2^30 via a #[cfg(test)] seam and sweeps output-buffer sizes
across the header-only window; without the sender fix it fails at
exactly the cap that leaves header-sized space, and it verifies the
skipped data is delivered afterwards.
* Keep draining stream's read offset in sync with empty STREAM frames
An empty non-fin STREAM frame now advances RecvBuf::len, but on a
draining stream the early return skipped the `off = len` update the
buffered path performs. Connection already counts the frame's offset
delta as consumed for a draining stream, so a later RESET_STREAM
credited `final_size - off` a second time.
Also drop the redundant flushable-queue bookkeeping from the sender-side
skip (a stream reaching it is still flushable), restore off_back()'s
misplaced doc comment, and trim the new comments.
* Keep rotating incremental streams when a STREAM frame is skipped
Dropping the whole flushable-queue update from the header-only skip
left the skipped stream at the head of the queue. Since packet space is
also bounded by the congestion window, the same size can recur on every
send() until an ACK arrives, so an incremental stream with a long header
would block other same-urgency incremental streams whose data does fit.
Restore the rotation (the not-flushable branch stays removed: a stream
reaching the skip still has data queued) and add a regression test.
Fixes#2696.
Pick up upstream fixes for undefined behavior in RBTree iterators and
races when mutating atomic links. Quiche uses both for stream priority
queues, so update to the release containing these safety fixes.
Version 0.10 changes the intrusive_adapter! link-field separator. Update
the stream priority adapters to use the new syntax while preserving
their existing behavior.
Upgrade the default boring dependency from 4.3 to 5.2 while retaining
support for consumers pinned to boring 4.19. Use boring-sys build
metadata to select version-specific RPK APIs and test expectations.
Boring 5 enables post-quantum key shares by default, which can split the
ClientHello across multiple Initial packets. Update affected size and
packet-count expectations. Tests that require one Initial disable
post-quantum groups through Config::set_curves_list().
Update the fuzz build to use Boring 5's consolidated unsafe-mode define.
Exercise the minimum supported version and the RPK feature in CI.
BoringSSL's x86 assembly requires SSE2. boring-sys normally supplies
the necessary flags only when the host and target differ, but the cross
i686 image is 32-bit native.
Add target-scoped CFLAGS and CXXFLAGS passthrough entries so the flags
reach the BoringSSL build. These flags work with both boring 4 and 5, so
the preparation can land before the dependency upgrade.
`tests::initial_cwnd` asserts that `tx_cap` falls in `[expected,
expected + 1]` after the handshake. For BBR2 in Startup the
congestion window grows by exactly `bytes_acked` per ACK, so the
post-handshake `tx_cap` equals `initial_cwnd` plus the bytes the
server sent during the handshake that the client has acknowledged.
That total is sensitive to the encoded length of the ACK frame's
`ack_delay` field (a VarInt of microseconds since receipt), which
can shift by a byte between architectures.
On aarch64 and armv7 (under `cross`'s Docker+QEMU) we observe
`tx_cap = expected + 2`, just outside the existing tolerance.
Allow a 4-byte upper-bound tolerance and replace the prior
`TODO understand` comment with an explanation of what the test is
actually measuring.
boring-sys 5.x's build script unconditionally passes
`CMAKE_MSVC_RUNTIME_LIBRARY` for any `target_os == "windows"`. Combined
with cmake's default Windows behaviour, this makes cmake look for
`cl.exe` even on `*-windows-gnu` targets, where only MinGW gcc is
available, and the build fails during compiler detection.
Replace the `bwoodsend/setup-winlibs-action` step with a setup that
mirrors `boring`'s own CI:
- For `x86_64-pc-windows-gnu`, point CC/CXX/include/library at the
MSYS2 toolchain preinstalled at `C:\msys64`.
- For `i686-pc-windows-gnu`, install a 32-bit MSYS2 environment via
`msys2/setup-msys2@v2` (the runner's preinstalled MSYS2 is 64-bit
only) and force `CMAKE_GENERATOR="MinGW Makefiles"` so cmake-rs
doesn't fall back to "MSYS Makefiles" + cl.exe.
Also set `CXXFLAGS=-msse2` for `i686-pc-windows-msvc` to satisfy
BoringSSL's x86 SSE2 requirement on that target.
BoringSSL 5.x emits C++ symbols (vtables, exception personality,
std::optional, etc.) into its static archives. Linking `libquiche.a`
into the C example binaries with `cc` therefore fails with undefined
references to the C++ runtime.
Switch the link step to `$(CXX)` so libc++/libstdc++ is pulled in
automatically. Wrap the source with `-x c ... -x none` to keep the C
language semantics for the .c file and let clang re-detect the
following `.a` archive normally.
Expose the TLS curve preference list through Config and the C API. This
allows callers and tests to disable post-quantum groups when they need a
smaller ClientHello or deterministic handshake packet sizes.
BoringSSL can report handshake success after accepting early data while
the handshake is still in progress. The success path skipped applying
configuration staged by handshake callbacks, so the next call rebuilt
ExData from the original congestion control settings.
Commit 2e67e72c8 ("Allow updates to the recovery config after sending
ACK-only packets") relaxed the recovery reinitialization guard, but
continued to apply callback changes only after Error::Done. Apply those
changes after either non-fatal TLS result.
Select the path marked active when collecting connection statistics as
the iterator order does not guarantee that the first path is active,
which can report telemetry for a stale path after migration.
Store collected stream sequences in separate range sets for each stream
type so adjacent completed streams share a range instead of separate
hash set entries. This reduces retained memory for sequential stream
usage without changing stream lifecycle or credit behavior.
Add exact RangeSet membership checks and cover type separation,
out-of-order collection, implicit gaps, and stream limit rejection.
Run Bonk automatically for same-repository pull requests and allow
authorized organization members to request reviews through comments.
Use GPT-5.6 Sol through the organization AI Gateway credentials and
prevent the reviewer from pushing changes.
Migrate the argument builder, flag actions, and match accessors to the
clap 4 API. Define clap in the workspace so all member crates share the
same dependency version.
Move HPACK Huffman output behind a small writer trait so callers can
reuse the encoder with sinks other than OctetsMut. Keep the existing
OctetsMut API by delegating through the trait implementation and add
tests for custom sink output and error propagation.
This makes the encoder useful for streaming or segmented output where
building one contiguous temporary buffer would be unnecessary (e.g.
for HTTP/2 HEADERS+CONTINUATION frames).
Move the public API tests out of src/lib.rs into octets/tests so
the crate code no longer carries a large inline test module. Keep the
Huffman-specific coverage in its own feature-gated test file.
Newer nightly rustfmt wraps comments differently now, so update the
existing formatting.
Some comments are reworded to improve readability and avoid awkward
wrapping.
Use EVP_AEAD_CTX_new() and EVP_AEAD_CTX_free() so BoringSSL owns the
context layout and cleanup. This removes the copied layout that needed
to track upstream changes.
Follow-up to !2547 (size body_recv_buf to readable length). The buffer is
still sized to the amount readable on the stream, but the upper bound of
the clamp is now configurable via Http3Settings::max_recv_body_buf_size
instead of being hardcoded to BufFactory::MAX_BUF_SIZE (64 KiB).
The default when unset is lowered to 16 KiB (DEFAULT_MAX_BODY_RECV_BUF_SIZE)
so a request that carries a body no longer pins a large allocation for the
life of the stream; a larger streamed body simply reallocates once per
driver read-cycle. Downstream users can override the cap.
Because MIN_BODY_RECV_BUF_SIZE (1 KiB) is a soft floor while the cap is
now configurable, body_recv_buf_size caps the floor by the configured max
(clamp over [min(floor, max), max]) so a cap below the floor still bounds
the allocation and never inverts the clamp range.
Add a project-local opencode skill for preparing quiche GitHub draft
releases from either a release commit hash or an existing release tag.
The helper script validates the quiche version bump, infers the previous
quiche release tag, and creates draft releases with --target when the tag
has not been created yet.
Quiche release PRs are opened before the release tag exists, so check
for a GitHub draft release using the version from quiche/Cargo.toml.
The workflow is scoped to quiche version bumps and uses contents: write
so the token can see draft releases.