Crate fastwebsockets

Source
Expand description

fastwebsockets is a minimal, fast WebSocket server implementation.

https://github.com/denoland/fastwebsockets

Passes the Autobahn|TestSuite and fuzzed with LLVM’s libfuzzer.

You can use it as a raw websocket frame parser and deal with spec compliance yourself, or you can use it as a full-fledged websocket server.

§Example

use tokio::net::TcpStream;
use fastwebsockets::{WebSocket, OpCode, Role};
use anyhow::Result;

async fn handle(
  socket: TcpStream,
) -> Result<()> {
  let mut ws = WebSocket::after_handshake(socket, Role::Server);
  ws.set_writev(false);
  ws.set_auto_close(true);
  ws.set_auto_pong(true);

  loop {
    let frame = ws.read_frame().await?;
    match frame.opcode {
      OpCode::Close => break,
      OpCode::Text | OpCode::Binary => {
        ws.write_frame(frame).await?;
      }
      _ => {}
    }
  }
  Ok(())
}

§Fragmentation

By default, fastwebsockets will give the application raw frames with FIN set. Other crates like tungstenite which will give you a single message with all the frames concatenated.

For concanated frames, use FragmentCollector:

use fastwebsockets::{FragmentCollector, WebSocket, Role};
use tokio::net::TcpStream;
use anyhow::Result;

async fn handle(
  socket: TcpStream,
) -> Result<()> {
  let mut ws = WebSocket::after_handshake(socket, Role::Server);
  let mut ws = FragmentCollector::new(ws);
  let incoming = ws.read_frame().await?;
  // Always returns full messages
  assert!(incoming.fin);
  Ok(())
}

permessage-deflate is not supported yet.

§HTTP Upgrades

Enable the upgrade feature to do server-side upgrades and client-side handshakes.

This feature is powered by hyper.

use fastwebsockets::upgrade::upgrade;
use http_body_util::Empty;
use hyper::{Request, body::{Incoming, Bytes}, Response};
use anyhow::Result;

async fn server_upgrade(
  mut req: Request<Incoming>,
) -> Result<Response<Empty<Bytes>>> {
  let (response, fut) = upgrade(&mut req)?;

  tokio::spawn(async move {
    let ws = fut.await;
    // Do something with the websocket
  });

  Ok(response)
}

Use the handshake module for client-side handshakes.

use fastwebsockets::handshake;
use fastwebsockets::FragmentCollector;
use hyper::{Request, body::Bytes, upgrade::Upgraded, header::{UPGRADE, CONNECTION}};
use http_body_util::Empty;
use hyper_util::rt::TokioIo;
use tokio::net::TcpStream;
use std::future::Future;
use anyhow::Result;

async fn connect() -> Result<FragmentCollector<TokioIo<Upgraded>>> {
  let stream = TcpStream::connect("localhost:9001").await?;

  let req = Request::builder()
    .method("GET")
    .uri("http://localhost:9001/")
    .header("Host", "localhost:9001")
    .header(UPGRADE, "websocket")
    .header(CONNECTION, "upgrade")
    .header(
      "Sec-WebSocket-Key",
      fastwebsockets::handshake::generate_key(),
    )
    .header("Sec-WebSocket-Version", "13")
    .body(Empty::<Bytes>::new())?;

  let (ws, _) = handshake::client(&SpawnExecutor, req, stream).await?;
  Ok(FragmentCollector::new(ws))
}

// Tie hyper's executor to tokio runtime
struct SpawnExecutor;

impl<Fut> hyper::rt::Executor<Fut> for SpawnExecutor
where
  Fut: Future + Send + 'static,
  Fut::Output: Send + 'static,
{
  fn execute(&self, fut: Fut) {
    tokio::task::spawn(fut);
  }
}

Modules§

handshakeupgrade
Client handshake.
upgradeupgrade
HTTP upgrades.

Structs§

FragmentCollector
Collects fragmented messages over a WebSocket connection and returns the completed message once all fragments have been received.
Frame
Represents a WebSocket frame.
WebSocket
WebSocket protocol implementation over an async stream.

Enums§

CloseCode
Status code used to indicate why an endpoint is closing the WebSocket connection.
OpCode
Payload
Role
WebSocketError

Functions§

unmask
Unmask a payload using the given 4-byte mask.