Composable WebSockets made easy, for Rust 🦀

Overview

ezsockets

Have you ever struggle with creating a WebSocket server or a client in Rust? This crate is for you.

  • High level abstraction of WebSocket, handling Ping/Pong from both Client and Server.
  • Use of traits to allow declarative and event-based programming.
  • Automatic reconnection of WebSocket Client.

Client

The code below represents simple client that redirects stdin to the WebSocket server.

use async_trait::async_trait;
use ezsockets::ClientConfig;
use std::io::BufRead;
use url::Url;

struct Client {}

#[async_trait]
impl ezsockets::ClientExt for Client {
    type Params = ();

    async fn text(&mut self, text: String) -> Result<(), ezsockets::Error> {
        tracing::info!("received message: {text}");
        Ok(())
    }

    async fn binary(&mut self, bytes: Vec<u8>) -> Result<(), ezsockets::Error> {
        tracing::info!("received bytes: {bytes:?}");
        Ok(())
    }

    async fn call(&mut self, params: Self::Params) -> Result<(), ezsockets::Error> {
        let () = params;
        Ok(())
    }
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();
    let url = Url::parse("ws://localhost:8080/websocket").unwrap();
    let config = ClientConfig::new(url);
    let (handle, future) = ezsockets::connect(|_client| Client { }, config).await;
    tokio::spawn(async move {
        future.await.unwrap();
    });
    let stdin = std::io::stdin();
    let lines = stdin.lock().lines();
    for line in lines {
        let line = line.unwrap();
        tracing::info!("sending {line}");
        handle.text(line).await;
    }
}

Server

To create a simple echo server, you'll need to define a Session struct. The code below represents a simple echo server.

use async_trait::async_trait;
use ezsockets::Session;

type SessionID = u16;

struct EchoSession {
    handle: Session,
    id: SessionID,
}

#[async_trait]
impl ezsockets::SessionExt for EchoSession {
    type ID = SessionID;
    type Args = ();
    type Params = ();

    fn id(&self) -> &Self::ID {
        &self.id
    }

    async fn text(&mut self, text: String) -> Result<(), ezsockets::Error> {
        self.handle.text(text).await; // Send response to the client
        Ok(())
    }

    async fn binary(&mut self, _bytes: Vec<u8>) -> Result<(), ezsockets::Error> {
        unimplemented!()
    }

    async fn call(&mut self, params: Self::Params) -> Result<(), ezsockets::Error> {
        let () = params;
        Ok(())
    }
}

Then, we need to define a Server struct

use async_trait::async_trait;
use ezsockets::Server;
use ezsockets::Session;
use ezsockets::Socket;
use std::net::SocketAddr;

struct EchoServer {}

#[async_trait]
impl ezsockets::ServerExt for EchoServer {
    type Session = EchoSession;
    type Params = ();

    async fn accept(
        &mut self,
        socket: Socket,
        address: SocketAddr,
        _args: (),
    ) -> Result<Session, ezsockets::Error> {
        let id = address.port();
        let session = Session::create(|handle| EchoSession { id, handle }, id, socket);
        Ok(session)
    }

    async fn disconnected(
        &mut self,
        _id: <Self::Session as ezsockets::SessionExt>::ID,
    ) -> Result<(), ezsockets::Error> {
        Ok(())
    }

    async fn call(&mut self, params: Self::Params) -> Result<(), ezsockets::Error> {
        let () = params;
        Ok(())
    }
}

That's all! Now we can start the server. Take a look at the available Server back-ends. For a simple usage, I'd recommend tokio-tungstenite.

Server back-ends

  • tokio-tungstenite, a neat Tokio based WebSocket implementation. However, it does not provide fancy features like routing or authentication.
  • axum, an ergonomic and modular web framework built with Tokio, Tower, and Hyper.
  • actix-web a powerful, pragmatic, and extremely fast web framework for Rust.

tokio-tungstenite

Enable using

ezsockets = { version = "0.3", features = ["tungstenite"] }
struct EchoServer {}

#[async_trait]
impl ezsockets::ServerExt for EchoServer {
    // ...
}

#[tokio::main]
async fn main() {
    let server = ezsockets::Server::create(|_| EchoServer {});
    ezsockets::tungstenite::run(server, "127.0.0.1:8080", |_socket| async move { Ok(()) })
        .await
        .unwrap();
}

