ng_async_tungstenite/
async_std.rs

1//! `async-std` integration.
2use ng_tungstenite::client::IntoClientRequest;
3use ng_tungstenite::handshake::client::{Request, Response};
4use ng_tungstenite::protocol::WebSocketConfig;
5use ng_tungstenite::Error;
6
7use async_std::net::TcpStream;
8
9use super::{domain, port, WebSocketStream};
10
11#[cfg(feature = "async-native-tls")]
12use futures_io::{AsyncRead, AsyncWrite};
13
14#[cfg(feature = "async-native-tls")]
15pub(crate) mod async_native_tls {
16    use async_native_tls::TlsConnector as AsyncTlsConnector;
17    use async_native_tls::TlsStream;
18    use real_async_native_tls as async_native_tls;
19
20    use ng_tungstenite::client::uri_mode;
21    use ng_tungstenite::handshake::client::Request;
22    use ng_tungstenite::stream::Mode;
23    use ng_tungstenite::Error;
24
25    use futures_io::{AsyncRead, AsyncWrite};
26
27    use crate::stream::Stream as StreamSwitcher;
28    use crate::{
29        client_async_with_config, domain, IntoClientRequest, Response, WebSocketConfig,
30        WebSocketStream,
31    };
32
33    /// A stream that might be protected with TLS.
34    pub type MaybeTlsStream<S> = StreamSwitcher<S, TlsStream<S>>;
35
36    pub type AutoStream<S> = MaybeTlsStream<S>;
37
38    pub type Connector = AsyncTlsConnector;
39
40    async fn wrap_stream<S>(
41        socket: S,
42        domain: String,
43        connector: Option<Connector>,
44        mode: Mode,
45    ) -> Result<AutoStream<S>, Error>
46    where
47        S: 'static + AsyncRead + AsyncWrite + Unpin,
48    {
49        match mode {
50            Mode::Plain => Ok(StreamSwitcher::Plain(socket)),
51            Mode::Tls => {
52                let stream = {
53                    let connector = if let Some(connector) = connector {
54                        connector
55                    } else {
56                        AsyncTlsConnector::new()
57                    };
58                    connector
59                        .connect(&domain, socket)
60                        .await
61                        .map_err(|err| Error::Tls(err.into()))?
62                };
63                Ok(StreamSwitcher::Tls(stream))
64            }
65        }
66    }
67
68    /// Creates a WebSocket handshake from a request and a stream,
69    /// upgrading the stream to TLS if required and using the given
70    /// connector and WebSocket configuration.
71    pub async fn client_async_tls_with_connector_and_config<R, S>(
72        request: R,
73        stream: S,
74        connector: Option<AsyncTlsConnector>,
75        config: Option<WebSocketConfig>,
76    ) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error>
77    where
78        R: IntoClientRequest + Unpin,
79        S: 'static + AsyncRead + AsyncWrite + Unpin,
80        AutoStream<S>: Unpin,
81    {
82        let request: Request = request.into_client_request()?;
83
84        let domain = domain(&request)?;
85
86        // Make sure we check domain and mode first. URL must be valid.
87        let mode = uri_mode(request.uri())?;
88
89        let stream = wrap_stream(stream, domain, connector, mode).await?;
90        client_async_with_config(request, stream, config).await
91    }
92}
93
94#[cfg(not(any(feature = "async-tls", feature = "async-native-tls")))]
95pub(crate) mod dummy_tls {
96    use futures_io::{AsyncRead, AsyncWrite};
97
98    use ng_tungstenite::client::{uri_mode, IntoClientRequest};
99    use ng_tungstenite::handshake::client::Request;
100    use ng_tungstenite::stream::Mode;
101    use ng_tungstenite::Error;
102
103    use crate::{client_async_with_config, domain, Response, WebSocketConfig, WebSocketStream};
104
105    pub type AutoStream<S> = S;
106    type Connector = ();
107
108    async fn wrap_stream<S>(
109        socket: S,
110        _domain: String,
111        _connector: Option<()>,
112        mode: Mode,
113    ) -> Result<AutoStream<S>, Error>
114    where
115        S: 'static + AsyncRead + AsyncWrite + Unpin,
116    {
117        match mode {
118            Mode::Plain => Ok(socket),
119            Mode::Tls => Err(Error::Url(
120                ng_tungstenite::error::UrlError::TlsFeatureNotEnabled,
121            )),
122        }
123    }
124
125    /// Creates a WebSocket handshake from a request and a stream,
126    /// upgrading the stream to TLS if required and using the given
127    /// connector and WebSocket configuration.
128    pub async fn client_async_tls_with_connector_and_config<R, S>(
129        request: R,
130        stream: S,
131        connector: Option<Connector>,
132        config: Option<WebSocketConfig>,
133    ) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error>
134    where
135        R: IntoClientRequest + Unpin,
136        S: 'static + AsyncRead + AsyncWrite + Unpin,
137        AutoStream<S>: Unpin,
138    {
139        let request: Request = request.into_client_request()?;
140
141        let domain = domain(&request)?;
142
143        // Make sure we check domain and mode first. URL must be valid.
144        let mode = uri_mode(request.uri())?;
145
146        let stream = wrap_stream(stream, domain, connector, mode).await?;
147        client_async_with_config(request, stream, config).await
148    }
149}
150
151#[cfg(not(any(feature = "async-tls", feature = "async-native-tls")))]
152pub use self::dummy_tls::client_async_tls_with_connector_and_config;
153#[cfg(not(any(feature = "async-tls", feature = "async-native-tls")))]
154use self::dummy_tls::AutoStream;
155
156#[cfg(all(feature = "async-tls", not(feature = "async-native-tls")))]
157pub use crate::async_tls::client_async_tls_with_connector_and_config;
158#[cfg(all(feature = "async-tls", not(feature = "async-native-tls")))]
159use crate::async_tls::AutoStream;
160#[cfg(all(feature = "async-tls", not(feature = "async-native-tls")))]
161type Connector = real_async_tls::TlsConnector;
162
163#[cfg(feature = "async-native-tls")]
164pub use self::async_native_tls::client_async_tls_with_connector_and_config;
165#[cfg(feature = "async-native-tls")]
166use self::async_native_tls::{AutoStream, Connector};
167
168/// Type alias for the stream type of the `client_async()` functions.
169pub type ClientStream<S> = AutoStream<S>;
170
171#[cfg(feature = "async-native-tls")]
172/// Creates a WebSocket handshake from a request and a stream,
173/// upgrading the stream to TLS if required.
174pub async fn client_async_tls<R, S>(
175    request: R,
176    stream: S,
177) -> Result<(WebSocketStream<ClientStream<S>>, Response), Error>
178where
179    R: IntoClientRequest + Unpin,
180    S: 'static + AsyncRead + AsyncWrite + Unpin,
181    AutoStream<S>: Unpin,
182{
183    client_async_tls_with_connector_and_config(request, stream, None, None).await
184}
185
186#[cfg(feature = "async-native-tls")]
187/// Creates a WebSocket handshake from a request and a stream,
188/// upgrading the stream to TLS if required and using the given
189/// WebSocket configuration.
190pub async fn client_async_tls_with_config<R, S>(
191    request: R,
192    stream: S,
193    config: Option<WebSocketConfig>,
194) -> Result<(WebSocketStream<ClientStream<S>>, Response), Error>
195where
196    R: IntoClientRequest + Unpin,
197    S: 'static + AsyncRead + AsyncWrite + Unpin,
198    AutoStream<S>: Unpin,
199{
200    client_async_tls_with_connector_and_config(request, stream, None, config).await
201}
202
203#[cfg(feature = "async-native-tls")]
204/// Creates a WebSocket handshake from a request and a stream,
205/// upgrading the stream to TLS if required and using the given
206/// connector.
207pub async fn client_async_tls_with_connector<R, S>(
208    request: R,
209    stream: S,
210    connector: Option<Connector>,
211) -> Result<(WebSocketStream<ClientStream<S>>, Response), Error>
212where
213    R: IntoClientRequest + Unpin,
214    S: 'static + AsyncRead + AsyncWrite + Unpin,
215    AutoStream<S>: Unpin,
216{
217    client_async_tls_with_connector_and_config(request, stream, connector, None).await
218}
219
220/// Type alias for the stream type of the `connect_async()` functions.
221pub type ConnectStream = ClientStream<TcpStream>;
222
223/// Connect to a given URL.
224pub async fn connect_async<R>(
225    request: R,
226) -> Result<(WebSocketStream<ConnectStream>, Response), Error>
227where
228    R: IntoClientRequest + Unpin,
229{
230    connect_async_with_config(request, None).await
231}
232
233/// Connect to a given URL with a given WebSocket configuration.
234pub async fn connect_async_with_config<R>(
235    request: R,
236    config: Option<WebSocketConfig>,
237) -> Result<(WebSocketStream<ConnectStream>, Response), Error>
238where
239    R: IntoClientRequest + Unpin,
240{
241    let request: Request = request.into_client_request()?;
242
243    let domain = domain(&request)?;
244    let port = port(&request)?;
245
246    let try_socket = TcpStream::connect((domain.as_str(), port)).await;
247    let socket = try_socket.map_err(Error::Io)?;
248    client_async_tls_with_connector_and_config(request, socket, None, config).await
249}
250
251#[cfg(any(feature = "async-tls", feature = "async-native-tls"))]
252/// Connect to a given URL using the provided TLS connector.
253pub async fn connect_async_with_tls_connector<R>(
254    request: R,
255    connector: Option<Connector>,
256) -> Result<(WebSocketStream<ConnectStream>, Response), Error>
257where
258    R: IntoClientRequest + Unpin,
259{
260    connect_async_with_tls_connector_and_config(request, connector, None).await
261}
262
263#[cfg(any(feature = "async-tls", feature = "async-native-tls"))]
264/// Connect to a given URL using the provided TLS connector.
265pub async fn connect_async_with_tls_connector_and_config<R>(
266    request: R,
267    connector: Option<Connector>,
268    config: Option<WebSocketConfig>,
269) -> Result<(WebSocketStream<ConnectStream>, Response), Error>
270where
271    R: IntoClientRequest + Unpin,
272{
273    let request: Request = request.into_client_request()?;
274
275    let domain = domain(&request)?;
276    let port = port(&request)?;
277
278    let try_socket = TcpStream::connect((domain.as_str(), port)).await;
279    let socket = try_socket.map_err(Error::Io)?;
280    client_async_tls_with_connector_and_config(request, socket, connector, config).await
281}