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}