Giter Site home page Giter Site logo

1c3t3a / rust-socketio Goto Github PK

View Code? Open in Web Editor NEW
393.0 4.0 68.0 1.14 MB

An implementation of a socket.io client written in the Rust programming language.

License: MIT License

Rust 96.38% JavaScript 2.27% Makefile 0.23% Dockerfile 0.13% Shell 0.98%
hacktoberfest rust socket-io engine-io

rust-socketio's Introduction

Latest Version docs.rs Build and code style Test codecov

Rust-socketio-client

An implementation of a socket.io client written in the rust programming language. This implementation currently supports revision 5 of the socket.io protocol and therefore revision 4 of the engine.io protocol. If you have any connection issues with this client, make sure the server uses at least revision 4 of the engine.io protocol. Information on the async version can be found below.

Example usage

Add the following to your Cargo.toml file:

rust_socketio = "*"

Then you're able to run the following example code:

use rust_socketio::{ClientBuilder, Payload, RawClient};
use serde_json::json;
use std::time::Duration;

// define a callback which is called when a payload is received
// this callback gets the payload as well as an instance of the
// socket to communicate with the server
let callback = |payload: Payload, socket: RawClient| {
       match payload {
           Payload::String(str) => println!("Received: {}", str),
           Payload::Binary(bin_data) => println!("Received bytes: {:#?}", bin_data),
       }
       socket.emit("test", json!({"got ack": true})).expect("Server unreachable")
};

// get a socket that is connected to the admin namespace
let socket = ClientBuilder::new("http://localhost:4200")
     .namespace("/admin")
     .on("test", callback)
     .on("error", |err, _| eprintln!("Error: {:#?}", err))
     .connect()
     .expect("Connection failed");

// emit to the "foo" event
let json_payload = json!({"token": 123});
socket.emit("foo", json_payload).expect("Server unreachable");

// define a callback, that's executed when the ack got acked
let ack_callback = |message: Payload, _| {
    println!("Yehaa! My ack got acked?");
    println!("Ack data: {:#?}", message);
};

let json_payload = json!({"myAckData": 123});
// emit with an ack
socket
    .emit_with_ack("test", json_payload, Duration::from_secs(2), ack_callback)
    .expect("Server unreachable");

socket.disconnect().expect("Disconnect failed")

The main entry point for using this crate is the ClientBuilder which provides a way to easily configure a socket in the needed way. When the connect method is called on the builder, it returns a connected client which then could be used to emit messages to certain events. One client can only be connected to one namespace. If you need to listen to the messages in different namespaces you need to allocate multiple sockets.

Documentation

Documentation of this crate can be found up on docs.rs.

Current features

This implementation now supports all of the features of the socket.io protocol mentioned here. It generally tries to make use of websockets as often as possible. This means most times only the opening request uses http and as soon as the server mentions that he is able to upgrade to websockets, an upgrade is performed. But if this upgrade is not successful or the server does not mention an upgrade possibility, http-long polling is used (as specified in the protocol specs). Here's an overview of possible use-cases:

  • connecting to a server.
  • register callbacks for the following event types:
    • open
    • close
    • error
    • message
    • custom events like "foo", "on_payment", etc.
  • send JSON data to the server (via serde_json which provides safe handling).
  • send JSON data to the server and receive an ack.
  • send and handle Binary data.

This library provides an ability for being executed in an asynchronous context using tokio as the execution runtime. Please note that the current async implementation is still experimental, the interface can be object to changes at any time. The async Client and ClientBuilder support a similar interface to the sync version and live in the asynchronous module. In order to enable the support, you need to enable the async feature flag:

rust_socketio = { version = "*", features = ["async"] }

The following code shows the example above in async fashion:

use futures_util::FutureExt;
use rust_socketio::{
    asynchronous::{Client, ClientBuilder},
    Payload,
};
use serde_json::json;
use std::time::Duration;

