Expand description
🥧 Savoury implementation of the QUIC transport protocol and HTTP/3.
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.
§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)?;
In both cases, the application is responsible for generating a new source connection ID that will be used to identify the new connection.
The application also need to pass the address of the remote peer of the connection: in the case of a client that would be the address of the server it is trying to connect to, and for a server that is the address of the client that initiated the connection.
§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(quiche::Error::Done) => {
// Done reading.
break;
},
Err(e) => {
// An error occurred, handle it.
break;
},
};
}
The application has to pass a RecvInfo
structure in order to provide
additional information about the received packet (such as the address it
was received from).
§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();
}
The application will be provided with a SendInfo
structure providing
additional information about the newly created packet (such as the address
the packet should be sent to).
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.
§Congestion Control
The quiche library provides a high-level API for configuring which congestion control algorithm to use throughout the QUIC connection.
When a QUIC connection is created, the application can optionally choose
which CC algorithm to use. See CongestionControlAlgorithm
for currently
available congestion control algorithms.
For example:
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
config.set_cc_algorithm(quiche::CongestionControlAlgorithm::Reno);
Alternatively, you can configure the congestion control algorithm to use by its name.
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
config.set_cc_algorithm_name("reno").unwrap();
Note that the CC algorithm should be configured before calling connect()
or accept()
. Otherwise the connection will use a default CC algorithm.
§Feature flags
quiche defines a number of feature flags to reduce the amount of compiled code and dependencies:
-
boringssl-vendored
(default): Build the vendored BoringSSL library. -
boringssl-boring-crate
: Use the BoringSSL library provided by the boring crate. It takes precedence overboringssl-vendored
if both features are enabled. -
pkg-config-meta
: Generate pkg-config metadata file for libquiche. -
ffi
: Build and expose the FFI API. -
qlog
: Enable support for the qlog logging format.
Modules§
- HTTP/3 wire protocol and QPACK implementation.
Structs§
- Stores configuration shared between multiple connections.
- A QUIC connection.
- Represents information carried by
CONNECTION_CLOSE
frames. - A QUIC connection ID.
- A QUIC packet’s header.
- Statistics about the path of a connection.
- Ancillary information about incoming packets.
- Ancillary information about outgoing packets.
- An iterator over SocketAddr.
- Statistics about the connection.
- An iterator over QUIC streams.
- QUIC Transport Parameters
Enums§
- Available congestion control algorithms.
- A QUIC error.
- A path-specific event.
- Qlog
Level qlog
Qlog logging level. - The side of the stream to be shut down.
- QUIC packet type.
- QUIC error codes sent on the wire.
Constants§
- The maximum length of a connection ID.
- The minimum length of Initial packets sent by a client.
- The current QUIC wire version.
Functions§
- Creates a new server-side connection.
- Creates a new client-side connection.
- Writes a version negotiation packet.
- Writes a stateless retry packet.
- Returns true if the given protocol version is supported.
Type Aliases§
- A specialized
Result
type for quiche operations.