1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! # fast_websocket_client
//!
//! A fast asynchronous websocket client built on top of [fastwebsockets](https://github.com/denoland/fastwebsockets) library
//!
//! ## `use fast_websocket_client::{client, connect, OpCode};`
//!
//! That's all you need to import. Just grap a slick toolbox and go.  
//! Please read [examples/wss_client.rs](https://github.com/Osteoporosis/fast_websocket_client/blob/main/examples/wss_client.rs) or see below.
//!
//! ```
//! // try this example with
//! // $ cargo run --example wss_client
//!
//! use std::time::{Duration, Instant};
//!
//! use fast_websocket_client::{client, connect, OpCode};
//!
//! #[derive(serde::Serialize)]
//! struct Subscription {
//!     method: String,
//!     params: Vec<String>,
//!     id: u128,
//! }
//!
//! async fn subscribe(
//!     client: &mut client::Online,
//!     started_at: Instant,
//! ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//!     let data = Subscription {
//!         method: "SUBSCRIBE".to_string(),
//!         params: vec!["btcusdt@bookTicker".to_string()],
//!         id: started_at.elapsed().as_nanos(),
//!     };
//!     tokio::time::timeout(Duration::from_millis(0), client.send_json(&data)).await??;
//!     Ok(())
//! }
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     let started_at = Instant::now();
//!     let runtime = tokio::runtime::Builder::new_current_thread()
//!         .enable_all()
//!         .build()
//!         .unwrap();
//!
//!     // the lowest volume example
//!     let url = "wss://data-stream.binance.vision:9443/ws/bakeusdt@bookTicker";
//!
//!     let handle = runtime.spawn(async move {
//!         'reconnect_loop: loop {
//!             let future = connect(url);
//!             /*
//!                 alternative code for an example:
//!                     1. make a Offline client
//!                     2. apply an intentional error raising setting before `connect`
//!                     3. call `connect` to get a future
//!             */
//!             // let mut client = client::Offline::new();
//!             // client.set_max_message_size(64);
//!             // let future = client.connect(url);
//!
//!             let mut client: client::Online = match future.await {
//!                 Ok(client) => {
//!                     println!("conneted");
//!                     client
//!                 }
//!                 Err(e) => {
//!                     eprintln!("Reconnecting from an Error: {e:?}");
//!                     tokio::time::sleep(Duration::from_secs(10)).await;
//!                     continue;
//!                 }
//!             };
//!
//!             // we can modify settings while running.
//!             // without pong, this app stops in about 15 minutes.(by the binance API spec.)
//!             client.set_auto_pong(false);
//!
//!             // add one more example subscription here after connect
//!             if let Err(e) = subscribe(&mut client, started_at).await {
//!                 eprintln!("Reconnecting from an Error: {e:?}");
//!                 let _ = client.send_close(&[]).await;
//!                 tokio::time::sleep(Duration::from_secs(10)).await;
//!                 continue;
//!             };
//!
//!             // message processing loop
//!             loop {
//!                 let message = if let Ok(result) =
//!                     tokio::time::timeout(Duration::from_millis(100), client.receive_frame()).await
//!                 {
//!                     match result {
//!                         Ok(message) => message,
//!                         Err(e) => {
//!                             eprintln!("Reconnecting from an Error: {e:?}");
//!                             let _ = client.send_close(&[]).await;
//!                             break; // break the message loop then reconnect
//!                         }
//!                     }
//!                 } else {
//!                     println!("timeout");
//!                     continue;
//!                 };
//!
//!                 match message.opcode {
//!                     OpCode::Text => {
//!                         let payload = match simdutf8::basic::from_utf8(message.payload.as_ref()) {
//!                             Ok(payload) => payload,
//!                             Err(e) => {
//!                                 eprintln!("Reconnecting from an Error: {e:?}");
//!                                 let _ = client.send_close(&[]).await;
//!                                 break; // break the message loop then reconnect
//!                             }
//!                         };
//!                         println!("{payload}");
//!                     }
//!                     OpCode::Close => {
//!                         println!("{:?}", String::from_utf8_lossy(message.payload.as_ref()));
//!                         break 'reconnect_loop;
//!                     }
//!                     _ => {}
//!                 }
//!             }
//!         }
//!     });
//!     runtime.block_on(handle)?;
//!     Ok(())
//! }
//! ```

pub mod client;
pub use fastwebsockets::OpCode;

/// Connects to the url and returns an Online client.
pub async fn connect(
    url: &str,
) -> Result<self::client::Online, Box<dyn std::error::Error + Send + Sync>> {
    self::client::Offline::new().connect(url).await
}

mod fragment;
mod handshake;
mod recv;
mod tls_connector;
mod websocket;