tf_rust_engineio/asynchronous/client/
async_client.rs1use std::{fmt::Debug, pin::Pin};
2
3use crate::{
4 asynchronous::{async_socket::Socket as InnerSocket, generator::StreamGenerator},
5 error::Result,
6 Packet,
7};
8use async_stream::try_stream;
9use futures_util::{Stream, StreamExt};
10
11#[derive(Clone)]
19pub struct Client {
20 pub(super) socket: InnerSocket,
21 generator: StreamGenerator<Packet>,
22}
23
24impl Client {
25 pub(super) fn new(socket: InnerSocket) -> Self {
26 Client {
27 socket: socket.clone(),
28 generator: StreamGenerator::new(Self::stream(socket)),
29 }
30 }
31
32 pub async fn close(&self) -> Result<()> {
33 self.disconnect().await
34 }
35
36 pub async fn connect(&self) -> Result<()> {
39 self.socket.connect().await
40 }
41
42 pub async fn disconnect(&self) -> Result<()> {
44 let client = self.clone();
45 tokio::spawn(async move {
46 client.generator.close().await;
48 client.socket.disconnect().await
49 })
50 .await
51 .map_err(std::io::Error::other)?
52 }
53
54 pub async fn emit(&self, packet: Packet) -> Result<()> {
56 self.socket.emit(packet).await
57 }
58
59 fn stream(
61 socket: InnerSocket,
62 ) -> Pin<Box<impl Stream<Item = Result<Packet>> + 'static + Send>> {
63 Box::pin(try_stream! {
64 let socket = socket.clone();
65 for await item in socket.as_stream() {
66 let packet = item?;
67 socket.handle_incoming_packet(packet.clone()).await?;
68 yield packet;
69 }
70 })
71 }
72
73 pub fn is_connected(&self) -> bool {
75 self.socket.is_connected()
76 }
77}
78
79impl Stream for Client {
80 type Item = Result<Packet>;
81
82 fn poll_next(
83 mut self: Pin<&mut Self>,
84 cx: &mut std::task::Context<'_>,
85 ) -> std::task::Poll<Option<Self::Item>> {
86 self.generator.poll_next_unpin(cx)
87 }
88}
89
90impl Debug for Client {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 f.debug_struct("Client")
93 .field("socket", &self.socket)
94 .finish()
95 }
96}
97
98#[cfg(test)]
99mod test {
100
101 use super::*;
102 use crate::{asynchronous::ClientBuilder, header::HeaderMap, packet::PacketId, Error};
103 use bytes::Bytes;
104 use futures_util::StreamExt;
105 use native_tls::TlsConnector;
106 use url::Url;
107
108 #[tokio::test]
113 async fn test_client_cloneable() -> Result<()> {
114 let url = crate::test::engine_io_server()?;
115
116 let mut sut = builder(url).build().await?;
117 let mut cloned = sut.clone();
118
119 sut.connect().await?;
120
121 assert!(sut.is_connected());
124 assert!(cloned.is_connected());
125
126 assert_eq!(
128 sut.next().await.unwrap()?,
129 Packet::new(PacketId::Message, "hello client")
130 );
131
132 sut.emit(Packet::new(PacketId::Message, "respond")).await?;
133
134 assert_eq!(
135 cloned.next().await.unwrap()?,
136 Packet::new(PacketId::Message, "Roger Roger")
137 );
138
139 cloned.disconnect().await?;
140
141 assert!(!sut.is_connected());
144 assert!(!cloned.is_connected());
145
146 Ok(())
147 }
148
149 #[tokio::test]
150 async fn test_illegal_actions() -> Result<()> {
151 let url = crate::test::engine_io_server()?;
152 let mut sut = builder(url.clone()).build().await?;
153
154 assert!(sut
155 .emit(Packet::new(PacketId::Close, Bytes::new()))
156 .await
157 .is_err());
158
159 sut.connect().await?;
160
161 assert!(sut.next().await.unwrap().is_ok());
162
163 assert!(builder(Url::parse("fake://fake.fake").unwrap())
164 .build_websocket()
165 .await
166 .is_err());
167
168 sut.disconnect().await?;
169
170 Ok(())
171 }
172
173 use reqwest::header::HOST;
174
175 use crate::packet::Packet;
176
177 fn builder(url: Url) -> ClientBuilder {
178 ClientBuilder::new(url)
179 .on_open(|_| {
180 Box::pin(async {
181 println!("Open event!");
182 })
183 })
184 .on_packet(|packet| {
185 Box::pin(async move {
186 println!("Received packet: {:?}", packet);
187 })
188 })
189 .on_data(|data| {
190 Box::pin(async move {
191 println!("Received data: {:?}", std::str::from_utf8(&data));
192 })
193 })
194 .on_close(|_| {
195 Box::pin(async {
196 println!("Close event!");
197 })
198 })
199 .on_error(|error| {
200 Box::pin(async move {
201 println!("Error {}", error);
202 })
203 })
204 }
205
206 async fn test_connection(socket: Client) -> Result<()> {
207 let mut socket = socket;
208
209 socket.connect().await.unwrap();
210
211 assert_eq!(
212 socket.next().await.unwrap()?,
213 Packet::new(PacketId::Message, "hello client")
214 );
215 println!("received msg, about to send");
216
217 socket
218 .emit(Packet::new(PacketId::Message, "respond"))
219 .await?;
220
221 println!("send msg");
222
223 assert_eq!(
224 socket.next().await.unwrap()?,
225 Packet::new(PacketId::Message, "Roger Roger")
226 );
227 println!("received 2");
228
229 socket.close().await
230 }
231
232 #[tokio::test]
233 async fn test_connection_long() -> Result<()> {
234 let url = crate::test::engine_io_server()?;
236 let mut socket = builder(url).build().await?;
237
238 socket.connect().await?;
239
240 assert!(matches!(
242 socket.next().await.unwrap()?,
243 Packet {
244 packet_id: PacketId::Message,
245 ..
246 }
247 ));
248 assert!(matches!(
250 socket.next().await.unwrap()?,
251 Packet {
252 packet_id: PacketId::Ping,
253 ..
254 }
255 ));
256
257 socket.disconnect().await?;
258
259 assert!(!socket.is_connected());
260
261 Ok(())
262 }
263
264 #[tokio::test]
265 async fn test_connection_dynamic() -> Result<()> {
266 let url = crate::test::engine_io_server()?;
267 let socket = builder(url).build().await?;
268 test_connection(socket).await?;
269
270 let url = crate::test::engine_io_polling_server()?;
271 let socket = builder(url).build().await?;
272 test_connection(socket).await
273 }
274
275 #[tokio::test]
276 async fn test_connection_fallback() -> Result<()> {
277 let url = crate::test::engine_io_server()?;
278 let socket = builder(url).build_with_fallback().await?;
279 test_connection(socket).await?;
280
281 let url = crate::test::engine_io_polling_server()?;
282 let socket = builder(url).build_with_fallback().await?;
283 test_connection(socket).await
284 }
285
286 #[tokio::test]
287 async fn test_connection_dynamic_secure() -> Result<()> {
288 let url = crate::test::engine_io_server_secure()?;
289 let mut socket_builder = builder(url);
290 socket_builder = socket_builder.tls_config(crate::test::tls_connector()?);
291 let socket = socket_builder.build().await?;
292 test_connection(socket).await
293 }
294
295 #[tokio::test]
296 async fn test_connection_polling() -> Result<()> {
297 let url = crate::test::engine_io_server()?;
298 let socket = builder(url).build_polling().await?;
299 test_connection(socket).await
300 }
301
302 #[tokio::test]
303 async fn test_connection_wss() -> Result<()> {
304 let url = crate::test::engine_io_polling_server()?;
305 assert!(builder(url).build_websocket_with_upgrade().await.is_err());
306
307 let host =
308 std::env::var("ENGINE_IO_SECURE_HOST").unwrap_or_else(|_| "localhost".to_owned());
309 let mut url = crate::test::engine_io_server_secure()?;
310
311 let mut headers = HeaderMap::default();
312 headers.insert(HOST, host);
313 let mut builder = builder(url.clone());
314
315 builder = builder.tls_config(crate::test::tls_connector()?);
316 builder = builder.headers(headers.clone());
317 let socket = builder.clone().build_websocket_with_upgrade().await?;
318
319 test_connection(socket).await?;
320
321 let socket = builder.build_websocket().await?;
322
323 test_connection(socket).await?;
324
325 url.set_scheme("wss").unwrap();
326
327 let builder = self::builder(url)
328 .tls_config(crate::test::tls_connector()?)
329 .headers(headers);
330 let socket = builder.clone().build_websocket().await?;
331
332 test_connection(socket).await?;
333
334 assert!(builder.build_websocket_with_upgrade().await.is_err());
335
336 Ok(())
337 }
338
339 #[tokio::test]
340 async fn test_connection_ws() -> Result<()> {
341 let url = crate::test::engine_io_polling_server()?;
342 assert!(builder(url.clone()).build_websocket().await.is_err());
343 assert!(builder(url).build_websocket_with_upgrade().await.is_err());
344
345 let mut url = crate::test::engine_io_server()?;
346
347 let builder = builder(url.clone());
348 let socket = builder.clone().build_websocket().await?;
349 test_connection(socket).await?;
350
351 let socket = builder.build_websocket_with_upgrade().await?;
352 test_connection(socket).await?;
353
354 url.set_scheme("ws").unwrap();
355
356 let builder = self::builder(url);
357 let socket = builder.clone().build_websocket().await?;
358
359 test_connection(socket).await?;
360
361 assert!(builder.build_websocket_with_upgrade().await.is_err());
362
363 Ok(())
364 }
365
366 #[tokio::test]
367 async fn test_open_invariants() -> Result<()> {
368 let url = crate::test::engine_io_server()?;
369 let illegal_url = "this is illegal";
370
371 assert!(Url::parse(illegal_url).is_err());
372
373 let invalid_protocol = "file:///tmp/foo";
374 assert!(builder(Url::parse(invalid_protocol).unwrap())
375 .build()
376 .await
377 .is_err());
378
379 let sut = builder(url.clone()).build().await?;
380 let _error = sut
381 .emit(Packet::new(PacketId::Close, Bytes::new()))
382 .await
383 .expect_err("error");
384 assert!(matches!(Error::IllegalActionBeforeOpen(), _error));
385
386 let mut headers = HeaderMap::default();
388 let host =
390 std::env::var("ENGINE_IO_SECURE_HOST").unwrap_or_else(|_| "localhost:4201".to_owned());
391 headers.insert(HOST, host);
392
393 let _ = builder(url.clone())
394 .tls_config(
395 TlsConnector::builder()
396 .danger_accept_invalid_certs(true)
397 .build()
398 .unwrap(),
399 )
400 .build()
401 .await?;
402 let _ = builder(url).headers(headers).build().await?;
403 Ok(())
404 }
405}