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
//! HTTP/2 server that uses optioned parameters. //! //! Automatic stream 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. 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::{ http::{ AutoStream, ManualServerStreamTokio, OperationMode, OptionedServer, ReqResBuffer, Response, StatusCode, is_web_socket_handshake, }, http2::{Http2Buffer, Http2Params, WebSocketOverStream}, misc::{TokioRustlsAcceptor, Vector, 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(), (), (), ), |_| {}, |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.rrd.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(()) }