Git Product home page Git Product logo

fastwebsockets's Introduction

Crates.io

Documentation | Benchmarks

fastwebsockets is a fast WebSocket protocol implementation.

Passes the Autobahn|TestSuite1 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 client/server.

use fastwebsockets::{Frame, OpCode, WebSocket};

async fn handle_client(
  mut socket: TcpStream,
) -> Result<(), WebSocketError> {
  handshake(&mut socket).await?;

  let mut ws = WebSocket::after_handshake(socket);
  ws.set_writev(true);
  ws.set_auto_close(true);
  ws.set_auto_pong(true);

  loop {
    let frame = ws.read_frame().await?;

    match frame {
      OpCode::Close => break,
      OpCode::Text | OpCode::Binary => {
        let frame = Frame::new(true, frame.opcode, None, frame.payload);
        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:

let mut ws = WebSocket::after_handshake(socket);
let mut ws = FragmentCollector::new(ws);

let incoming = ws.read_frame().await?;
// Always returns full messages
assert!(incoming.fin);

permessage-deflate is not supported yet.

HTTP Upgrade

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

This feature is powered by hyper.

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

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

  tokio::spawn(async move {
    if let Err(e) = handle_client(fut).await {
      eprintln!("Error in websocket connection: {}", e);
    }
  });

  Ok(response)
}

Use the handshake module for client-side handshakes.

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

async fn connect() -> Result<WebSocket<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(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);
  }
}

Usage with Axum

Enable the Axum integration with features = ["upgrade", "with_axum"] in Cargo.toml.

use axum::{response::IntoResponse, routing::get, Router};
use fastwebsockets::upgrade;
use fastwebsockets::OpCode;
use fastwebsockets::WebSocketError;

#[tokio::main]
async fn main() {
  let app = Router::new().route("/", get(ws_handler));

  let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
  axum::serve(listener, app).await.unwrap();
}

async fn handle_client(fut: upgrade::UpgradeFut) -> Result<(), WebSocketError> {
  let mut ws = fastwebsockets::FragmentCollector::new(fut.await?);

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

  Ok(())
}

async fn ws_handler(ws: upgrade::IncomingUpgrade) -> impl IntoResponse {
  let (response, fut) = ws.upgrade().unwrap();

  tokio::task::spawn(async move {
    if let Err(e) = handle_client(fut).await {
      eprintln!("Error in websocket connection: {}", e);
    }
  });

  response
}

fastwebsockets's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastwebsockets's Issues

Ext traits should not be used in where bounds

The codebase has various where clauses like the following:

pub async fn read_frame(&mut self) -> Result<Frame<'f>, WebSocketError>
where
S: AsyncReadExt + AsyncWriteExt + Unpin,
{

It is recommended that you instead use S: AsyncRead + AsyncWrite + Unpin. Functionally they are equivalent, but avoiding the Ext traits makes more sense semantically and produces better error messages for users of your library.

How to close a websocket connection?

Is there a way to close a websocket connection in the way uWebsockets does or websocket.rs?
in webs-socket.rs case they faev soemthing like ws.close("bye")
in uwebsockets case they have somehting like ws.end(some_code) or ws.close()
i cant find anything in the docs

Splitting connection cannot handle ping

I have been following the example 'echo_server_split' from the examples section, but I am facing an issue when the client connection tries to ping.

The relevant part of code:

    let ws = fut.await?;
    let (rx, mut tx) = ws.split(tokio::io::split);
    let mut rx = FragmentCollectorRead::new(rx);

    loop {
        let frame = match rx
            .read_frame::<_, WebSocketError>(&mut move |_| async {
                unreachable!();
            }).await {
            Ok(frame) => frame,
            Err(e) => {
                println!("Frame Error {:?}", e);
                break;
            }
        };
    }

The code is getting to this unreachable part right after pinging from client. I don't know if there is a different way handling pings after splitting the connection.

I have already tested the connection without the splitting and the ping works.

In my application, I need the split because I use an Arc with RwLock to store only the tx part of the connections.

Backtrace:
thread 'main' panicked at src/main.rs:282:17:
internal error: entered unreachable code
stack backtrace:
0: rust_begin_unwind
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
1: core::panicking::panic_fmt
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
2: core::panicking::panic
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:144:5
3: websocket_server::handle_client::{{closure}}::{{closure}}::{{closure}}
at ./src/main.rs:282:17
4: fastwebsockets::fragment::FragmentCollectorRead::read_frame::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/fastwebsockets-0.7.1/src/fragment.rs:178:34
5: websocket_server::handle_client::{{closure}}
at ./src/main.rs:283:16
6: websocket_server::server_upgrade::{{closure}}::{{closure}}
at ./src/main.rs:370:86
7: tokio::runtime::task::core::Core<T,S>::poll::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/core.rs:328:17
8: tokio::loom::std::unsafe_cell::UnsafeCell::with_mut
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/loom/std/unsafe_cell.rs:16:9
9: tokio::runtime::task::core::Core<T,S>::poll
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/core.rs:317:13
10: tokio::runtime::task::harness::poll_future::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/harness.rs:485:19
11: <core::panic::unwind_safe::AssertUnwindSafe as core::ops::function::FnOnce<()>>::call_once
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panic/unwind_safe.rs:272:9
12: std::panicking::try::do_call
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:552:40
13: __rust_try
14: std::panicking::try
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:516:19
15: std::panic::catch_unwind
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panic.rs:142:14
16: tokio::runtime::task::harness::poll_future
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/harness.rs:473:18
17: tokio::runtime::task::harness::Harness<T,S>::poll_inner
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/harness.rs:208:27
18: tokio::runtime::task::harness::Harness<T,S>::poll
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/harness.rs:153:15
19: tokio::runtime::task::raw::poll
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/raw.rs:271:5
20: tokio::runtime::task::raw::RawTask::poll
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/raw.rs:201:18
21: tokio::runtime::task::LocalNotified::run
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/task/mod.rs:416:9
22: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:700:25
23: tokio::runtime::coop::with_budget
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/coop.rs:107:5
24: tokio::runtime::coop::budget
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/coop.rs:73:5
25: tokio::runtime::scheduler::current_thread::Context::run_task::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:343:43
26: tokio::runtime::scheduler::current_thread::Context::enter
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:404:19
27: tokio::runtime::scheduler::current_thread::Context::run_task
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:343:23
28: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:699:35
29: tokio::runtime::scheduler::current_thread::CoreGuard::enter::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:737:68
30: tokio::runtime::context::scoped::Scoped::set
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/context/scoped.rs:40:9
31: tokio::runtime::context::set_scheduler::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/context.rs:176:26
32: std::thread::local::LocalKey::try_with
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/thread/local.rs:270:16
33: std::thread::local::LocalKey::with
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/thread/local.rs:246:9
34: tokio::runtime::context::set_scheduler
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/context.rs:176:9
35: tokio::runtime::scheduler::current_thread::CoreGuard::enter
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:737:27
36: tokio::runtime::scheduler::current_thread::CoreGuard::block_on
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:646:19
37: tokio::runtime::scheduler::current_thread::CurrentThread::block_on::{{closure}}
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:175:28
38: tokio::runtime::context::runtime::enter_runtime
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/context/runtime.rs:65:16
39: tokio::runtime::scheduler::current_thread::CurrentThread::block_on
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/scheduler/current_thread/mod.rs:167:9
40: tokio::runtime::runtime::Runtime::block_on
at /home/gabrielfonte/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/runtime/runtime.rs:348:47
41: websocket_server::main
at ./src/main.rs:388:5
42: core::ops::function::FnOnce::call_once
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with RUST_BACKTRACE=full for a verbose backtrace.

Zero unsafe usage

Eliminate unsafe code and examine dependencies that use it.

This should have never happened #42, my lazy oversight that gone unnoticed in Deno due to single thread usage but impacted other crate users.

proxy connection problem

I use the same proxy connection connect to websocket, tungstenite works well, but fastwebsockets failed.

when connect to wss://echo.websocket.org, the error is connection closed before message completed;

when connect to wss://ws.okx.com:8443/ws/v5/public, the error is Invalid status code: 400.

code is below:


use std::future::Future;

use anyhow::Result;
use bytes::Bytes;
use fastwebsockets::handshake;
use fastwebsockets::FragmentCollector;
use http_body_util::Empty;
use hyper::header::CONNECTION;
use hyper::header::UPGRADE;
use hyper::rt::{Read, Write};
use hyper::upgrade::Upgraded;
use hyper::Request;
use hyper_util::rt::TokioIo;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;

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);
    }
}

