WebSocket over HTTP/2
At the current time only servers support the handshake procedure defined in RFC-8441.
While HTTP/2 inherently supports full-duplex communication, web browsers typically don’t expose this functionality directly to developers and that is why WebSocket tunneling over HTTP/2 is important.
- Servers can efficiently handle multiple concurrent streams within a single TCP connection
- Client applications can continue using existing WebSocket APIs without modification
For this particular scenario the no-masking parameter defined in https://datatracker.ietf.org/doc/html/draft-damjanovic-websockets-nomasking-02 is also supported.
To use this functionality, it is necessary to activate the http2 and web-socket features.
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},
net::{Stream, Uri},
rng::{ChaCha20, CryptoSeedableRng},
tls::{TlsAcceptor, TlsConfig},
};
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_config = TlsConfig::from_keys_pem(PUBLIC_KEY.try_into()?, &mut rng, SECRET_KEY)?;
let tls_stream = TlsAcceptor::new(tls_config, 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(())
}