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

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 http-client-pool or http-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 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;
extern crate wtx_instances;

use tokio::net::TcpStream;
use wtx::{
  http::{HttpClient, Method, ReqResBuffer},
  http2::{Http2Buffer, Http2ErrorCode, Http2Params, Http2Tokio},
  misc::{Uri, from_utf8_basic},
  rng::{Xorshift64, simple_seed},
};

#[tokio::main]
async fn main() -> wtx::Result<()> {
  let uri = Uri::new("SOME_URI");
  let (frame_reader, mut http2) = Http2Tokio::connect(
    Http2Buffer::new(&mut Xorshift64::from(simple_seed())),
    Http2Params::default(),
    TcpStream::connect(uri.hostname_with_implied_port()).await?.into_split(),
  )
  .await?;
  let _jh = tokio::spawn(frame_reader);
  let res = http2.send_recv_single(Method::Get, ReqResBuffer::empty(), &uri).await?;
  println!("{}", from_utf8_basic(&res.rrd.body)?);
  http2.send_go_away(Http2ErrorCode::NoError).await;
  Ok(())
}

Server Example

//! Low-level HTTP/2 server that uses optioned parameters.
//!
//! Automatic streams are handled by the system while manual stream are handled by the user. In
//! this particular example all manual streams are considered to be WebSocket connections over
//! HTTP/2.
//!
//! Please note that it is much easier to just use the HTTP server framework.

extern crate tokio;
extern crate tokio_rustls;
extern crate wtx;
extern crate wtx_instances;

use tokio::{io::WriteHalf, net::TcpStream};
use tokio_rustls::server::TlsStream;
use wtx::{
  collection::Vector,
  http::{
    AutoStream, ManualServerStreamTokio, OperationMode, OptionedServer, ReqResBuffer, Response,
    StatusCode, is_web_socket_handshake,
  },
  http2::{Http2Buffer, Http2Params, WebSocketOverStream},
  misc::TokioRustlsAcceptor,
  rng::{Xorshift64, simple_seed},
  web_socket::{Frame, OpCode},
};

#[tokio::main]
async fn main() -> wtx::Result<()> {
  OptionedServer::http2_tokio(
    (
      TokioRustlsAcceptor::without_client_auth()
        .http2()
        .build_with_cert_chain_and_priv_key(wtx_instances::CERT, wtx_instances::KEY)?,
      &wtx_instances::host_from_args(),
      (),
      (),
    ),
    |_| Ok(()),
    |acceptor, stream| async move { Ok(tokio::io::split(acceptor.accept(stream).await?)) },
    |error| eprintln!("{error}"),
    |_| {
      Ok((
        (),
        Http2Buffer::new(&mut Xorshift64::from(simple_seed())),
        Http2Params::default()
          .set_enable_connect_protocol(true)
          .set_max_hpack_len((128 * 1024, 128 * 1024)),
      ))
    },
    |_| Ok((Vector::new(), ReqResBuffer::empty())),
    |_, _, protocol, req, _| {
      Ok((
        (),
        if is_web_socket_handshake(&req.rrd.headers, req.method, protocol) {
          OperationMode::Manual
        } else {
          OperationMode::Auto
        },
      ))
    },
    |error| eprintln!("{error}"),
    auto,
    manual,
  )
  .await
}

async fn auto(
  _: (),
  mut ha: AutoStream<(), Vector<u8>>,
) -> Result<Response<ReqResBuffer>, wtx::Error> {
  ha.req.clear();
  Ok(ha.req.into_response(StatusCode::Ok))
}

async fn manual(
  _: (),
  mut hm: ManualServerStreamTokio<(), Http2Buffer, Vector<u8>, WriteHalf<TlsStream<TcpStream>>>,
) -> Result<(), wtx::Error> {
  let rng = Xorshift64::from(simple_seed());
  hm.req.rrd.headers.clear();
  let mut wos = WebSocketOverStream::new(&hm.req.rrd.headers, false, rng, hm.stream).await?;
  loop {
    let mut frame = wos.read_frame(&mut hm.stream_aux).await?;
    match (frame.op_code(), frame.text_payload()) {
      (_, Some(elem)) => println!("{elem}"),
      (OpCode::Close, _) => break,
      _ => {}
    }
    wos.write_frame(&mut Frame::new_fin(OpCode::Text, frame.payload_mut())).await?;
  }
  wos.close().await?;
  Ok(())
}