pub async fn connect(
    uri: &str,
    proxy_url: &str,
) -> Result<FragmentCollector<TokioIo<Upgraded>>> {
    let url = uri.parse::<hyper::Uri>()?;
    let host = url.host().unwrap();
    let port = url.port_u16().unwrap_or(443);
    let req = Request::builder()
        .method("GET")
        .uri(uri)
        .header("Host", host)
        .header(UPGRADE, "websocket")
        .header(CONNECTION, "upgrade")
        .header("Sec-WebSocket-Key", handshake::generate_key())
        .header("Sec-WebSocket-Version", "13")
        .body(Empty::<Bytes>::new())?;

    let proxy = proxy_url.parse::<hyper::Uri>()?;
    let proxy_host = proxy.host().unwrap();
    let proxy_port = proxy.port_u16().unwrap_or(7890);
    let tcp_stream = TcpStream::connect(format!("{}:{}", proxy_host, proxy_port)).await?;

    let tcp_stream = tunnel(tcp_stream, host.to_string(), port).await;

    let (ws, _) = handshake::client(&SpawnExecutor, req, tcp_stream).await?;  // both failed at here!
    Ok(FragmentCollector::new(ws))
}

async fn tunnel(mut conn: TcpStream, host: String, port: u16) -> TcpStream {
    let mut buf = format!(
        "CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\nProxy-Connection: Keep-Alive\r\n"
    )
    .into_bytes();

    buf.extend_from_slice(b"\r\n");

    conn.write(&buf).await.unwrap();
    let mut buf = [0; 8192];
    let mut pos = 0;

    loop {
        let n = conn.read(&mut buf[pos..]).await.unwrap();

        if n == 0 {
            panic!("unexpected eof while tunneling");
        }
        pos += n;

        let recvd = &buf[..pos];
        if recvd.starts_with(b"HTTP/1.1 200") || recvd.starts_with(b"HTTP/1.0 200") {
            if recvd.ends_with(b"\r\n\r\n") {
                println!("tunnel {}:{} success", host, port);
                return conn;
            }
            if pos == buf.len() {
                panic!("proxy headers too long for tunnel");
            }
            // else read more
        } else if recvd.starts_with(b"HTTP/1.1 407") {
            panic!("proxy authentication required");
        } else {
            panic!("unsuccessful tunnel");
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::okx2::{connect};
    use fastwebsockets::{Frame, OpCode};

    #[tokio::test]
    async fn depth_connect() {
        let target = "wss://echo.websocket.org";
        // let target = "wss://ws.okx.com:8443/ws/v5/public";
        let proxy = "http://127.0.0.1:7890";
        let mut ws = connect(target, proxy).await.unwrap();

        // ws.write_frame("");
        loop {
            let msg = match ws.read_frame().await {
                Ok(msg) => msg,
                Err(e) => {
                    println!("Error: {}", e);
                    ws.write_frame(Frame::close_raw(vec![].into()))
                        .await
                        .unwrap();
                    break;
                }
            };

            match msg.opcode {
                OpCode::Text => {
                    let payload =
                        String::from_utf8(msg.payload.to_vec()).expect("Invalid UTF-8 data");
                    println!("{:?}", payload);
                }
                OpCode::Close => {
                    break;
                }
                _ => {}
            }
        }
    }
}

`cargo test` fails on `aarch64-unknown-linux-gnu`

This is a teaxyz/cli docker image based off of debian:buster-slim, running on an aarch64 Mac:

$ cargo test
   Compiling fastwebsockets v0.1.2 (/root/fastwebsockets)
error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/bin:/root/.tea/gnome.org/libxml2/v2.10.4/bin:/root/.tea/openssl.org/v1.1.1t/bin:/root/.tea/llvm.org/v14.0.6/bin:/root/.tea/gnu.org/gettext/v0.21.1/bin:/root/.tea/curl.se/v8.0.1/bin:/root/.tea/perl.org/v5.36.0/bin:/root/.tea/libexpat.github.io/v2.5.0/bin:/root/.tea/tea.xyz/gx/cc/v0.1.4/bin:/root/.tea/git-scm.org/v2.40.0/bin:/root/.tea/rust-lang.org/v1.68.2/bin:/root/.tea/rust-lang.org/cargo/v0.69.1/bin:/root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" VSLANG="1033" "cc" "/tmp/rustcUbiYCl/symbols.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.1408h0q61iax08yk.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.1607lai6xs83s25z.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.1nigg8uvy2mkc2g5.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.1riwamvt34amgica.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.1w9a4bfn66bhtozl.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.1wcl906w44snb3hs.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.218kx785cg977nta.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.31lexj7y8jh9x7gm.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.35rka2w5w047cfzg.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.393mumwrlgkwjayu.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.3felql2av83otq8a.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.3j4ca8g2ra9zuf47.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.3jidi70gqbykb5cs.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.3tv13w94cj7xf63c.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.4suj02t77oq80xav.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.4vncuzaq8la54na9.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.54bdmuflb8klrktw.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.5el7n7nqe0wkmizt.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.89ln7xdyfh2kijj.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.8hkkow04kj4flzs.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.k1j5exstacxgmqo.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.n0ueazdmoyl001i.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.wmdykshmpih4akr.rcgu.o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.55sn319sew84hkfu.rcgu.o" "-Wl,--as-needed" "-L" "/root/fastwebsockets/target/debug/deps" "-L" "/root/fastwebsockets/target/debug/build/fastwebsockets-d4a71c0bb8d48c3b/out" "-L" "/root/fastwebsockets/target/debug/gn_out/obj" "-L" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "-Wl,--whole-archive" "-lfastwebsockets_neon" "-Wl,--no-whole-archive" "/root/fastwebsockets/target/debug/deps/librand-f8bc52a19828a115.rlib" "/root/fastwebsockets/target/debug/deps/librand_chacha-d6857c621a19613f.rlib" "/root/fastwebsockets/target/debug/deps/libppv_lite86-a825f293ae63396c.rlib" "/root/fastwebsockets/target/debug/deps/librand_core-53a67aff8ff556be.rlib" "/root/fastwebsockets/target/debug/deps/libgetrandom-b61098756ec1cce5.rlib" "/root/fastwebsockets/target/debug/deps/libsimdutf8-c8b39205320d2710.rlib" "/root/fastwebsockets/target/debug/deps/libutf8-54a514b570021492.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libtest-4f69f6bb1a9bde7f.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libgetopts-998dac2fed4c152c.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libunicode_width-6869cd51aeb054da.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/librustc_std_workspace_std-60f6ad728b38027f.rlib" "/root/fastwebsockets/target/debug/deps/libtokio-300fdfb8be3ea129.rlib" "/root/fastwebsockets/target/debug/deps/libsignal_hook_registry-f3c73d6e406f06d8.rlib" "/root/fastwebsockets/target/debug/deps/libnum_cpus-6a23505251652b03.rlib" "/root/fastwebsockets/target/debug/deps/libsocket2-b9a69c50d914fbd6.rlib" "/root/fastwebsockets/target/debug/deps/libmemchr-29bf6e1006bc4a34.rlib" "/root/fastwebsockets/target/debug/deps/libbytes-0a53648904ddeb2e.rlib" "/root/fastwebsockets/target/debug/deps/libmio-894d6982e9288e43.rlib" "/root/fastwebsockets/target/debug/deps/liblog-505a75cdf9a2a78c.rlib" "/root/fastwebsockets/target/debug/deps/libpin_project_lite-5f22aa57aa11df42.rlib" "/root/fastwebsockets/target/debug/deps/libparking_lot-687ed5fb7eaa4d12.rlib" "/root/fastwebsockets/target/debug/deps/libparking_lot_core-c9b7de6b474f62c2.rlib" "/root/fastwebsockets/target/debug/deps/liblibc-ff6a02487e399d4f.rlib" "/root/fastwebsockets/target/debug/deps/libcfg_if-92098c891ba5528f.rlib" "/root/fastwebsockets/target/debug/deps/libsmallvec-cf060462502ce7d3.rlib" "/root/fastwebsockets/target/debug/deps/liblock_api-ed435f55394c657e.rlib" "/root/fastwebsockets/target/debug/deps/libscopeguard-fca832f9d4563ffd.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libstd-0406cde8cbb0233c.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libpanic_unwind-fcec773cd2373078.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libobject-e13e7ff689a45c49.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libmemchr-5c0d5857635b21f1.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libaddr2line-204e6d63ea2c9536.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libgimli-6e9f1aa966874d8a.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/librustc_demangle-0dcfc30a0b688ff1.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libstd_detect-7ed007c67e0b15ac.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libhashbrown-3685c3f0e78f6880.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libminiz_oxide-f444e770a55bd1c2.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libadler-68c937a2d2894434.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-b498ac4077af72cb.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libunwind-75528367b29b6e35.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libcfg_if-13c5b352a2a1b2c9.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/liblibc-38357e83bc4865f6.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/liballoc-b14f5a30863c1836.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/librustc_std_workspace_core-e38acf84e4ff1c01.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libcore-34fe633817fcf718.rlib" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib/libcompiler_builtins-7da74b9e3e897504.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-znoexecstack" "-L" "/root/.tea/rust-lang.org/v1.68.2/lib/rustlib/aarch64-unknown-linux-gnu/lib" "-o" "/root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro,-znow" "-nodefaultlibs"
  = note: ld.lld: error: undefined symbol: unmask
          >>> referenced by mask.rs:35 (src/mask.rs:35)
          >>>               /root/fastwebsockets/target/debug/deps/fastwebsockets-c0700031a34e2c13.1408h0q61iax08yk.rcgu.o:(fastwebsockets::mask::unmask::h60c7e7487226bf97)
          >>> did you mean: _unmask
          >>> defined in: /root/fastwebsockets/target/debug/build/fastwebsockets-d4a71c0bb8d48c3b/out/libfastwebsockets_neon.a(m_arm64_neon.o)
          clang-14: error: linker command failed with exit code 1 (use -v to see invocation)

rustc target-spec:

$ rustc --print target-spec-json -Z unstable-options
{
  "arch": "aarch64",
  "crt-static-respected": true,
  "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
  "dynamic-linking": true,
  "env": "gnu",
  "features": "+outline-atomics",
  "has-rpath": true,
  "has-thread-local": true,
  "is-builtin": true,
  "llvm-target": "aarch64-unknown-linux-gnu",
  "max-atomic-width": 128,
  "os": "linux",
  "position-independent-executables": true,
  "relro-level": "full",
  "supported-sanitizers": [
    "address",
    "cfi",
    "leak",
    "memory",
    "memtag",
    "thread",
    "hwaddress"
  ],
  "supported-split-debuginfo": [
    "packed",
    "unpacked",
    "off"
  ],
  "target-family": [
    "unix"
  ],
  "target-mcount": "\u0001_mcount",
  "target-pointer-width": "64"
}

Currently, blocking building deno 1.32.4 on aarch64 linux. Interestingly, it builds fine on aarch64 macOS. It's been a long time since I've gotten into the assembler, but since it's injecting the .S file on aarch64, it feels like that's not happening on Linux, somehow, even though I can confirm that the correct build.rs stanza is running.

Any ideas?

Plot uWebSockets for public reference

Would be good to plot uWebSockets in the graph, for reference since Deno representatives use it in public claims:

Currently it performs on par with uWebSockets. Sorry for the lack of detailed information, we'll add benchmarks tomorrow.

I downloaded this repo, built it with --release and I got 40-87% that of uWS in the benchmark, esp. at 1 kB messages the difference is major (see graph below).

Sure, it's high up there on 20 byte messages which is good but it's not really there for anything longer than so. Can be a good point of reference to add uWS in benchmarks and increase message sizes to more than 20 bytes. 1 kB is still 1 single TCP packet in most cases. I would be interested in following the gap over time.

Benchmark Clairity

The benchmark graphs don't have a labled Y-Axis, so it is hard to tell what that is. If its time, then it appears fastwebsockets ir ironically slower. Is it number of packets managed to be sent in some period of time? Even just a note in the page that says "Hey the Y axis is " would be useful if editing the graphs are too hard.

Support futures / async-std traits

By supporting the traits from futures, library users who use async-std (and a few other runtimes that use traits from futures) wouldn't need to pull in Tokio's competing set of async traits. Constantly creating compatibility layers between the Tokio traits and the futures traits becomes tedious after a while.

Error: "Invalid status code" when connecting to a wss server

Hello, is there a working wss client example? Coming from actix-web because it doesn't support connecting to wss servers. This is the code I am using:

async fn connect(host: &str, path: &str) -> Result<FragmentCollector<Upgraded>> {
    let stream = TcpStream::connect(host).await?;

    let req = Request::builder()
        .method("GET")
        .uri(format!("wss://{}{}", host, path))
        .header("Host", host)
        .header(UPGRADE, "websocket")
        .header(CONNECTION, "upgrade")
        .header(
            "Sec-WebSocket-Key",
            fastwebsockets::handshake::generate_key(),
        )
        .header("Sec-WebSocket-Version", "13")
        .body(Body::empty())?;

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

let mut ws = connect("fstream.binance.com:443", "/ws/BTCUSDT@markPrice").await?;
ws.write_frame(Frame::text(fastwebsockets::Payload::Borrowed(b"hi"))).await?;

And this is what I've got:

Error: "Invalid status code"

And one more question, does this ws client support forward proxies like curl? If so, how to use proxies? Because tokio-tungstenite doesn't support proxies.

curl -x http://localhost:8080 https://google.com

Thank you.

Library is not thread safe

Hi there, I think that the library is currently not thread safe as pointed out in the test in #41. The Problem is that the library defines a global variable static mut RECV_BUF: SharedRecv = SharedRecv::null(); that can be accessed from anywhere without any checks. I guess it's up to the maintainers to decide how to change the architecture to fix that (maybe use an individual buffer for every open websocket?).

I would suggest to remove unsafe impl Send for SharedRecv {} until this is fixed.

Reconnecting on disconnect

Hi I was wondering if there is an easy way to automatically start a new connection in case of any error which wasn't opcode quit. Did anyone do that yet? :)

Error in websocket connection: Unexpected EOF

I have setup an upgrade server and client and although my client is able to send text and the server receives it, at the end of any communication the server returns the error shown in the title. Here's the code I'm using.

// server
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), WebSocketError> {    
    let list = TcpListener::bind("127.0.0.1:9001").await.unwrap();
    println!("started listener");

    loop {
        let (stream, _) = list.accept().await?;
        println!("listener accepting connections");

        tokio::spawn(async move {
          let conn_fut = Http::new()
            .serve_connection(stream, service_fn(server_upgrade))
            .with_upgrades();
          println!("started http upgrade service");

          match conn_fut.await {
            Ok(_body) => println!("response received"),
            Err(e) => println!("An error occurred: {:?}", e)
          }
        });
      }
}

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

  tokio::spawn(async move {
    if let Err(e) = handle_client(fut).await {
      eprintln!("Error in websocket connection: {}", e);
    }
  });

  Ok(response)
}

async fn handle_client(
  fut: UpgradeFut,
) -> Result<(), WebSocketError> {
  let mut ws = FragmentCollector::new(fut.await?);

  loop {
    let frame = ws.read_frame().await?;
    match frame.opcode {
      OpCode::Close => break,
      OpCode::Text | OpCode::Binary => {
        println!("writing back {:?}", String::from_utf8(frame.payload.to_vec()));
        ws.write_frame(frame).await?;
      }
      _ => println!("an unexpected opcode occurred")
    }
  }

  Ok(())
}
// client
#[tokio::main(flavor = "current_thread")]
async fn main() {
    println!("start client");
    let mut ws = connect().await.unwrap();
    println!("connection made from client");

    let message = String::from("hello world");
    let binding = message.clone();
    let message = binding.as_bytes();

    println!("start send message");
    let write_result = ws.write_frame(Frame::text(Payload::from(message))).await;
    match write_result {
        Ok(()) => println!("end send message"),
        Err(e) => println!("error writing {}", e)
    }    

    _ = ws.write_frame(Frame::close(1000, &[]));
}

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);
    }
}

