Crate fastwebsockets

source ·
Expand description

fastwebsockets is a minimal, fast WebSocket server implementation.

https://github.com/littledivy/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

Structs

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

Enums

Functions

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