Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

  1. Servers can efficiently handle multiple concurrent streams within a single TCP connection
  2. 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(())
}