#[allow(unused)]
pub async fn connect() -> Result<FragmentCollector<Upgraded>> {
    let stream = TcpStream::connect("127.0.0.1:9001").await?;
    println!("tcpstream connected");

    let req = Request::builder()
        .method("GET")
        .uri("http://127.0.0.1:9001")
        .header("Host", "127.0.0.1:9001")
        .header(UPGRADE, "websocket")
        .header(CONNECTION, "upgrade")
        .header(
            "Sec-Websocket-Key",
            handshake::generate_key()
        )
        .header("Sec-Websocket-Version", "13")
        .body(Body::empty())?;
    println!("request created");

    println!("start handshake");
    let ws_result = handshake::client(&SpawnExecutor, req, stream).await;
    match ws_result {
        Ok((ws, _)) => {
            println!("completed handshake");
            Ok(FragmentCollector::new(ws))
        },
        Err(err) => {
            println!("{}", err);
            Err(err.into())
        }
    }    
}

Error: Invalid status code on examples/tls_client.ts

I've been getting an error "Invalid status code" when I tried to use a tls stream to connect to a websocket endpoint. So I went to run the examples/tls_client.ts directly from this repo and I still am getting the same error. I'm not sure why.

My environment:

uname -a
Darwin MP14.local 22.4.0 Darwin Kernel Version 22.4.0: Mon Mar  6 20:59:28 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T6000 arm64
cargo -V
cargo 1.71.0-nightly (569b648b5 2023-05-05)
pwd
/fastwebsockets
git rev-parse HEAD
0d6f5bd86c94f6ceb8a9410eaa39fac8d7341d99

