30 releases
0.9.2 | Oct 8, 2024 |
---|---|
0.9.0 | Sep 4, 2024 |
0.8.1 | Jul 22, 2024 |
0.7.2 | Mar 13, 2024 |
0.5.4 | Nov 29, 2021 |
#12 in #ietf
1,836 stars & 37 watchers
740KB
16K
SLoC
The HTTP/3 protocol
This crate implements RFC9114.
The implementation depends on:
- neqo-transport --- implements the QUIC protocol (RFC9000) and
- neqo-qpack --- implements QPACK (RFC9204);
Features
Both client and server-side HTTP/3 protocols are implemented, although the server-side implementation is not meant to be used in production and its only purpose is to facilitate testing of the client-side code.
WebTransport
(draft version 2) is
supported and can be enabled using Http3Parameters
.
Interaction with an application
Driving HTTP/3 session
The crate does not create an OS level UDP socket, it produces, i.e. encodes, data that should be
sent as a payload in a UDP packet and consumes data received on the UDP socket. For example,
std::net::UdpSocket
or mio::net::UdpSocket
could be used for creating UDP sockets.
The application is responsible for creating a socket, polling the socket, and sending and receiving data from the socket.
In addition to receiving data HTTP/3 session’s actions may be triggered when a certain amount of time passes, e.g. after a certain amount of time data may be considered lost and should be retransmitted, packet pacing requires a timer, etc. The implementation does not use timers, but instead informs the application when processing needs to be triggered.
The core functions for driving HTTP/3 sessions are:
- On the client-side :
process_output
used for producing UDP payload. If a payload is not produced this function returns a callback time, e.g. the time whenprocess_output
should be called again.process_input
used consuming UDP payload.process
combines the 2 functions into one, i.e. it consumes UDP payload if available and produces some UDP payload to be sent or returns a callback time.- On the server-side only
process
is available.
An example interaction with a socket:
let socket = match UdpSocket::bind(local_addr) {
Err(e) => {
eprintln!("Unable to bind UDP socket: {}", e);
}
Ok(s) => s,
};
let mut client = Http3Client::new(...);
...
// process_output can return 3 values, data to be sent, time duration when process_output should
// be called, and None when Http3Client is done.
match client.process_output(Instant::now()) {
Output::Datagram(dgram) => {
// Send dgram on a socket.
socket.send_to(&dgram[..], dgram.destination())
}
Output::Callback(duration) => {
// the client is idle for “duration”, set read timeout on the socket to this value and
// poll the socket for reading in the meantime.
socket.set_read_timeout(Some(duration)).unwrap();
}
Output::None => {
// client is done.
}
};
...
// Reading new data coming for the network.
match socket.recv_from(&mut buf[..]) {
Ok((sz, remote)) => {
let d = Datagram::new(remote, *local_addr, &buf[..sz]);
client.process_input(d, Instant::now());
}
Err(err) => {
eprintln!("UDP error: {}", err);
}
}
HTTP/3 session events
Http3Client
and Http3Server
produce
events that can be obtain by calling
next_event
. The events are of type
Http3ClientEvent
and
Http3ServerEvent
respectively. They are informing the application
when the connection changes state, when new data is received on a stream, etc.
...
while let Some(event) = client.next_event() {
match event {
Http3ClientEvent::DataReadable { stream_id } => {
println!("New data available on stream {}", stream_id);
}
Http3ClientEvent::StateChange(Http3State::Connected) => {
println!("Http3 session is in state Connected now");
}
_ => {
println!("Unhandled event {:?}", event);
}
}
}
Dependencies
~4.5MB
~94K SLoC