axum

Enable using

ezsockets = { version = "0.3", features = ["axum"] }
struct EchoServer {}

#[async_trait]
impl ezsockets::ServerExt for EchoServer {
    // ...
}

#[tokio::main]
async fn main() {
    let server = ezsockets::Server::create(|_| EchoServer {});
    let app = axum::Router::new()
        .route("/websocket", get(websocket_handler))
        .layer(Extension(server.clone()));

    let address = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));

    tokio::spawn(async move {
        tracing::debug!("listening on {}", address);
        axum::Server::bind(&address)
            .serve(app.into_make_service_with_connect_info::<SocketAddr>())
            .await
            .unwrap();
    });

}

async fn websocket_handler(
    Extension(server): Extension<ezsockets::Server>,
    ezsocket: Upgrade,
) -> impl IntoResponse {
    ezsocket.on_upgrade(|socket, address| async move {
        server.accept(socket, address, ()).await;
    })
}

actix-web

Work in progress!

You might also like...
A very-very simple url shortener for Rust

urlshortener-rs A very simple urlshortener for Rust. This library aims to implement as much URL shortener services as possible and to provide an inter

A WebSocket (RFC6455) library written in Rust

Rust-WebSocket Rust-WebSocket is a WebSocket (RFC6455) library written in Rust. Rust-WebSocket provides a framework for dealing with WebSocket connect

An aria2 websocket jsonrpc in Rust.

aria2-ws An aria2 websocket jsonrpc in Rust. Built with tokio. Docs.rs aria2 RPC docs Features Almost all methods and structed responses Auto reconnec

A simple toy websocket client to connect to Bitstamp.net and print the live order book written in Rust.

A simple toy websocket client to connect to Bitstamp.net and print the live order book written in Rust.

Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

A super-easy, composable, web server framework for warp speeds.

warp A super-easy, composable, web server framework for warp speeds. The fundamental building block of warp is the Filter: they can be combined and co

Structured, contextual, extensible, composable logging for Rust
Structured, contextual, extensible, composable logging for Rust

Getting started Introduction FAQ Crate list slog-rs - The Logging for Rust Introduction (please read) slog is an ecosystem of reusable components for

🌱🦀🌱 Trillium is a composable toolkit for building web applications with async rust 🌱🦀🌱

🌱🦀🌱 Trillium is a composable toolkit for building web applications with async rust 🌱🦀🌱

Reusable Reproducible Composable Software
Reusable Reproducible Composable Software

Reusable Reproducible Composable Software Welcome What is this? Fractalide is a free and open source service programming platform using dataflow graph

Composable probability distributions

