@Ryrenz: When the signal is down to one bar and you can still load web pages, it’s thanks to things like quiche—Cloudflare has f…
Summary
Cloudflare has open-sourced quiche, a Rust implementation of the QUIC transport protocol and HTTP/3, used in their edge network and adopted by Android and curl for enhanced network performance.
View Cached Full Text
Cached at: 09/20/26, 09:20 AM
📡 When the signal is down to one bar and you can still load web pages, it’s thanks to things like quiche—Cloudflare has fully open-sourced their implementation of QUIC and HTTP/3 running on their edge network.
18.1K stars on GitHub, Android’s system DNS resolver uses it for DNS over HTTP/3, and curl can plug into it to support HTTP/3 too—this kind of endorsement is more solid than just star counts.
The most frustrating part under weak network conditions is that old TCP setup: lose one packet, and every stream on the entire connection grinds to a halt; switching from WiFi to 4G just straight-up breaks and forces a reconnect. QUIC fixes these issues from the ground up, and quiche’s job is to implement this protocol as a library you can embed into your own service—it only handles packets and connection states, leaving I/O and event loops for you to write yourself, so it’s not picky about runtime environments and can be slotted into existing services without touching the architecture.
The core is written in Rust, with a slim C interface exposed for external use, so C, C++, or any other language that can do FFI can call it. In the Config, you can tweak parameters like QUIC version, ALPN, flow control, congestion control, idle timeouts, and more. It comes with two command-line tools, quiche-client and quiche-server, but the docs explicitly say they’re just for debugging—don’t use them to handle production traffic.
The chunk of code powering Cloudflare’s edge network is right there now for anyone to check out.
GitHub:
cloudflare/quiche
Source: https://github.com/cloudflare/quiche
quiche is an implementation of the QUIC transport protocol and HTTP/3 as specified by the IETF. It provides a low level API for processing QUIC packets and handling connection state. The application is responsible for providing I/O (e.g. sockets handling) as well as an event loop with support for timers.
For more information on how quiche came about and some insights into its design you can read a post on Cloudflare’s blog that goes into some more detail.
Who uses quiche?
Cloudflare
quiche powers Cloudflare edge network’s HTTP/3 support. The cloudflare-quic.com website can be used for testing and experimentation.
Android
Android’s DNS resolver uses quiche to implement DNS over HTTP/3.
curl
quiche can be integrated into curl to provide support for HTTP/3.
Getting Started
Command-line apps
Before diving into the quiche API, here are a few examples on how to use the quiche tools provided as part of the quiche-apps crate. These are not suitable for production environments; see disclaimers and notes.
After cloning the project according to the command mentioned in the building section, the client can be run as follows:
$ cargo run --bin quiche-client -- https://cloudflare-quic.com/
while the server can be run as follows:
$ cargo run --bin quiche-server -- --cert apps/src/bin/cert.crt --key apps/src/bin/cert.key
(note that the certificate provided is self-signed and should not be used in production)
Use the --help command-line flag to get a more detailed description of each
tool’s options.
Configuring connections
The first step in establishing a QUIC connection using quiche is creating a
Config object:
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
config.set_application_protos(&[b"example-proto"]);
// Additional configuration specific to application and use case...
The Config object controls important aspects of the QUIC connection such
as QUIC version, ALPN IDs, flow control, congestion control, idle timeout
and other properties or features.
QUIC is a general-purpose transport protocol and there are several configuration properties where there is no reasonable default value. For example, the permitted number of concurrent streams of any particular type is dependent on the application running over QUIC, and other use-case specific concerns.
quiche defaults several properties to zero, applications most likely need to set these to something else to satisfy their needs using the following:
set_initial_max_streams_bidi()set_initial_max_streams_uni()set_initial_max_data()set_initial_max_stream_data_bidi_local()set_initial_max_stream_data_bidi_remote()set_initial_max_stream_data_uni()
Config also holds TLS configuration. This can be changed by mutators on
the an existing object, or by constructing a TLS context manually and
creating a configuration using with_boring_ssl_ctx_builder().
A configuration object can be shared among multiple connections.
Connection setup
On the client-side the connect() utility function can be used to create
a new connection, while accept() is for servers:
// Client connection.
let conn = quiche::connect(Some(&server_name), &scid, local, peer, &mut config)?;
// Server connection.
let conn = quiche::accept(&scid, None, local, peer, &mut config)?;
Handling incoming packets
Using the connection’s recv() method the application can process
incoming packets that belong to that connection from the network:
let to = socket.local_addr().unwrap();
loop {
let (read, from) = socket.recv_from(&mut buf).unwrap();
let recv_info = quiche::RecvInfo { from, to };
let read = match conn.recv(&mut buf[..read], recv_info) {
Ok(v) => v,
Err(e) => {
// An error occurred, handle it.
break;
},
};
}
Generating outgoing packets
Outgoing packet are generated using the connection’s send() method
instead:
loop {
let (write, send_info) = match conn.send(&mut out) {
Ok(v) => v,
Err(quiche::Error::Done) => {
// Done writing.
break;
},
Err(e) => {
// An error occurred, handle it.
break;
},
};
socket.send_to(&out[..write], &send_info.to).unwrap();
}
When packets are sent, the application is responsible for maintaining a
timer to react to time-based connection events. The timer expiration can be
obtained using the connection’s timeout() method.
let timeout = conn.timeout();
The application is responsible for providing a timer implementation, which
can be specific to the operating system or networking framework used. When
a timer expires, the connection’s on_timeout() method should be called,
after which additional packets might need to be sent on the network:
// Timeout expired, handle it.
conn.on_timeout();
// Send more packets as needed after timeout.
loop {
let (write, send_info) = match conn.send(&mut out) {
Ok(v) => v,
Err(quiche::Error::Done) => {
// Done writing.
break;
},
Err(e) => {
// An error occurred, handle it.
break;
},
};
socket.send_to(&out[..write], &send_info.to).unwrap();
}
Pacing
It is recommended that applications pace sending of outgoing packets to avoid creating packet bursts that could cause short-term congestion and losses in the network.
quiche exposes pacing hints for outgoing packets through the [at] field
of the [SendInfo] structure that is returned by the send() method.
This field represents the time when a specific packet should be sent into
the network.
Applications can use these hints by artificially delaying the sending of
packets through platform-specific mechanisms (such as the SO_TXTIME
socket option on Linux), or custom methods (for example by using user-space
timers).
Sending and receiving stream data
After some back and forth, the connection will complete its handshake and will be ready for sending or receiving application data.
Data can be sent on a stream by using the stream_send() method:
if conn.is_established() {
// Handshake completed, send some data on stream 0.
conn.stream_send(0, b"hello", true)?;
}
The application can check whether there are any readable streams by using
the connection’s readable() method, which returns an iterator over all
the streams that have outstanding data to read.
The stream_recv() method can then be used to retrieve the application
data from the readable stream:
if conn.is_established() {
// Iterate over readable streams.
for stream_id in conn.readable() {
// Stream is readable, read until there's no more data.
while let Ok((read, fin)) = conn.stream_recv(stream_id, &mut buf) {
println!("Got {} bytes on stream {}", read, stream_id);
}
}
}
HTTP/3
The quiche HTTP/3 module provides a high level API for sending and receiving HTTP requests and responses on top of the QUIC transport protocol.
Have a look at the [quiche/examples/] directory for more complete examples on how to use the quiche API, including examples on how to use quiche in C/C++ applications (see below for more information).
Calling quiche from C/C++
quiche exposes a thin C API on top of the Rust API that can be used to more easily integrate quiche into C/C++ applications (as well as in other languages that allow calling C APIs via some form of FFI). The C API follows the same design of the Rust one, modulo the constraints imposed by the C language itself.
When running cargo build, a static library called libquiche.a will be
built automatically alongside the Rust one. This is fully stand-alone and can
be linked directly into C/C++ applications.
Note that in order to enable the FFI API, the ffi feature must be enabled (it
is disabled by default), by passing --features ffi to cargo.
Building
quiche requires Rust 1.88 or later to build. The latest stable Rust release can be installed using rustup.
Once the Rust build environment is setup, the quiche source code can be fetched using git:
$ git clone https://github.com/cloudflare/quiche
and then built using cargo:
$ cargo build --examples
cargo can also be used to run the testsuite:
$ cargo test
Note that BoringSSL, which is used to implement QUIC’s cryptographic handshake
based on TLS, needs to be built and linked to quiche. This is done automatically
by the boring-sys crate when building with cargo, but requires the cmake
command to be available during the build process.
On Windows NASM is also required. The official BoringSSL documentation has more details.
In alternative you can use your own custom build of BoringSSL by configuring
the BoringSSL directory with the BORING_BSSL_PATH environment variable:
$ BORING_BSSL_PATH="/path/to/boringssl" cargo build --examples
Building for Android
Building quiche for Android (NDK version 19 or higher, 21 recommended), can be done using cargo-ndk (v2.0 or later).
First the Android NDK needs to be installed, either using Android Studio or
directly, and the ANDROID_NDK_HOME environment variable needs to be set to the
NDK installation path, e.g.:
$ export ANDROID_NDK_HOME=/usr/local/share/android-ndk
Then the Rust toolchain for the Android architectures needed can be installed as follows:
$ rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
Note that the minimum API level is 21 for all target architectures.
cargo-ndk (v2.0 or later) also needs to be installed:
$ cargo install cargo-ndk
Finally the quiche library can be built using the following procedure. Note that
the -t <architecture> and -p <NDK version> options are mandatory.
$ cargo ndk -t arm64-v8a -p 21 -- build --features ffi
See build_android_ndk19.sh for more information.
Building for iOS
To build quiche for iOS, you need the following:
- Install Xcode command-line tools. You can install them with Xcode or with the following command:
$ xcode-select --install
- Install the Rust toolchain for iOS architectures:
$ rustup target add aarch64-apple-ios x86_64-apple-ios
- Install
cargo-lipo:
$ cargo install cargo-lipo
To build libquiche, run the following command:
$ cargo lipo --features ffi
or
$ cargo lipo --features ffi --release
iOS build is tested in Xcode 10.1 and Xcode 11.2.
Building Docker images
In order to build the Docker images, simply run the following command:
$ make docker-build
You can find the quiche Docker images on the following Docker Hub repositories:
The latest tag will be updated whenever quiche master branch updates.
cloudflare/quiche
Provides a server and client installed in /usr/local/bin.
cloudflare/quiche-qns
Provides the script to test quiche within the quic-interop-runner.
Disclaimers and Notes
⚠️ This repository includes a number of client and server example applications that are provided to demonstrate simple usage of the quiche library API. They are not intended to be used in production environments; no performance, security or reliability guarantees are provided.
Copyright
Copyright (C) 2018-2019, Cloudflare, Inc.
See COPYING for the license.
Similar Articles
cloudflare/quiche
quiche is a Rust-based implementation of the QUIC transport protocol and HTTP/3, providing a low-level API for processing QUIC packets and managing connections. It is used by Cloudflare for HTTP/3 support, Android's DNS resolver, and can be integrated into curl.
When "idle" isn't idle: how a Linux kernel optimization became a QUIC bug
Cloudflare details a bug in their QUIC implementation, quiche, where a Linux kernel optimization for CUBIC congestion control caused performance issues, and describes the resulting fix.
@ayushagarwal027: Cloudflare spent 6 weeks chasing a bug in the hyper Rust HTTP library. The fix was 4 lines. The symptom: image response…
Cloudflare engineers spent six weeks debugging a race condition in the hyper Rust HTTP library that caused silent data truncation in large image responses. The root cause was a single `let _ =` discarding a `Poll::Pending` signal during flush; the fix was a four-line change now merged upstream.
Using QUIC backscatter to infer hypergiant deployment configurations
Researchers use QUIC backscatter from network telescopes to infer retransmission configurations of Google, Meta, and Cloudflare servers, revealing deployment details despite QUIC’s privacy aims.
@paulmillr: Cool! SSHing personal devices is now more relevant than ever. https://try.cloudflare.com
Cloudflare's Quick Tunnels allow developers to instantly expose a local server to the internet with a secure, encrypted URL using a simple CLI command, optimized for coding agents and webhooks without setup.