Skip to main content

async_wsocket/
socket.rs

1// Copyright (c) 2022-2024 Yuki Kishimoto
2// Distributed under the MIT software license
3
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use futures_util::{Sink, Stream};
8#[cfg(not(target_arch = "wasm32"))]
9use tokio::net::TcpStream;
10#[cfg(not(target_arch = "wasm32"))]
11use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
12use url::Url;
13
14#[cfg(target_arch = "wasm32")]
15use crate::wasm::WsStream;
16use crate::{ConnectionMode, Error, Message};
17
18#[cfg(not(target_arch = "wasm32"))]
19type WsStream<T> = WebSocketStream<MaybeTlsStream<T>>;
20
21enum InnerWebSocket {
22    #[cfg(not(target_arch = "wasm32"))]
23    Tokio(Box<WsStream<TcpStream>>),
24    #[cfg(target_arch = "wasm32")]
25    Wasm(WsStream),
26}
27
28pub struct WebSocket {
29    inner: InnerWebSocket,
30}
31
32impl WebSocket {
33    #[inline]
34    fn new(inner: InnerWebSocket) -> Self {
35        Self { inner }
36    }
37
38    #[inline]
39    #[cfg(not(target_arch = "wasm32"))]
40    pub(crate) fn tokio(inner: Box<WsStream<TcpStream>>) -> Self {
41        Self::new(InnerWebSocket::Tokio(inner))
42    }
43    #[inline]
44    #[cfg(target_arch = "wasm32")]
45    pub(crate) fn wasm(inner: WsStream) -> Self {
46        Self::new(InnerWebSocket::Wasm(inner))
47    }
48
49    pub async fn connect(url: &Url, _mode: &ConnectionMode) -> Result<Self, Error> {
50        #[cfg(not(target_arch = "wasm32"))]
51        let socket: WebSocket = crate::native::connect(url, _mode).await?;
52
53        #[cfg(target_arch = "wasm32")]
54        let socket: WebSocket = crate::wasm::connect(url).await?;
55
56        Ok(socket)
57    }
58
59    /// Connect with additional HTTP headers in the WebSocket upgrade request.
60    #[cfg(not(target_arch = "wasm32"))]
61    pub async fn connect_with_headers(
62        url: &Url,
63        mode: &ConnectionMode,
64        headers: crate::native::HeaderMap,
65    ) -> Result<Self, Error> {
66        crate::native::connect_with_headers(url, mode, headers).await
67    }
68}
69
70impl Sink<Message> for WebSocket {
71    type Error = Error;
72
73    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
74        match &mut self.inner {
75            #[cfg(not(target_arch = "wasm32"))]
76            InnerWebSocket::Tokio(s) => Pin::new(s.as_mut()).poll_ready(cx).map_err(Into::into),
77            #[cfg(target_arch = "wasm32")]
78            InnerWebSocket::Wasm(s) => Pin::new(s).poll_ready(cx),
79        }
80    }
81
82    fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
83        match &mut self.inner {
84            #[cfg(not(target_arch = "wasm32"))]
85            InnerWebSocket::Tokio(s) => Pin::new(s.as_mut())
86                .start_send(item.into())
87                .map_err(Into::into),
88            #[cfg(target_arch = "wasm32")]
89            InnerWebSocket::Wasm(s) => Pin::new(s).start_send(item),
90        }
91    }
92
93    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
94        match &mut self.inner {
95            #[cfg(not(target_arch = "wasm32"))]
96            InnerWebSocket::Tokio(s) => Pin::new(s.as_mut()).poll_flush(cx).map_err(Into::into),
97            #[cfg(target_arch = "wasm32")]
98            InnerWebSocket::Wasm(s) => Pin::new(s).poll_flush(cx),
99        }
100    }
101
102    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
103        match &mut self.inner {
104            #[cfg(not(target_arch = "wasm32"))]
105            InnerWebSocket::Tokio(s) => Pin::new(s.as_mut()).poll_close(cx).map_err(Into::into),
106            #[cfg(target_arch = "wasm32")]
107            InnerWebSocket::Wasm(s) => Pin::new(s).poll_close(cx).map_err(Into::into),
108        }
109    }
110}
111
112impl Stream for WebSocket {
113    type Item = Result<Message, Error>;
114
115    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
116        match &mut self.inner {
117            #[cfg(not(target_arch = "wasm32"))]
118            InnerWebSocket::Tokio(s) => Pin::new(s)
119                .poll_next(cx)
120                .map(|i| i.map(|res| res.map(Message::from_native)))
121                .map_err(Into::into),
122            #[cfg(target_arch = "wasm32")]
123            InnerWebSocket::Wasm(s) => Pin::new(s).poll_next(cx).map_err(Into::into),
124        }
125    }
126
127    fn size_hint(&self) -> (usize, Option<usize>) {
128        match &self.inner {
129            #[cfg(not(target_arch = "wasm32"))]
130            InnerWebSocket::Tokio(s) => s.size_hint(),
131            #[cfg(target_arch = "wasm32")]
132            InnerWebSocket::Wasm(s) => s.size_hint(),
133        }
134    }
135}
136
137#[cfg(target_arch = "wasm32")]
138#[allow(dead_code)]
139fn assert_wasm_send(url: &Url, mode: &ConnectionMode) {
140    fn assert_type<T: Send>() {}
141    fn assert_value<T: Send>(_: T) {}
142
143    assert_type::<WebSocket>();
144    assert_value(WebSocket::connect(url, mode));
145}