WebSocket over HTTP/2
At the current time only servers support the handshake procedure defined in RFC8441.
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},
misc::Uri,
rng::{ChaCha20, CryptoSeedableRng},
stream::Stream,
tls::{TlsAcceptor, TlsConfig},
};
use wtx_examples::{LocalTlsMode, 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(LocalTlsMode::default(), PUBLIC_KEY, SECRET_KEY)?,
rng,
stream,
)
.accept()
.await?
.rslt()?
.stream;
let (frame_reader, http2) =
Http2::accept(hb, HttpRecvParams::with_optioned_params(), 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(())
}