Cargo run

cargo run --color=always --package fastwebsockets --example tls_client --features=upgrade

    Finished dev [unoptimized + debuginfo] target(s) in 0.06s
     Running `target/debug/examples/tls_client`
Error: Invalid status code

Can't import OpCOde

I can't import OpCode from fastwebsokcets whatver futures i use. i tried the example from the examples folder

proxy

How to connect with proxy? can you give an example?

Thanks.

Both `read_frame` and `write_frame` are `&mut self`?

Why is read mut? How am I supposed to read and write at the same time in a performant way?

Is there some sort of select trickery I'm missing, or is it fundamentally impossible to write if read was called and is waiting on io?

`read_frame` may panic

if read_frame() of Websocket cannot send ping response because of a socket error it will panic the whole server.

self.write_half.write_frame(&mut self.stream, frame).await?;

The read_frame() of WebSocketRead seems to try some error conversions, but then panics anyway?

fastwebsockets/src/lib.rs

Lines 302 to 303 in bd38ea5

let res = send_fn(frame).await;
res.map_err(|e| WebSocketError::SendError(e.into()))?;

This came up in a production environment. I hope this behaviour might be fixed or we will have to use another implementation.

Benchmark is mostly idle at 10 connections

The current run of only 10 connections is not enough to stress the servers, at least not uWS. Here is a list of differences between uWS and fastwebsockets at different number of connections assuming 1 kB messages. 10 connections has the least difference, so it's a natural pick if one wants to convey a minimal diff:

  • at 10 connections the diff is 7%
  • at 100 connections the diff is 9%
  • at 200 connections the diff is 16%
  • at 500 connections the diff is 34%