#[tokio::main]
async fn main() {
    // define a callback which is called when a payload is received
    // this callback gets the payload as well as an instance of the
    // socket to communicate with the server
    let callback = |payload: Payload, socket: Client| {
        async move {
            match payload {
                Payload::String(str) => println!("Received: {}", str),
                Payload::Binary(bin_data) => println!("Received bytes: {:#?}", bin_data),
            }
            socket
                .emit("test", json!({"got ack": true}))
                .await
                .expect("Server unreachable");
        }
        .boxed()
    };

    // get a socket that is connected to the admin namespace
    let socket = ClientBuilder::new("http://localhost:4200/")
        .namespace("/admin")
        .on("test", callback)
        .on("error", |err, _| {
            async move { eprintln!("Error: {:#?}", err) }.boxed()
        })
        .connect()
        .await
        .expect("Connection failed");

    // emit to the "foo" event
    let json_payload = json!({"token": 123});
    socket
        .emit("foo", json_payload)
        .await
        .expect("Server unreachable");

    // define a callback, that's executed when the ack got acked
    let ack_callback = |message: Payload, _: Client| {
        async move {
            println!("Yehaa! My ack got acked?");
            println!("Ack data: {:#?}", message);
        }
        .boxed()
    };

    let json_payload = json!({"myAckData": 123});
    // emit with an ack
    socket
        .emit_with_ack("test", json_payload, Duration::from_secs(2), ack_callback)
        .await
        .expect("Server unreachable");

    socket.disconnect().await.expect("Disconnect failed");
}

Content of this repository

This repository contains a rust implementation of the socket.io protocol as well as the underlying engine.io protocol.

The details about the engine.io protocol can be found here:

The specification for the socket.io protocol here:

