ezsockets

Have you ever had troubles building a WebSocket server or a client in Rust? This crate might come very handy.

Client

NOTE: Enable client feature to use it.

Simplest client that redirects stdin to the WebSocket server can be represented by the following code:

```rust use asynctrait::asynctrait; use ezsockets::BoxError; use ezsockets::ClientConfig; use std::io::BufRead; use url::Url;

struct Client {}

[async_trait]

impl ezsockets::ClientExt for Client { type Message = ();

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

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

async fn closed(&mut self) -> Result<(), BoxError> {
    Ok(())
}

async fn call(&mut self, message: Self::Message) {
    match message {
        () => {}
    }
}

}

[tokio::main]

async fn main() { tracingsubscriber::fmt::init(); let url = format!("ws://127.0.0.1:8080"); let url = Url::parse(&url).unwrap(); let config = ClientConfig::new(url); let (handle, future) = ezsockets::connect(|| 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

NOTE: Enable server-<backend> feature to use it.

To create a simple echo server, you'll need to first define a Session struct Simplest echo server can be represented by the following code:

```rust use asynctrait::asynctrait; use ezsockets::BoxError; use ezsockets::Session;

type SessionID = u16;

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

[async_trait]

impl ezsockets::SessionExt for EchoSession { type ID = SessionID;

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

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

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

} ```

After that, we'll also need a Server struct

```rust use asynctrait::asynctrait; use ezsockets::BoxError; use ezsockets::Server; use ezsockets::Session; use ezsockets::Socket; use std::net::SocketAddr;

struct EchoServer {}

[async_trait]

impl ezsockets::ServerExt for EchoServer { type Message = (); type Session = EchoSession;

async fn accept(
    &mut self,
    socket: Socket,
    address: SocketAddr,
) -> Result<Session, BoxError> {
    let handle = Session::create(
        |handle| EchoSession {
            // use port as the SessionID, since we don't have any other meaningful information about the client
            id: address.port(),
            handle,
        },
        socket,
    );
    Ok(handle)
}

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

async fn message(&mut self, message: Self::Message) {
    match message {
        () => {}
    };
}

} ```

And that's it! We got that, now we need to start the server somehow, take a look at available Server back-ends, for simplest usage, I'd recommend tokio-tungstenite

Server back-ends

tokio-tungstenite

```rust struct MyServer {}

[async_trait]

impl ezsockets::ServerExt for MyServer { // ... }

[tokio::main]

async fn main() { let server = ezsockets::Server::create(|_| MyServer {}).await; ezsockets::tungstenite::run(server, "127.0.0.1:8080") .await .unwrap(); } ```

axum

```rust struct MyServer {}

[async_trait]

impl ezsockets::ServerExt for MyServer { // ... }

[tokio::main]

async fn main() { let server = ezsockets::Server::create(|| MyServer {}).await; let app = axum::Router::new() .route("/websocket", get(websockethandler)) .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 websockethandler( Extension(server): Extension>, ezsocket: Upgrade, ) -> impl IntoResponse { ezsocket.onupgrade(|socket, address| async move { server.accept(socket, address).await; }) } ```

actix-web

Work in progress!