So it's pretty easy to tell there's some scaling issues that aren't being conveyed with the low count of only 10 connections. This can be improved by using more connections.

Edit: oh wow for 16 kB messages the diff is 56% at 200 connections

Encountering numerous errors when trying to run a TLS client example in a new Rust project

I recently started a new Rust project using the command 'cargo new fastwebsocket'. After modifying the Cargo.toml file, I tried to run a TLS client example that I copied and pasted into main.rs. However, I encountered a number of errors that prevented the program from running correctly.

Here are the steps I took:

  1. I executed 'cargo new fastwebsocket' to start a new project.
  2. I made changes to the Cargo.toml file.
  3. I copied and pasted a TLS client example into main.rs and tried to run it.

I'm currently unsure of how to proceed due to these errors. Any advice or suggestions would be greatly appreciated. Please let me know if any additional information is needed.

Cargo.toml

[package]
name = "fastwebsocket"
version = "0.1.0"
edition = "2021"

[dependencies]
fastwebsockets = "0.4.2"
tokio = { version = "1",  default-features = false, features = ["full"] }
simdutf8 = { version = "0.1.4", optional = true }
hyper = { version = "0.14.26", features = ["http1", "server", "client"], optional = true }
pin-project = { version = "1.0.8", optional = true }
base64 = { version = "0.21.0", optional = true }
sha1 = { version = "0.10.5", optional = true }
utf-8 = "0.7.5"
rand = "0.8.4"
thiserror = "1.0.40"
tokio-rustls = "0.24.0"
rustls-pemfile = "1.0"
assert2 = "0.3.4"
trybuild = "1.0.80"
criterion = "0.4.0"
anyhow = "1.0.71"
webpki-roots = "0.23.0"