Looking at the component chart, the following parts are implemented (Source: https://socket.io/images/dependencies.jpg):

Licence

MIT

rust-socketio's People

Contributors

1c3t3a avatar colin1860 avatar ctrlaltf24 avatar dependabot-preview[bot] avatar dependabot[bot] avatar felix-gohla avatar george-keyrock avatar ghamza-jd avatar haukeh avatar marekvospel avatar maxohn avatar ollipal avatar rageshkrishna avatar serg06 avatar shenjackyuanjie avatar sirkrypt0 avatar ssebo avatar tyilo avatar veerapatyok avatar zetanumbers avatar

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

rust-socketio's Issues

How can I use this with tokio?

My code runs in a tokio runtime with #[tokio::main(flavor = "multi_thread", worker_threads = 4)]
connecting to a server as shown in the examples causes

thread 'main' panicked at 'Cannot drop a runtime in a context where blocking is not allowed. This happens when a runtime is dropped from within an asynchronous context.', /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.12.0/src/runtime/blocking/shutdown.rs:51:21
stack backtrace:
   0: std::panicking::begin_panic
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa/library/std/src/panicking.rs:543:12
   1: tokio::runtime::blocking::shutdown::Receiver::wait
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.12.0/src/runtime/blocking/shutdown.rs:51:21
   2: tokio::runtime::blocking::pool::BlockingPool::shutdown
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.12.0/src/runtime/blocking/pool.rs:145:12
   3: <tokio::runtime::blocking::pool::BlockingPool as core::ops::drop::Drop>::drop
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.12.0/src/runtime/blocking/pool.rs:162:9
   4: core::ptr::drop_in_place<tokio::runtime::blocking::pool::BlockingPool>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa/library/core/src/ptr/mod.rs:188:1
   5: core::ptr::drop_in_place<tokio::runtime::Runtime>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa/library/core/src/ptr/mod.rs:188:1
   6: reqwest::blocking::wait::enter
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwest-0.11.7/src/blocking/wait.rs:76:21
   7: reqwest::blocking::wait::timeout
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwest-0.11.7/src/blocking/wait.rs:13:5
   8: reqwest::blocking::client::ClientHandle::new
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwest-0.11.7/src/blocking/client.rs:996:15
   9: reqwest::blocking::client::ClientBuilder::build
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwest-0.11.7/src/blocking/client.rs:103:9
  10: reqwest::blocking::client::Client::new
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwest-0.11.7/src/blocking/client.rs:798:9
  11: rust_engineio::transports::polling::PollingTransport::new
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/rust_engineio-0.3.1/src/transports/polling.rs:36:29
  12: rust_engineio::client::client::ClientBuilder::handshake
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/rust_engineio-0.3.1/src/client/client.rs:142:25
  13: rust_engineio::client::client::ClientBuilder::build
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/rust_engineio-0.3.1/src/client/client.rs:153:9
  14: rust_engineio::client::client::ClientBuilder::build_with_fallback
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/rust_engineio-0.3.1/src/client/client.rs:262:22
  15: rust_socketio::client::builder::ClientBuilder::connect_manual
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/rust_socketio-0.3.1/src/client/builder.rs:256:35
  16: rust_socketio::client::builder::ClientBuilder::connect
             at /home/samthomas/.cargo/registry/src/github.com-1ecc6299db9ec823/rust_socketio-0.3.1/src/client/builder.rs:217:22
  17: sf_edge_gateway::send::start_send::{{closure}}

The connection does happen from within an async function(# 17) . Any suggestions on how to approach this? I see that reqwest is trying to create its own tokio runtime internally beacuse this library uses a blocking version.

Server Implementation Checklist

Development on hold, someone else is welcome to take over

It's that time @1c3t3a , might want to make a branch. How servers are implemented informs discussion about what the best interface method is (for 0.3.X).

TBD:

  • Polling server added
  • Websocket server added
  • Socket implemented
  • Introduce tokio v1 internally
    • DO NOT expose async functions
    • Interface should still be synchronous externally.
  • Tests written
    • Unit tests
    • Integration tests against rust_socketio client
    • Integration tests for client against server
    • Integration tests against nodejs reference impl
    • Performance tests
    • Maintain coverage
  • Interface written

add function like "query" in rust-socketio?

maybe this:
var client = io("https:xxx.com/",
transport = [websocket],
query = {"uuid" = ''});
// this become "wss:xxx.com/socket.io/?uuid=" in websocket url.

add a function like "query" in rust-socketio?

thread '<unnamed>' panicked at 'EngineIO Error'

I have the exact same issue as closed issue #166. Are there any updates? I am not sure why it was closed.

EDIT: Something else I noticed it that I don't receive events from the server anymore after some time.

wasm support

I'm looking for a socket io client crate that works on both wasm and native. Is this something you'd want to add?

Switch websocket library

This library currently uses the websocket crate. However the library looks kind of abandoned and might not be the best fit for future plans like async / await support. Currently the library provides async support based on tokio 0.1. This could lead to problems and there are no real plans to get the support running with websocket (see #242). It's also not to be expected that the library will provide support in case we have a problem, so switching the library might be a good idea. A battle proven implementation is provided by tungstenite-rs (async here, blocking here). What do you think @nshaaban-cPacket?

Timeouts

Is it possible to set a timeout on an emitted event?
I didn't see anything about timeouts in the documentation.

How to return a value from a callback with emit_with_ack?

Hi!

Maybe I am asking for something really simple, but I have some lack of experience with callbacks in Rust. Consider the following code:

let ack_callback = |message: Payload, _| {
    println!("Yehaa! My ack got acked?");
    println!("Ack data: {:#?}", message);

    message.to_string() // I want to return the result from the server to the caller
};

let json_payload = json!({"myAckData": 123});
// emit with an ack
let ack = socket
    .emit_with_ack("test", json_payload, Duration::from_secs(2), ack_callback)
    .expect("Server unreachable");

// Here I want somehow use the result from ack_callback

So the idea is simple: a user sends a request to the server and receives some valuable answer from a server. With the current design, it's a little bit unclear to me, what is the best way to return a result to the user. Try to use channels? Pass an Arc<Mutex<Option<ResultType>>> to the callback and on the caller, side wait with a busy loop a result? Anything else? Did I miss some useful pattern here?

If you are interested, what I am trying to do - I am trying to port this piece of JS library to Rust: https://github.com/dplusic/GameLift-Nodejs-ServerSDK/blob/master/src/Server/AuxProxyMessageSender.ts#L173

I hope my question is clear :) Thanks in advance!

Add a server implementation

Until now this crate only provides a socketio client. A possible server implementation could use the same packet parser and maybe parts of the engine.io socket.

improve existing test coverage

this issue is meant to stand in as a low-priority to-do item for eventually
moving the test cases from original source files into a tests/ directory, it
may also be used to track the coverage of tests up until then and other
immediate testing related issues not specific to GH or CI/CD

How to join/leave a socket.io room

Hi there,
I am studying the rust-socketio crate. In my case, I need to join a socket.io room and leave this room when something happened,
so I want to known, how to join and leave a special socket.io room, I didn't found the rust functions.

Thanks for a lot.

Deserialization of Handshake is failing

๐Ÿ‘‹๐Ÿผ hello!

I've started experimenting with the crate this weekend and I'm having trouble integrating with a socketio server written in php. The Error event is being triggered on every connection. I believe I've narrowed the error down to this section:

// the response contains the handshake data
if let Ok(conn_data) = serde_json::from_str::<HandshakeData>(&response[1..]) {
self.connected.store(true, Ordering::Release);
// check if we could upgrade to websockets
let websocket_upgrade = conn_data
.upgrades
.iter()
.any(|upgrade| upgrade.to_lowercase() == *"websocket");
// if we have an upgrade option, send the corresponding request, if this doesn't work
// for some reason, proceed via polling
if websocket_upgrade {
let _ = self.upgrade_connection(&conn_data.sid);
}
// set the connection data
let mut connection_data = self.connection_data.write()?;
*connection_data = Some(conn_data);
drop(connection_data);
// call the on_open callback
let function = self.on_open.read()?;
if let Some(function) = function.as_ref() {
spawn_scoped!(function(()));
}
drop(function);
// set the last ping to now and set the connected state
*self.last_ping.lock()? = Instant::now();
// emit a pong packet to keep trigger the ping cycle on the server
self.emit(Packet::new(PacketId::Pong, Bytes::new()), false)?;
return Ok(());
}
let error = Error::HandshakeError(response);
self.call_error_callback(format!("{}", error))?;
return Err(error);

What I'm seeing locally is this:

HandshakeError("\u{0}\u{1}\u{0}\u{1}๏ฟฝ0{\"sid\":\"c7f0f5c5b72fd8410429e568\",\"upgrades\":[\"websocket\"],\"pingInterval\":25000,\"pingTimeout\":60000}")

I can't tell why there's garbage in front of the JSON payload, but I'm able to connect to this same socketio server with a .NET implementation.

Support handler for any custom event

In Javascript client that works like:

socket.on('*', handler);

Without this it is required for the client to always know all events that the server can emit and set up separate handlers to all of them.

0.3.X Checklist

0.3.X:

  • finalize socketio iter interface? or postpone until 0.4.0?
  • All public types implement Debug, Sync, Send
    • Data types implement Eq and Clone

autoreconect socketio

How does the socket autoreconnect work,
or someone who does it implemented payment for the implementation thanks

Project Examples?

Has anyone used this for a project and would like to share an example for me that is a little more complex than the provided one? I need it for a project I'm working on but I can't figure out how to use the .on() functions to modify data that is scoped outside of it. I may be dumb, as my experience with rust is only novel but I've looked all over and don't see a way around this. For example, if I were to create a snake game and wanted to use this library for multiplayer how could I modify the snake struct inside of a socket .on method. I really just need one example of modifying data that isn't scoped to the .on function and ill be able to make my project work.

  let mut snake = Snake{
        x: 1,
        y: 1,
    };
    ClientBuilder::new("http:://localhost:3001")
        .namespace("/")
        .on("update", |p,c|{
            //get the position from the payload: Payload,
            let new_pos_x = 4;
            let new_pos_y = 4;
            //this causes a error since the closure could outlive snake
            snake.x = new_pos_x;
            snake.y = new_pos_y;
        })
        .on("error", |err, _| eprintln!("Error: {:#?}", err))
        .connect();

ps. This is just very very simple code I wrote to show what I'm talking about this is not how I would be actually structuring it.

Add multiple transport layers

The documentation talks about different 'layers' for transporting which are:

  • websockets
  • long-polling

I currently implemented long-polling, but feel free to extend the transport enum in engineio/transport.rs.

String payload doesn't have proper decoded utf-8

My string payload contains weird characters for returned unicode symbols.

For example I should receive 'โ„ข', I am getting 'รข๏ฟฝยข', and for 'โ˜…' I am getting 'รข๏ฟฝ๏ฟฝ'.

I've tried everything but with no success so I am now manually replacing them. I am pretty new to rust and to low level programming, so I might be doing something wrong.

EngineIO Panic

I a using your great library, but my applications panics after some time (seems to be random ~10-30 secs after startup) with the following backtrace:

thread '<unnamed>' panicked at 'EngineIO Error', /home/mawoka/.cargo/registry/src/github.com-1ecc6299db9ec823/rust_socketio-0.3.0/src/client/builder.rs:229:21
stack backtrace:
   0:     0x557646368a8c - std::backtrace_rs::backtrace::libunwind::trace::hd70f18a67bf1064d
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:     0x557646368a8c - std::backtrace_rs::backtrace::trace_unsynchronized::hed700f39aaa9560e
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x557646368a8c - std::sys_common::backtrace::_print_fmt::h05ffc8c800d3fd6e
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/sys_common/backtrace.rs:66:5
   3:     0x557646368a8c - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hc3335dc9ac9ea141
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/sys_common/backtrace.rs:45:22
   4:     0x5576463904dc - core::fmt::write::h8ba7e47d56fb9287
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/core/src/fmt/mod.rs:1190:17
   5:     0x557646362018 - std::io::Write::write_fmt::hcc4602e4a7d8cb4e
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/io/mod.rs:1657:15
   6:     0x55764636ace7 - std::sys_common::backtrace::_print::hfefb27db9027fc13
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/sys_common/backtrace.rs:48:5
   7:     0x55764636ace7 - std::sys_common::backtrace::print::h3b5c5f5af201c47a
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/sys_common/backtrace.rs:35:9
   8:     0x55764636ace7 - std::panicking::default_hook::{{closure}}::h9364b8e096329e42
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/panicking.rs:295:22
   9:     0x55764636a99f - std::panicking::default_hook::hbb3fd2f25c08d7b9
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/panicking.rs:314:9
  10:     0x55764636b43b - std::panicking::rust_panic_with_hook::hb8806bff47a676e3
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/panicking.rs:698:17
  11:     0x55764636b127 - std::panicking::begin_panic_handler::{{closure}}::h9d9d57a8e207ec62
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/panicking.rs:588:13
  12:     0x557646368f54 - std::sys_common::backtrace::__rust_end_short_backtrace::h41203f137e127a88
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/sys_common/backtrace.rs:138:18
  13:     0x55764636ae39 - rust_begin_unwind
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/panicking.rs:584:5
  14:     0x557645918b73 - core::panicking::panic_fmt::h472b827f82d8bfa4
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/core/src/panicking.rs:142:14
  15:     0x557645b4c104 - core::panicking::panic_display::h2419126f417c20d1
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/core/src/panicking.rs:73:5
  16:     0x557645b4585a - rust_socketio::client::builder::ClientBuilder::connect::{{closure}}::h887cc7cd172ebccc
                               at /home/mawoka/.cargo/registry/src/github.com-1ecc6299db9ec823/rust_socketio-0.3.0/src/client/builder.rs:229:21
  17:     0x557645b53310 - std::sys_common::backtrace::__rust_begin_short_backtrace::haf4ad59f77837423
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/sys_common/backtrace.rs:122:18
  18:     0x557645b3ed4d - std::thread::Builder::spawn_unchecked_::{{closure}}::{{closure}}::h9fc04ac80e2aa3df
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/thread/mod.rs:498:17
  19:     0x557645b3d991 - <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once::h013c64b8db9e80d3
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/core/src/panic/unwind_safe.rs:271:9
  20:     0x557645b4c439 - std::panicking::try::do_call::h5e086d5aed2793f2
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/panicking.rs:492:40
  21:     0x557645b4c8eb - __rust_try
  22:     0x557645b4c371 - std::panicking::try::h1cf7f936c2d9e65d
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/panicking.rs:456:19
  23:     0x557645b3d491 - std::panic::catch_unwind::h3e2a3829123712dc
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/panic.rs:137:14
  24:     0x557645b3eb57 - std::thread::Builder::spawn_unchecked_::{{closure}}::h9f4f924223a945bc
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/thread/mod.rs:497:30
  25:     0x557645b4431f - core::ops::function::FnOnce::call_once{{vtable.shim}}::hfa8076f56c9bb1ad
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/core/src/ops/function.rs:227:5
  26:     0x557646371163 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::h78e3a4498542e3c1
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/alloc/src/boxed.rs:1854:9
  27:     0x557646371163 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::h350da154130e2756
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/alloc/src/boxed.rs:1854:9
  28:     0x557646371163 - std::sys::unix::thread::Thread::new::thread_start::h918df3b0ffbb0232
                               at /rustc/68369a041cea809a87e5bd80701da90e0e0a4799/library/std/src/sys/unix/thread.rs:108:17
  29:     0x7fc67ed715c2 - start_thread
  30:     0x7fc67edf6584 - __clone
  31:                0x0 - <unknown>

It's probably just me, doing something completely wrong, but I think that it still shouldn't panic.

Thanks for your help!

Requests only going through sometimes?

For some reason, requests only go through sometimes and with little consistency. Im not too sure why? Maybe because its upgrading but Im a bit stuck...

Client-side Code

use rust_socketio::{Payload, Socket, SocketBuilder};
use serde_json::json;

fn main() {
    let callback = |payload: Payload, mut socket: Socket| {
        match payload {
            Payload::String(str) => println!("Received: {}", str),
            Payload::Binary(bin_data) => println!("Received bytes: {:#?}", bin_data),
        }
        socket
            .emit("message", json!("aquired"))
            .expect("Server unreachable")
    };

    let mut socket = SocketBuilder::new("https://censored.herokuapp.com")
        .on("message", callback)
        .on("error", |err, _| eprintln!("Error: {:#?}", err))
        .connect()
        .expect("Connection failed");

    socket.emit("message", json!("cool message one")).expect("Server unreachable");
    socket.emit("message", json!("cool message two")).expect("Server unreachable");
    
    socket.emit("message", json!("cool message three")).expect("Server unreachable");
    socket.emit("message", json!("cool message four")).expect("Server unreachable");
}

Serverside Code

const cors = require('cors');
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http, {
	allowEIO3: true,
	cors: {
		origin: "*"
	}
});

const port = process.env.PORT || 3000;

io.on('connection', (socket) => {
	console.log("user connect");
	
	socket.on('message', function(data) {
		io.emit('message', data);
		console.log(data);
	});

	socket.on('clear', function clear() {
		io.emit('clear');
	});
});

http.listen(port, () => {
	console.log(`Socket.IO server running at http://localhost:${port}/`);
});

SoX99.mp4

Add SSL support

Currently only connection via http is possible, it would be nice (and secure) to connect via https (and wss later). This might also affect the current way of parsing an URL, which is currently done inside each method. It might be nice to receive something like an Into<Url> type. This also makes the API more flexible for the user.

Add proper debug logging

This is a very important issue to tackle, as I found myself multiple times in the situation where I needed to debug the javascript socket.io server.

Auto-reconnect on disconnects

From what I have seen, most client libraries support auto-reconnects. It would be really nice to have this ๐Ÿ™ˆ

Redo the codecov config as soon as tarpaulin is able to check the coverage on every line

Currently tarpaulin has some issues detecting dangling or formatted lines, see #351. This lead to some issues in #44 where the diff target was hit. I reconfigured the CI to shut up, but we should change it back as soon as tarpaulin is stable. The old config looked like this:

coverage: 
    status:
      project:
        default:        
          target: auto
          threshold: 2%
          paths:
             - "src"
         branches:
           - main
         if_ci_failed: ignore
         informational: false
         only_pulls: false

Troubleshooting "Invalid packet id: 57"

I am experiencing problems trying to connect to a service, but being new to both socket.io and Rust I continuously get lost debugging.

I have little documentation about the service (it's a Beta API under development), and only know the bare minimum.
My problem might be caused by a misconfiguration of the service, but I don't know how to determine if that's the case.

When I try connecting, I get some encouraging log statements, and then suddenly the error mentioned in the title and things stop (see logs below).

Attempting to curl the last URL gives this output:
96:0{"sid":"xxxxxxxxxxxxxxxxxxxx","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}2:40

Digging into various docs, this looks like it might be using the engine.io v3 protocol, while it looks like rust-socketio 0.3.1 uses/expects engine.io v4?
Is it possible to downgrade my client somehow?

Apologies for the vague issue description, I'm not really sure what I'm doing ๐Ÿ˜„

Logs ``` [2022-08-03T19:06:17Z INFO homely_ws_mqtt] Making socket.io connection to https://sdk.iotiliti.cloud/?locationId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx [2022-08-03T19:06:17Z DEBUG reqwest::connect] starting new connection: https://sdk.iotiliti.cloud/ [2022-08-03T19:06:17Z DEBUG reqwest::async_impl::client] response '200 OK' for https://sdk.iotiliti.cloud/socket.io/?locationId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&EIO=4&transport=polling&t=995692734 [2022-08-03T19:06:17Z DEBUG reqwest::connect] starting new connection: https://sdk.iotiliti.cloud/ [2022-08-03T19:06:17Z DEBUG reqwest::async_impl::client] response '200 OK' for https://sdk.iotiliti.cloud/socket.io/?locationId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&EIO=4&transport=polling&t=999035077 Error: EngineIO Error

Caused by:
Invalid packet id: 57

Process finished with exit code 1

</details>

Add support for Socket.IO v2.x

... and therefore revision 4 of Socket.IO protocol and revision 3 of Engine.IO protocol, behind feature gate I suppose.
The v3 server can communicate with v2 clients, but v3 client can't connect to a v2 server.

I'm still learning Rust and Socket.IO but I'd like to contribute if this seems good first issue,

Limit transport to websockets

Is it possible to specify the transport before initializing a connection? We have a service which only supports websocket connections and no long polling.

Emit with multiple parameters

The Client.emit and Client.emit_with_ack only seem to support a single data parameter

This causes issues for servers who are expecting multiple parameters for the data for these events

rust.rs

socket.emit_with_ack("foo", json!("{ack:true}", duration, callback) 

example_socketio_server.js

socket.on("foo", async (userId, body, callback)=>{})

no mater what is placed in data, it will place all data under the "userId" and no way to populate the body parameter.

Is there a workaround that could fix this?

[Help] .on callback and response.

Hi! @1c3t3a

My server emits an event and is expecting a return value from the receiving client.
server code is python:

https://python-socketio.readthedocs.io/en/latest/api.html#server-class

Server.call

Emit a custom event to a client and wait for the response.

python:

sio.call('test', data="a", sid=sid, timeout=3)
...
Traceback (most recent call last):
    raise exceptions.TimeoutError()
    socketio.exceptions.TimeoutError

rust-socketio:

let callback = |payload: Payload, socket: Client| {
       match payload {
           Payload::String(str) => println!("Received: {}", str),
           Payload::Binary(bin_data) => println!("Received bytes: {:#?}", bin_data),
       }
       //  How to return a value  !!!
};
let socket = ClientBuilder::new("http://localhost:4200")
     .namespace("/")
     .auth(json!(""))
     .on("test", callback)
     .on("error", |err, _| eprintln!("Error: {:#?}", err))
     .connect()
     .expect("Connection failed");

HelpHelpHelp,Thanks.

Add optional query params on connect

Some APIs require authentication by query parameters (e.g. FXCM REST API) by appending a &key=val to the url. In said case this is only required once on login to get session authentication infos and not part of every request.

Just review the code

This might sound very general, but I'm thankful for any help. I already added some TODO flags to parts of the code where a deeper review makes sense. In case you find something odd or unusual, just comment this issue, or even better: commit a PR with a proposed fix.

Failed to emit plain text

Javascript socket.io client can emit plain text. rust_socketio currently is not capable of.

client.emit('message-received', data); // javascript

For example on will received a payload. can not just emit that payload back when it is plain text.
currently should use json!() macro to encode.

let socket = ClientBuilder::new("http://localhost:4200")
       .namespace("/admin")
       .on("message-received", |p, socket| {
            let s = match &p {
                Payload::String(s) => s,
                _ => "",
            };
            println!("message-received {:?}", s);
            let _ = socket.emit("utf-8โ„ข", p); // will failed
        })
       .connect()

because of this function

pub(crate) fn build_packet_for_payload<'a>(
&'a self,
payload: Payload,
event: Event,
nsp: &'a str,
id: Option<i32>,
) -> Result<Packet> {
match payload {
Payload::Binary(bin_data) => Ok(Packet::new(
if id.is_some() {
PacketId::BinaryAck
} else {
PacketId::BinaryEvent
},
nsp.to_owned(),
Some(serde_json::Value::String(event.into()).to_string()),
id,
1,
Some(vec![bin_data]),
)),
Payload::String(str_data) => {
serde_json::from_str::<serde_json::Value>(&str_data)?;
let payload = format!("[\"{}\",{}]", String::from(event), str_data);
Ok(Packet::new(
PacketId::Event,
nsp.to_owned(),
Some(payload),
id,
0,
None,
))
}
}

this line

serde_json::from_str::<serde_json::Value>(&str_data)?;

Performance degradation using SSL

When connecting with HTTPS to a Flask SocketIO server I'm seeing a slowdown of 20 seconds waiting for a response compared to http.

This is happening with header auth and with certificate auth.

let socket = SocketBuilder::new("https://$IP$".to_string())
        .opening_header(
            "Authorization",
            settings.header_auth_value.as_str()
        )
        .on("open", connect_callback)
        .on("submit", submit_callback)
        .on("error", |err, _| {
            warn!("Server Connection Error: {:#?}", err)
        })
        .on("error", |err, _| eprintln!("Error: {:#?}", err))
        // .transport_type(TransportType::WebsocketUpgrade)
        //  .tls_config(tls_connector)
        .connect();

I can see in the nginx logs that the websocket upgrade is switching protocols

client GET /socket.io/?EIO=4&sid=n4wlLsEmzjqGaiGHAAAG&transport=websocket HTTP/1.1 101 45

Any ideas on what could be going wrong?

PS. Not seeing this degradation with python socket io client

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.