porco Composable probability distributions. Examples Create simple probability distributions. enum Coin { Heads, Tails, } impl Coin { fn

Wrapped ICP (WICP) - A composable and interoperable wrapped version of ICP.
Wrapped ICP (WICP) - A composable and interoperable wrapped version of ICP.

Wrapped ICP - WICP Wrapped ICP (WICP) is a wrapped version of the IC's native token, ICP. Each WICP will be backed 1:1 with ICP, meaning that 1 WICP w

Composable proof transcripts for public-coin arguments of knowledge
Composable proof transcripts for public-coin arguments of knowledge

Merlin: composable proof transcripts for public-coin arguments of knowledge Merlin is a STROBE-based transcript construction for zero-knowledge proofs

Composable n-gram combinators that are ergonomic and bare-metal fast
Composable n-gram combinators that are ergonomic and bare-metal fast

CREATURE FEATUR(ization) A crate for polymorphic ML & NLP featurization that leverages zero-cost abstraction. It provides composable n-gram combinator

A collection of lower-level libraries for composable network services.

Actix Net A collection of lower-level libraries for composable network services. Example See actix-server/examples and actix-tls/examples for some bas

Composable Alternatives to Bevy's RunCriteria, States, FixedTimestep

Composable Alternatives to Bevy's RunCriteria, States, FixedTimestep This crate offers alternatives to the Run Criteria, States, and FixedTimestep sch

Lightweight, event-driven WebSockets for Rust.

WS-RS Lightweight, event-driven WebSockets for Rust. /// A WebSocket echo server listen("127.0.0.1:3012", |out| { move |msg| { out.send(ms

Synchronized state machines for Rust over WebSockets.

Aper is a framework for real-time sharing of application state over WebSockets.

Autogenerated async RPC bindings that instantly connect a JS frontend to a Rust backend service via WebSockets and WASM.

Turbocharger Autogenerated async RPC bindings that instantly connect a JS frontend to a Rust backend service via WebSockets and WASM. See https://gith

Rust + wasm + websockets

This is a template repo for eframe, a framework for writing apps using egui.

Comments
  • fix: make some client & server methods synchronous

    fix: make some client & server methods synchronous

    The binary, text, and call methods are just calling a synchronous function and returns immediately, so there's no apparent reason to make them async function as it is. (unless you have plans for the future and want your API to be like this already to match your future plans)

    opened by StyMaar 1
  • random panic if client disconnects non gracefully

    random panic if client disconnects non gracefully

    thread 'tokio-runtime-worker' panicked at 'called Result::unwrap() on an Err value: SendError

    session.rs:99:33

        /// Calls a method on the session
        pub async fn call(&self, params: P) {
            self.calls.send(params).unwrap();
        }
    

    I assume that .unwrap()'s in many places can cause panics which makes websocket server not able to handle further connections.

    opened by stepan-romankov 1
  • Question: can this be used to send json files back and forth?

    Question: can this be used to send json files back and forth?

    I’m very new to Rust (i just recently finished my Hello World program) but i am looking for a basic server/client system that can send JSON files back and forth for compliance checks of endpoints.

    basicly the server will send the client a list of checks to perform (atm registry checks since thats what im learning) which the client performs and then sends back the results. The server will then store those results in a (postgresql) database where a web client then can grab the results and build piecharts of it.

    Something like that :)

    opened by jhjacobs81 1
Releases(v0.1.0)
Owner
Grzegorz Baranski
17yo software developer and a high school student
Grzegorz Baranski
Synchronized state machines for Rust over WebSockets.

Aper is a framework for real-time sharing of application state over WebSockets.

null 139 Dec 2, 2022
Rust + wasm + websockets

This is a template repo for eframe, a framework for writing apps using egui.

Emil Ernerfeldt 12 Oct 3, 2022
Rust API connector for Bybit's WebSockets APIs.

rust-bybit English | 简体中文 Unofficial Rust API connector for Bybit's WebSockets APIs. Disclaimer This is an unofficial Rust API connector for Bybit's A

yufuquant 12 Nov 12, 2022
Command-line client for WebSockets, like netcat (or curl) for ws:// with advanced socat-like functions

websocat Netcat, curl and socat for WebSockets. Examples Connect to public echo server $ websocat ws://echo.websocket.org 123 123 ABC ABC Serve and c

Vitaly Shukela 4.9k Dec 2, 2022
Clearly a repo about websockets and their comparison...

Tags Go vs Typescript: Video 1 of the series is tag go-vs-ts-video-1 Current Project Video 2 and 3 are going to be likely Go vs Rust vs Typescript, bu

ThePrimeagen 327 Nov 24, 2022
😎 A custom invoke system for Tauri that leverages WebSockets

?? tauri-awesome-rpc This is a crate provides a custom invoke system for Tauri using a localhost JSON RPC WebSocket. Each message is delivered through

Victor Aremu 19 Nov 25, 2022
ChatApp made using the standard library net module and tui-rs.

chatui Simple chat application. You'll need both chatui_server and chatui_client to run this application. Installation With cargo cargo install chatui

Gauravsingh Sisodia 6 Dec 15, 2021
SockJS server for rust language

SockJS server SockJS server for Actix framework. API Documentation Cargo package: sockjs SockJS is built with Actix web Minimum supported Rust version

Actix 63 Oct 7, 2022
A WebSocket (RFC6455) library written in Rust

Rust-WebSocket Note: Maintainership of this project is slugglish. You may want to use tungstenite or tokio-tungstenite instead. Rust-WebSocket is a We

Rust Websockets 1.2k Dec 5, 2022
Lightweight stream-based WebSocket implementation for Rust.

Tungstenite Lightweight stream-based WebSocket implementation for Rust. use std::net::TcpListener; use std::thread::spawn; use tungstenite::server::ac

Snapview GmbH 1.2k Dec 1, 2022