rust_socketio/
lib.rs

1//! Rust-socket.io is a socket.io client written in the Rust Programming Language.
2//! ## Example usage
3//!
4//! ``` rust
5//! use rust_socketio::{ClientBuilder, Payload, RawClient};
6//! use serde_json::json;
7//! use std::time::Duration;
8//!
9//! // define a callback which is called when a payload is received
10//! // this callback gets the payload as well as an instance of the
11//! // socket to communicate with the server
12//! let callback = |payload: Payload, socket: RawClient| {
13//!        match payload {
14//!            Payload::Text(values) => println!("Received: {:#?}", values),
15//!            Payload::Binary(bin_data) => println!("Received bytes: {:#?}", bin_data),
16//!            // This variant is deprecated, use Payload::Text instead
17//!            Payload::String(str) => println!("Received: {}", str),
18//!        }
19//!        socket.emit("test", json!({"got ack": true})).expect("Server unreachable")
20//! };
21//!
22//! // get a socket that is connected to the admin namespace
23//! let mut socket = ClientBuilder::new("http://localhost:4200/")
24//!      .namespace("/admin")
25//!      .on("test", callback)
26//!      .on("error", |err, _| eprintln!("Error: {:#?}", err))
27//!      .connect()
28//!      .expect("Connection failed");
29//!
30//! // emit to the "foo" event
31//! let json_payload = json!({"token": 123});
32//!
33//! socket.emit("foo", json_payload).expect("Server unreachable");
34//!
35//! // define a callback, that's executed when the ack got acked
36//! let ack_callback = |message: Payload, _: RawClient| {
37//!     println!("Yehaa! My ack got acked?");
38//!     println!("Ack data: {:#?}", message);
39//! };
40//!
41//! let json_payload = json!({"myAckData": 123});
42//!
43//! // emit with an ack
44//! let ack = socket
45//!     .emit_with_ack("test", json_payload, Duration::from_secs(2), ack_callback)
46//!     .expect("Server unreachable");
47//! ```
48//!
49//! The main entry point for using this crate is the [`ClientBuilder`] which provides
50//! a way to easily configure a socket in the needed way. When the `connect` method
51//! is called on the builder, it returns a connected client which then could be used
52//! to emit messages to certain events. One client can only be connected to one namespace.
53//! If you need to listen to the messages in different namespaces you need to
54//! allocate multiple sockets.
55//!
56//! ## Current features
57//!
58//! This implementation now supports all of the features of the socket.io protocol mentioned
59//! [here](https://github.com/socketio/socket.io-protocol).
60//! It generally tries to make use of websockets as often as possible. This means most times
61//! only the opening request uses http and as soon as the server mentions that he is able to use
62//! websockets, an upgrade  is performed. But if this upgrade is not successful or the server
63//! does not mention an upgrade possibility, http-long polling is used (as specified in the protocol specs).
64//!
65//! Here's an overview of possible use-cases:
66//!
67//! - connecting to a server.
68//! - register callbacks for the following event types:
69//!     - open
70//!     - close
71//!     - error
72//!     - message
73//!     - custom events like "foo", "on_payment", etc.
74//! - send JSON data to the server (via `serde_json` which provides safe
75//! handling).
76//! - send JSON data to the server and receive an `ack`.
77//! - send and handle Binary data.
78#![cfg_attr(
79    feature = "async",
80    doc = r#"
81## Async version
82This library provides an ability for being executed in an asynchronous context using `tokio` as
83the execution runtime.
84Please note that the current async implementation is in beta, the interface can be object to
85drastic changes.
86The async `Client` and `ClientBuilder` support a similar interface to the sync version and live
87in the [`asynchronous`] module. In order to enable the support, you need to enable the `async`
88feature flag:
89```toml
90rust_socketio = { version = "^0.4.1", features = ["async"] }
91```
92
93The following code shows the example above in async fashion:
94
95``` rust
96use futures_util::FutureExt;
97use rust_socketio::{
98    asynchronous::{Client, ClientBuilder},
99    Payload,
100};
101use serde_json::json;
102use std::time::Duration;
103
104#[tokio::main]
105async fn main() {
106    // define a callback which is called when a payload is received
107    // this callback gets the payload as well as an instance of the
108    // socket to communicate with the server
109    let callback = |payload: Payload, socket: Client| {
110        async move {
111            match payload {
112                Payload::Text(values) => println!("Received: {:#?}", values),
113                Payload::Binary(bin_data) => println!("Received bytes: {:#?}", bin_data),
114                // This is deprecated use Payload::Text instead
115                Payload::String(str) => println!("Received: {}", str),
116            }
117            socket
118                .emit("test", json!({"got ack": true}))
119                .await
120                .expect("Server unreachable");
121        }
122        .boxed()
123    };
124
125    // get a socket that is connected to the admin namespace
126    let socket = ClientBuilder::new("http://localhost:4200/")
127        .namespace("/admin")
128        .on("test", callback)
129        .on("error", |err, _| {
130            async move { eprintln!("Error: {:#?}", err) }.boxed()
131        })
132        .connect()
133        .await
134        .expect("Connection failed");
135
136    // emit to the "foo" event
137    let json_payload = json!({"token": 123});
138    socket
139        .emit("foo", json_payload)
140        .await
141        .expect("Server unreachable");
142
143    // define a callback, that's executed when the ack got acked
144    let ack_callback = |message: Payload, _: Client| {
145        async move {
146            println!("Yehaa! My ack got acked?");
147            println!("Ack data: {:#?}", message);
148        }
149        .boxed()
150    };
151
152    let json_payload = json!({"myAckData": 123});
153    // emit with an ack
154    socket
155        .emit_with_ack("test", json_payload, Duration::from_secs(2), ack_callback)
156        .await
157        .expect("Server unreachable");
158
159    socket.disconnect().await.expect("Disconnect failed");
160}
161```"#
162)]
163#![allow(clippy::rc_buffer)]
164#![warn(clippy::complexity)]
165#![warn(clippy::style)]
166#![warn(clippy::perf)]
167#![warn(clippy::correctness)]
168
169/// Defines client only structs
170pub mod client;
171/// Deprecated import since 0.3.0-alpha-2, use Event in the crate root instead.
172/// Defines the events that could be sent or received.
173pub mod event;
174pub(crate) mod packet;
175/// Deprecated import since 0.3.0-alpha-2, use Event in the crate root instead.
176/// Defines the types of payload (binary or string), that
177/// could be sent or received.
178pub mod payload;
179pub(self) mod socket;
180
181/// Deprecated import since 0.3.0-alpha-2, use Error in the crate root instead.
182/// Contains the error type which will be returned with every result in this
183/// crate.
184pub mod error;
185
186#[cfg(feature = "async")]
187/// Asynchronous version of the socket.io client. This module contains the async
188/// [`crate::asynchronous::Client`] as well as a builder
189/// ([`crate::asynchronous::ClientBuilder`]) that allows for configuring a client.
190pub mod asynchronous;
191
192pub use error::Error;
193
194pub use {event::Event, payload::Payload};
195
196pub use client::{ClientBuilder, RawClient, TransportType};
197
198// TODO: 0.4.0 remove
199#[deprecated(since = "0.3.0-alpha-2", note = "Socket renamed to Client")]
200pub use client::{ClientBuilder as SocketBuilder, RawClient as Socket};
201
202#[cfg(test)]
203pub(crate) mod test {
204    use url::Url;
205
206    /// The socket.io server for testing runs on port 4200
207    const SERVER_URL: &str = "http://localhost:4200";
208
209    pub(crate) fn socket_io_server() -> Url {
210        let url = std::env::var("SOCKET_IO_SERVER").unwrap_or_else(|_| SERVER_URL.to_owned());
211        let mut url = Url::parse(&url).unwrap();
212
213        if url.path() == "/" {
214            url.set_path("/socket.io/");
215        }
216
217        url
218    }
219
220    // The socket.io auth server for testing runs on port 4204
221    const AUTH_SERVER_URL: &str = "http://localhost:4204";
222
223    pub(crate) fn socket_io_auth_server() -> Url {
224        let url =
225            std::env::var("SOCKET_IO_AUTH_SERVER").unwrap_or_else(|_| AUTH_SERVER_URL.to_owned());
226        let mut url = Url::parse(&url).unwrap();
227
228        if url.path() == "/" {
229            url.set_path("/socket.io/");
230        }
231
232        url
233    }
234
235    // The socket.io restart server for testing runs on port 4205
236    const RESTART_SERVER_URL: &str = "http://localhost:4205";
237
238    pub(crate) fn socket_io_restart_server() -> Url {
239        let url = std::env::var("SOCKET_IO_RESTART_SERVER")
240            .unwrap_or_else(|_| RESTART_SERVER_URL.to_owned());
241        let mut url = Url::parse(&url).unwrap();
242
243        if url.path() == "/" {
244            url.set_path("/socket.io/");
245        }
246
247        url
248    }
249
250    // The socket.io restart url auth server for testing runs on port 4206
251    const RESTART_URL_AUTH_SERVER_URL: &str = "http://localhost:4206";
252
253    pub(crate) fn socket_io_restart_url_auth_server() -> Url {
254        let url = std::env::var("SOCKET_IO_RESTART_URL_AUTH_SERVER")
255            .unwrap_or_else(|_| RESTART_URL_AUTH_SERVER_URL.to_owned());
256        let mut url = Url::parse(&url).unwrap();
257
258        if url.path() == "/" {
259            url.set_path("/socket.io/");
260        }
261
262        url
263    }
264}