[features]
default = ["simd"]
simd = ["simdutf8/aarch64_neon"]
upgrade = ["hyper", "pin-project", "base64", "sha1"]

main.rs

use std::future::Future;
use std::sync::Arc;
use anyhow::Result;
use fastwebsockets::FragmentCollector;
use fastwebsockets::Frame;
use fastwebsockets::OpCode;
use hyper::header::CONNECTION;
use hyper::header::UPGRADE;
use hyper::upgrade::Upgraded;
use hyper::Body;
use hyper::Request;
use tokio::net::TcpStream;
use tokio_rustls::rustls::ClientConfig;
use tokio_rustls::rustls::OwnedTrustAnchor;
use tokio_rustls::TlsConnector;

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);
    }
}

fn tls_connector() -> Result<TlsConnector> {
    let mut root_store = tokio_rustls::rustls::RootCertStore::empty();

    root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
        OwnedTrustAnchor::from_subject_spki_name_constraints(
            ta.subject,
            ta.spki,
            ta.name_constraints,
        )
    }));

    let config = ClientConfig::builder()
        .with_safe_defaults()
        .with_root_certificates(root_store)
        .with_no_client_auth();

    Ok(TlsConnector::from(Arc::new(config)))
}

async fn connect(domain: &str) -> Result<FragmentCollector<Upgraded>> {
    let mut addr = String::from(domain);
    addr.push_str(":9443"); // Port number for binance stream

    let tcp_stream = TcpStream::connect(&addr).await?;
    let tls_connector = tls_connector().unwrap();
    let domain = tokio_rustls::rustls::ServerName::try_from(domain)
        .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid dnsname"))?;

    let tls_stream = tls_connector.connect(domain, tcp_stream).await?;

    let req = Request::builder()
        .method("GET")
        .uri(format!("wss://{}/ws/btcusdt@bookTicker", &addr)) //stream we want to subscribe to
        .header("Host", &addr)
        .header(UPGRADE, "websocket")
        .header(CONNECTION, "upgrade")
        .header(
            "Sec-WebSocket-Key",
            fastwebsockets::handshake::generate_key(),
        )
        .header("Sec-WebSocket-Version", "13")
        .body(Body::empty())?;

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

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
    let domain = "stream.binance.com";
    let mut ws = connect(domain).await?;

    loop {
        let msg = match ws.read_frame().await {
            Ok(msg) => msg,
            Err(e) => {
                println!("Error: {}", e);
                ws.write_frame(Frame::close_raw(vec![].into())).await?;
                break;
            }
        };

        match msg.opcode {
            OpCode::Text => {
                let payload = String::from_utf8(msg.payload.to_vec()).expect("Invalid UTF-8 data");
                // Normally deserialise from json here, print just to show it works
                println!("{:?}", payload);
            }
            OpCode::Close => {
                break;
            }
            _ => {}
        }
    }
    Ok(())
}

