quiche
Librarycloudflare/quiche
Rust implementation of QUIC and HTTP/3 for high-performance networking.
Overview
quiche is a Rust implementation of the QUIC transport protocol and HTTP/3, offering a low-level API for packet processing and connection state. Applications handle I/O, event loops and timers. It powers Cloudflare's edge HTTP/3, Android DNS-over-HTTP/3 and curl integration.
README Preview
\n\n[](https://crates.io/crates/quiche)\n[](https://docs.rs/quiche)\n[](https://opensource.org/licenses/BSD-2-Clause)\n\n\n[quiche] is an implementation of the QUIC transport protocol and HTTP/3 as\nspecified by the [IETF]. It provides a low level API for processing QUIC packets\nand handling connection state. The application is responsible for providing I/O\n(e.g. sockets handling) as well as an event loop with support for timers.\n\nFor more information on how quiche came about and some insights into its design\nyou can read a [post] on Cloudflare's blog that goes into some more detail.\n\n[quiche]: https://docs.quic.tech/quiche/\n[ietf]: https://quicwg.org/\n[post]: https://blog.cloudflare.com/enjoy-a-slice-of-quic-and-rust/\n\nWho uses quiche?\n----------------\n\n### Cloudflare\n\nquiche powers Cloudflare edge network's [HTTP/3 support][cloudflare-http3]. The\n[cloudflare-quic.com](https://cloudflare-quic.com) website can be used for\ntesting and experimentation.\n\n### Android\n\nAndroid's DNS resolver uses quiche to [implement DNS over HTTP/3][android-http3].\n\n### curl\n\nquiche can be [integrated into curl][curl-http3] to provide support for HTTP/3.\n\n[cloudflare-http3]: https://blog.cloudflare.com/http3-the-past-present-and-future/\n[android-http3]: https://security.googleblog.com/2022/07/dns-over-http3-in-android.html\n[curl-http3]: https://github.com/curl/curl/blob/master/docs/HTTP3.md#quiche-version\n\nGetting Started\n---------------\n\n### Command-line apps\n\nBefore diving into the quiche API, here are a few examples on how to use the\nquiche tools provided as part of the [quiche-apps](apps/) crate. These are not\nsuitable for production environments; see [disclaimers and\nnotes](#disclaimers-and-notes).\n\nAfter cloning the project according to the command mentioned in the [building](#building) section, the client can be run as follows:\n\n```bash\n $ cargo run --bin quiche-client -- https://cloudflare-quic.com/\n```\n\nwhile the server can be run
FAQ (5)
TroubleshootingHow do I fix quiche closing long-running QUIC bulk transfers with FLOW_CONTROL_ERROR even though the sender did not exceed MAX_DATA?
This is caused by quiche issue #2696. A zero-length, non-FIN STREAM frame advances the stream's largest received offset and is charged to connection-level flow control, but RecvBuf::write() discards the empty frame without advancing the stream high-water mark. When real data later reaches that offset, the same bytes are charged again, eventually making valid traffic appear to exceed MAX_DATA. It was reproduced on revision 0b1a89af, typically late in long transfers when large stream IDs/offsets produce an empty STREAM frame that exactly fills remaining packet capacity. Fix the receiver by updating the empty non-FIN early-return path in RecvBuf::write() to synchronize the high-water mark before returning: self.len = cmp::max(self.len, buf.max_off());. Also apply the sender-side fix: when stream-frame emit() returns (0, false), do not encode a STREAM frame; treat this as packet-size-limited rather than application-limited so BBR2 samples are not marked app-limited. Rebuild and redeploy quiche, or upgrade to a release containing the PR for issue #2696. If only a temporary mitigation is possible and both peers are controlled, deploying the sender-side change prevents these redundant empty frames from being generated.
TroubleshootingHow to fix a quiche zero-length STREAM FIN being dropped so the peer never receives EOF?
This is a bug in quiche 0.29.2: SendBuf::is_complete() considers a stream complete when all stream bytes are acknowledged, even if the STREAM frame carrying FIN was never sent or acknowledged. A zero-length stream_send(id, b"", true) at an already-acknowledged offset can therefore be discarded, and the peer never learns the final stream size.
Upgrade to a release containing the fix stream: track FIN acknowledgements separately. The fix tracks FIN acknowledgement independently from acknowledged byte ranges: a stream is retained and the empty FIN remains flushable/retransmittable until a STREAM frame with FIN=true at fin_off is acknowledged.
Temporary workaround when there is final data to send, combine the data and FIN instead of sending an empty FIN afterwards:
// Avoid:
stream_send(id, b"response", false)?;
// ... after the data is acknowledged ...
stream_send(id, b"", true)?;
// Prefer:
stream_send(id, b"response", true)?;
For a genuinely zero-length stream, upgrading or applying the FIN-tracking patch is required because there is no non-empty data frame on which the FIN can be piggybacked.
TroubleshootingHow do I prevent quiche from exceeding the RFC 9000 3x anti-amplification limit?
Do not configure an anti-amplification factor greater than 3. quiche defaults to the RFC-compliant value 3, so applications using the default are safe. If you call config.set_max_amplification_factor(v) in Rust or quiche_config_set_max_amplification_factor() in C, ensure v <= 3; use 3 for the protocol maximum or a smaller value for a stricter limit. Example: config.set_max_amplification_factor(3);. Values such as 4 intentionally allow more than 3 * received_bytes before address validation and violate RFC 9000 §8.1/RFC 9001 §9.3. If maintaining a fork that must enforce compliance, reject or clamp input in the setter, for example self.max_amplification_factor = v.min(3);.
TroubleshootingHow do I fix quiche closing a QUIC connection with PROTOCOL VIOLATION after duplicate RETIRE_CONNECTION_ID frames?
Upgrade quiche to a release containing the fix for issue #1833, or apply/cherry-pick the linked PR and rebuild. If you build from Git, use a quiche revision that includes the duplicate RETIRE_CONNECTION_ID fix.
The correct behavior is:
1. Look up the sequence number in the active connection-ID list first.
2. If the sequence number has already been retired or is unknown, ignore the frame as a valid retransmission.
3. Only retire the ID when it is active.
4. Return a protocol violation only if retiring an active ID leaves no usable connection ID.
In quiche/src/cid.rs, the inner.len() availability check must not run before confirming that the advertised sequence number is currently active. Duplicate RETIRE_CONNECTION_ID frames are permitted by RFC 9000 because the frame can be retransmitted after packet loss.
Temporary workaround: use a patched quiche build, or ensure the peer has additional active connection IDs available so retirement does not exhaust the connection-ID set.
TroubleshootingHow to fix a tokio-quiche listener not releasing its UDP socket after the accept stream is dropped?
This affects tokio-quiche 0.20.0. The router checks accept_sink.is_closed(), but an idle listener does not register a waker for stream closure, so it stays parked on UDP/connection events and retains the socket. Apply the upstream fix that retains and polls the accept channel's Sender::closed() future in the router's poll/select loop; dropping the returned accept stream then wakes the idle listener and allows it to shut down. Use a release containing PR fix(tokio-quiche): wake idle listener on stream drop, or patch tokio-quiche from that PR/build a fixed commit. As a temporary workaround, receiving a UDP packet can wake the parked router so it observes the closed flag, but the patch is the reliable fix.