Crate tokio_websockets

source ·
Expand description

tokio-websockets

High performance, strict, tokio-util based websockets implementation.

Notice

This crate has not been tested enough to an extent that I would deem it ready for usage in production enviroments of big projects. Try it before deploying and please give feedback!

Why use tokio-websockets?

  • Built with tokio-util, intended to be used with tokio from the ground up
  • Minimal dependencies: The base only requires:
    • tokio, tokio-util, bytes, futures-util (which almost all tokio projects depend on)
    • fastrand
    • SHA1 backend, e.g. sha1_smol (see Feature flags)
  • Big selection of features to tailor dependencies to any project (see Feature flags)
  • SIMD support: AVX2 or SSE2 for frame (un)masking and accelerated UTF-8 validation
  • Strict conformance with the websocket specification, passes the Autobahn test suite without relaxations by default (some can be enabled for performance)
  • TLS support
  • Reusable TLS connectors
  • Uses widely known crates from the ecosystem for types, for example Uri from http in the client
  • Cheaply clonable messages due to Bytes as payload storage
  • Tuned for performance: no unnecessary duplicate UTF-8 validation, no duplicate bounds checking (this however heavily uses unsafe code, which is sound to my knowledge, if not, open an issue!)

Feature flags

Feature flags in tokio-websockets are added to allow tailoring it to your needs.

  • simd will enable AVX2 and SSE2 accelerated masking and UTF-8 validation
  • client enables a tiny client implementation
  • server enables a tiny server implementation
  • http-integration enables a method for http::Request upgrade generation

TLS support is supported via any of the following feature flags:

One SHA1 implementation is required, usually provided by the TLS implementation:

  • ring is used if rustls is the TLS library
  • The openssl feature will use openssl, usually prefered on most Linux/BSD systems with native-tls
  • The sha1_smol feature can be used as a fallback if no TLS is needed

For these reasons, I recommend disabling default features and using a configuration that makes sense for you, for example:

tokio-websockets = { version = "*", default-features = false, features = ["client", "sha1_smol"] }
tokio-websockets = { version = "*", default-features = false, features = ["client", "simd", "rustls-webpki-roots"] }

Example

This is a simple websocket echo server without any proper error handling.

More examples can be found in the examples folder.

use futures_util::SinkExt;
use http::Uri;
use tokio::net::TcpListener;
use tokio_websockets::{ClientBuilder, Error, Message, ServerBuilder};

#[tokio::main]
async fn main() -> Result<(), Error> {
  let listener = TcpListener::bind("127.0.0.1:3000").await?;

  tokio::spawn(async move {
    while let Ok((stream, _)) = listener.accept().await {
      let mut ws_stream = ServerBuilder::new()
        .accept(stream)
        .await?;

      tokio::spawn(async move {
        // Just an echo server, really
        while let Some(Ok(msg)) = ws_stream.next().await {
          if msg.is_text() || msg.is_binary() {
            ws_stream.send(msg).await?;
          }
        }

        Ok::<_, Error>(())
      });
    }

    Ok::<_, Error>(())
  });

  let uri = Uri::from_static("ws://127.0.0.1:3000");
  let mut client = ClientBuilder::from_uri(uri).connect().await?;

  client.send(Message::text(String::from("Hello world!"))).await?;

  while let Some(Ok(msg)) = client.next().await {
    if let Ok(text) = msg.as_text() {
      assert_eq!(text, "Hello world!");
      // We got one message, just stop now
      client.close(None, None).await?;
    }
  }

  Ok(())
}

Caveats / Limitations / ToDo

Currently, WebsocketStream does not implement Stream due to the poll_next nature of the trait, which makes implementing it with actual async code near impossible.

I am waiting for async traits and will implement it once possible. Until then, a method called next already exists and serves as a replacement for futures-util’s next, which most users were probably looking for.

Further, websocket compression is currently unsupported.

Re-exports

pub use client::Builder as ClientBuilder;
pub use error::Error;
pub use proto::CloseCode;
pub use proto::Message;
pub use proto::OpCode;
pub use proto::WebsocketStream;
pub use server::Builder as ServerBuilder;
pub use tls::Connector;
pub use tls::MaybeTlsStream;
pub use self::http::upgrade_request;

Modules

clientclient
Implementation of a websocket client.
General error type used in the crate.
httphttp-integration and client
Helper for creating http::Request objects for HTTP/1.1 Upgrade requests with websocket servers.
This module contains a correct and complete implementation of RFC6455.
serverserver
Implementation of a websocket server.
Wrapper types for TLS functionality, abstracting over rustls and native-tls connector and stream types.