cargo run error

error[E0433]: failed to resolve: use of undeclared crate or module `hyper`
 --> src\main.rs:7:5
  |
7 | use hyper::header::CONNECTION;
  |     ^^^^^ use of undeclared crate or module `hyper`

error[E0433]: failed to resolve: use of undeclared crate or module `hyper`
 --> src\main.rs:8:5
  |
8 | use hyper::header::UPGRADE;
  |     ^^^^^ use of undeclared crate or module `hyper`

error[E0433]: failed to resolve: use of undeclared crate or module `hyper`
 --> src\main.rs:9:5
  |
9 | use hyper::upgrade::Upgraded;
  |     ^^^^^ use of undeclared crate or module `hyper`

error[E0432]: unresolved import `hyper`
  --> src\main.rs:10:5
   |
10 | use hyper::Body;
   |     ^^^^^ use of undeclared crate or module `hyper`

error[E0432]: unresolved import `hyper`
  --> src\main.rs:11:5
   |
11 | use hyper::Request;
   |     ^^^^^ use of undeclared crate or module `hyper`

error[E0433]: failed to resolve: use of undeclared crate or module `hyper`
  --> src\main.rs:19:11
   |
19 | impl<Fut> hyper::rt::Executor<Fut> for SpawnExecutor
   |           ^^^^^ use of undeclared crate or module `hyper`

error[E0433]: failed to resolve: could not find `handshake` in `fastwebsockets`
  --> src\main.rs:67:29
   |
67 |             fastwebsockets::handshake::generate_key(),
   |                             ^^^^^^^^^ could not find `handshake` in `fastwebsockets`

error[E0433]: failed to resolve: could not find `handshake` in `fastwebsockets`
  --> src\main.rs:72:35
   |
72 |     let (ws, _) = fastwebsockets::handshake::client(&SpawnExecutor, req, tls_stream).await?;
   |                                   ^^^^^^^^^ could not find `handshake` in `fastwebsockets`

Some errors have detailed explanations: E0432, E0433.
For more information about an error, try `rustc --explain E0432`.
error: could not compile `fastwebsocket` due to 8 previous errors

I'm a beginner in this area, so if possible, could you provide a step-by-step solution that would be easy for a beginner to understand?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.