HTTP/2
Implementation of RFC7541 and RFC9113. HTTP/2 is the second major version of the Hypertext Transfer Protocol, introduced in 2015 to improve web performance, it addresses limitations of HTTP/1.1 while maintaining backwards compatibility.
Passes the hpack-test-case and the h2spec test suites. Due to official and unofficial deprecations, prioritization and server-push are not supported.
There are a bunch of low-level details that most individuals don’t care about when they are building applications. If that is your case, high level interfaces are available in http2-client-pool or http2-server-framework.
To use this functionality, it is necessary to activate the http2 feature.
HTTP/1.1 Upgrade
Does not support upgrading from HTTP/1.1 because browsers also don’t support such a feature. Connections must be established directly using HTTP/2 (h2c) or via ALPN (Application-Layer Protocol Negotiation) during the TLS handshake.
Operating Modes
There are two distinct operating modes for handling data transmission.
Automatic Mode
The system takes full responsibility. When you provide a buffer of data to be sent, the implementation automatically fragments it into appropriate DATA frames based on the maximum frame size and the current flow control window.
Manual Mode
Allows more control but you should know HTTP/2 concepts and their interactions. In this mode the user is responsible for constructing and sending individual HEADERS, DATA and TRAILERS frames.
Client Example
//! Fetches an URI using low-level HTTP/2 resources.
extern crate tokio;
extern crate wtx;
use tokio::net::TcpStream;
use wtx::{
collections::Vector,
http::{HttpClient, HttpRecvParams, ReqBuilder},
http2::{Http2, Http2Buffer, Http2ErrorCode},
misc::{Uri, from_utf8_basic},
rng::{ChaCha20, CryptoSeedableRng},
stream::Stream,
tls::{TlsConfig, TlsConnector, TlsModeVerified},
};
#[tokio::main]
async fn main() -> wtx::Result<()> {
let uri = Uri::new("https://github.com/c410-f3r/wtx");
let stream = TcpStream::connect(uri.hostname_with_implied_port()).await?;
let mut rng = ChaCha20::from_getrandom()?;
let hb = Http2Buffer::new(&mut rng);
let hrp = HttpRecvParams::with_optioned_params();
let tls_config = TlsConfig::from_ccadb(TlsModeVerified::default())?;
let tcr = TlsConnector::new(tls_config, rng, stream).connect().await?;
let (frame_reader, http2) = Http2::connect(hb, hrp, tcr.tls_stream.into_split()?).await?;
let _jh = tokio::spawn(frame_reader);
let res = http2
.send_req_recv_res(&mut Vector::new(), ReqBuilder::get(uri.to_ref()).into_request())
.await?;
println!("{}", from_utf8_basic(&res.msg_data.body)?);
http2.send_go_away(Http2ErrorCode::NoError).await;
Ok(())
}
Server Example
//! Low-level HTTP/2 server that servers a single response.
extern crate tokio;
extern crate wtx;
extern crate wtx_examples;
use tokio::net::TcpListener;
use wtx::{
collections::Vector,
http::{HttpRecvParams, Response, StatusCode},
http2::{Http2, Http2Buffer, Http2ErrorCode, Http2RecvStatus},
misc::Uri,
rng::{ChaCha20, CryptoSeedableRng},
stream::Stream,
tls::{TlsAcceptor, TlsConfig, TlsModeVerified},
};
use wtx_examples::{PUBLIC_KEY, SECRET_KEY, host_from_args};
#[tokio::main]
async fn main() -> wtx::Result<()> {
let uri = Uri::new(host_from_args());
let listener = TcpListener::bind(uri.hostname_with_implied_port()).await?;
let (stream, _) = listener.accept().await?;
let mut rng = ChaCha20::from_getrandom()?;
let hb = Http2Buffer::new(&mut rng);
let tls_stream = TlsAcceptor::new(
TlsConfig::from_keys_pem(TlsModeVerified::default(), PUBLIC_KEY, SECRET_KEY)?,
rng,
stream,
)
.accept()
.await?
.tls_stream;
let hrp = HttpRecvParams::with_optioned_params();
let (frame_reader, http2) = Http2::accept(hb, hrp, tls_stream.into_split()?).await?;
let _jh = tokio::spawn(frame_reader);
let Some((mut stream, _)) = http2.stream(|_, _| {}).await? else {
println!("Connection closed!");
return Ok(());
};
let (hrs, msg) = stream.recv_req().await?;
if let Http2RecvStatus::ClosedConnection | Http2RecvStatus::ClosedStream(_) = hrs {
println!("Connection or stream closed!");
return Ok(());
}
println!("An arbitrary request has been received: {msg:#?}");
let _ = stream
.send_res(&mut Vector::new(), Response::new(b"By tea, for tea\n", StatusCode::ImATeapot))
.await?;
http2.send_go_away(Http2ErrorCode::NoError).await;
Ok(())
}