Skip to main content

async_tungstenite/
lib.rs

1//! Async WebSockets.
2//!
3//! This crate is based on [tungstenite](https://crates.io/crates/tungstenite)
4//! Rust WebSocket library and provides async bindings and wrappers for it, so you
5//! can use it with non-blocking/asynchronous `TcpStream`s from and couple it
6//! together with other crates from the async stack. In addition, optional
7//! integration with various other crates can be enabled via feature flags
8//!
9//!  * `async-tls`: Enables the `async_tls` module, which provides integration
10//!    with the [async-tls](https://crates.io/crates/async-tls) TLS stack and can
11//!    be used independent of any async runtime.
12//!  * `async-std-runtime`: Enables the `async_std` module, which provides
13//!    integration with the [async-std](https://async.rs) runtime.
14//!  * `async-native-tls`: Enables the additional functions in the `async_std`
15//!    module to implement TLS via
16//!    [async-native-tls](https://crates.io/crates/async-native-tls).
17//!  * `tokio-runtime`: Enables the `tokio` module, which provides integration
18//!    with the [tokio](https://tokio.rs) runtime.
19//!  * `tokio-native-tls`: Enables the additional functions in the `tokio` module to
20//!    implement TLS via [tokio-native-tls](https://crates.io/crates/tokio-native-tls).
21//!  * `tokio-rustls-native-certs`: Enables the additional functions in the `tokio`
22//!    module to implement TLS via [tokio-rustls](https://crates.io/crates/tokio-rustls)
23//!    and uses native system certificates found with
24//!    [rustls-native-certs](https://github.com/rustls/rustls-native-certs).
25//!  * `tokio-rustls-platform-verifier`: Enables the additional functions in the `tokio`
26//!    module to implement TLS via [tokio-rustls](https://crates.io/crates/tokio-rustls)
27//!    and uses native system certificates via the platform verifier found with
28//!    [rustls-platform-verifier](https://github.com/rustls/rustls-platform-verifier).
29//!  * `tokio-rustls-webpki-roots`: Enables the additional functions in the `tokio`
30//!    module to implement TLS via [tokio-rustls](https://crates.io/crates/tokio-rustls)
31//!    and uses the certificates [webpki-roots](https://github.com/rustls/webpki-roots)
32//!    provides.
33//!  * `tokio-openssl`: Enables the additional functions in the `tokio` module to
34//!    implement TLS via [tokio-openssl](https://crates.io/crates/tokio-openssl).
35//!  * `gio-runtime`: Enables the `gio` module, which provides integration with
36//!    the [gio](https://www.gtk-rs.org) runtime.
37//!
38//! Each WebSocket stream implements the required `Stream` and `Sink` traits,
39//! making the socket a stream of WebSocket messages coming in and going out.
40
41#![deny(
42    missing_docs,
43    unused_must_use,
44    unused_mut,
45    unused_imports,
46    unused_import_braces
47)]
48
49pub use tungstenite;
50
51mod compat;
52mod handshake;
53
54#[cfg(any(
55    feature = "async-tls",
56    feature = "async-native-tls",
57    feature = "smol-native-tls",
58    feature = "futures-rustls-manual-roots",
59    feature = "futures-rustls-webpki-roots",
60    feature = "futures-rustls-native-certs",
61    feature = "futures-rustls-platform-verifier",
62    feature = "tokio-native-tls",
63    feature = "tokio-rustls-manual-roots",
64    feature = "tokio-rustls-native-certs",
65    feature = "tokio-rustls-platform-verifier",
66    feature = "tokio-rustls-webpki-roots",
67    feature = "tokio-openssl",
68))]
69pub mod stream;
70
71use std::{
72    io::{Read, Write},
73    pin::Pin,
74    sync::{Arc, Mutex, MutexGuard},
75    task::{ready, Context, Poll},
76};
77
78use compat::{cvt, AllowStd, ContextWaker};
79use futures_core::stream::{FusedStream, Stream};
80use futures_io::{AsyncRead, AsyncWrite};
81use log::*;
82
83#[cfg(feature = "handshake")]
84use tungstenite::{
85    client::IntoClientRequest,
86    handshake::{
87        client::{ClientHandshake, Response},
88        server::{Callback, NoCallback},
89        HandshakeError,
90    },
91};
92use tungstenite::{
93    error::Error as WsError,
94    protocol::{Message, Role, WebSocket, WebSocketConfig},
95};
96
97#[cfg(feature = "async-std-runtime")]
98#[deprecated = "async-std is unmaintained upstream. Please use the smol runtime instead."]
99pub mod async_std;
100#[cfg(feature = "async-tls")]
101#[deprecated(
102    note = "async-tls has not seen an update for 2 years and depends on a vulnerable version of rustls. Use futures-rustls instead."
103)]
104pub mod async_tls;
105#[cfg(feature = "gio-runtime")]
106pub mod gio;
107#[cfg(feature = "smol-runtime")]
108pub mod smol;
109#[cfg(feature = "tokio-runtime")]
110pub mod tokio;
111
112pub mod bytes;
113pub use bytes::ByteReader;
114pub use bytes::ByteWriter;
115
116use tungstenite::protocol::CloseFrame;
117
118/// Creates a WebSocket handshake from a request and a stream.
119/// For convenience, the user may call this with a url string, a URL,
120/// or a `Request`. Calling with `Request` allows the user to add
121/// a WebSocket protocol or other custom headers.
122///
123/// Internally, this custom creates a handshake representation and returns
124/// a future representing the resolution of the WebSocket handshake. The
125/// returned future will resolve to either `WebSocketStream<S>` or `Error`
126/// depending on whether the handshake is successful.
127///
128/// This is typically used for clients who have already established, for
129/// example, a TCP connection to the remote server.
130#[cfg(feature = "handshake")]
131pub async fn client_async<'a, R, S>(
132    request: R,
133    stream: S,
134) -> Result<(WebSocketStream<S>, Response), WsError>
135where
136    R: IntoClientRequest + Unpin,
137    S: AsyncRead + AsyncWrite + Unpin,
138{
139    client_async_with_config(request, stream, None).await
140}
141
142/// The same as `client_async()` but the one can specify a websocket configuration.
143/// Please refer to `client_async()` for more details.
144#[cfg(feature = "handshake")]
145pub async fn client_async_with_config<'a, R, S>(
146    request: R,
147    stream: S,
148    config: Option<WebSocketConfig>,
149) -> Result<(WebSocketStream<S>, Response), WsError>
150where
151    R: IntoClientRequest + Unpin,
152    S: AsyncRead + AsyncWrite + Unpin,
153{
154    let f = handshake::client_handshake(stream, move |allow_std| {
155        let request = request.into_client_request()?;
156        let cli_handshake = ClientHandshake::start(allow_std, request, config)?;
157        cli_handshake.handshake()
158    });
159    f.await.map_err(|e| match e {
160        HandshakeError::Failure(e) => e,
161        e => WsError::Io(std::io::Error::new(
162            std::io::ErrorKind::Other,
163            e.to_string(),
164        )),
165    })
166}
167
168/// Accepts a new WebSocket connection with the provided stream.
169///
170/// This function will internally call `server::accept` to create a
171/// handshake representation and returns a future representing the
172/// resolution of the WebSocket handshake. The returned future will resolve
173/// to either `WebSocketStream<S>` or `Error` depending if it's successful
174/// or not.
175///
176/// This is typically used after a socket has been accepted from a
177/// `TcpListener`. That socket is then passed to this function to perform
178/// the server half of the accepting a client's websocket connection.
179#[cfg(feature = "handshake")]
180pub async fn accept_async<S>(stream: S) -> Result<WebSocketStream<S>, WsError>
181where
182    S: AsyncRead + AsyncWrite + Unpin,
183{
184    accept_hdr_async(stream, NoCallback).await
185}
186
187/// The same as `accept_async()` but the one can specify a websocket configuration.
188/// Please refer to `accept_async()` for more details.
189#[cfg(feature = "handshake")]
190pub async fn accept_async_with_config<S>(
191    stream: S,
192    config: Option<WebSocketConfig>,
193) -> Result<WebSocketStream<S>, WsError>
194where
195    S: AsyncRead + AsyncWrite + Unpin,
196{
197    accept_hdr_async_with_config(stream, NoCallback, config).await
198}
199
200/// Accepts a new WebSocket connection with the provided stream.
201///
202/// This function does the same as `accept_async()` but accepts an extra callback
203/// for header processing. The callback receives headers of the incoming
204/// requests and is able to add extra headers to the reply.
205#[cfg(feature = "handshake")]
206pub async fn accept_hdr_async<S, C>(stream: S, callback: C) -> Result<WebSocketStream<S>, WsError>
207where
208    S: AsyncRead + AsyncWrite + Unpin,
209    C: Callback + Unpin,
210{
211    accept_hdr_async_with_config(stream, callback, None).await
212}
213
214/// The same as `accept_hdr_async()` but the one can specify a websocket configuration.
215/// Please refer to `accept_hdr_async()` for more details.
216#[cfg(feature = "handshake")]
217pub async fn accept_hdr_async_with_config<S, C>(
218    stream: S,
219    callback: C,
220    config: Option<WebSocketConfig>,
221) -> Result<WebSocketStream<S>, WsError>
222where
223    S: AsyncRead + AsyncWrite + Unpin,
224    C: Callback + Unpin,
225{
226    let f = handshake::server_handshake(stream, move |allow_std| {
227        tungstenite::accept_hdr_with_config(allow_std, callback, config)
228    });
229    f.await.map_err(|e| match e {
230        HandshakeError::Failure(e) => e,
231        e => WsError::Io(std::io::Error::new(
232            std::io::ErrorKind::Other,
233            e.to_string(),
234        )),
235    })
236}
237
238/// A wrapper around an underlying raw stream which implements the WebSocket
239/// protocol.
240///
241/// A `WebSocketStream<S>` represents a handshake that has been completed
242/// successfully and both the server and the client are ready for receiving
243/// and sending data. Message from a `WebSocketStream<S>` are accessible
244/// through the respective `Stream` and `Sink`. Check more information about
245/// them in `futures-rs` crate documentation or have a look on the examples
246/// and unit tests for this crate.
247#[derive(Debug)]
248pub struct WebSocketStream<S> {
249    inner: WebSocket<AllowStd<S>>,
250    #[cfg(feature = "futures-03-sink")]
251    closing: bool,
252    ended: bool,
253    /// Tungstenite is probably ready to receive more data.
254    ///
255    /// `false` once start_send hits `WouldBlock` errors.
256    /// `true` initially and after `flush`ing.
257    ready: bool,
258}
259
260impl<S> WebSocketStream<S> {
261    /// Convert a raw socket into a WebSocketStream without performing a
262    /// handshake.
263    pub async fn from_raw_socket(stream: S, role: Role, config: Option<WebSocketConfig>) -> Self
264    where
265        S: AsyncRead + AsyncWrite + Unpin,
266    {
267        handshake::without_handshake(stream, move |allow_std| {
268            WebSocket::from_raw_socket(allow_std, role, config)
269        })
270        .await
271    }
272
273    /// Convert a raw socket into a WebSocketStream without performing a
274    /// handshake.
275    pub async fn from_partially_read(
276        stream: S,
277        part: Vec<u8>,
278        role: Role,
279        config: Option<WebSocketConfig>,
280    ) -> Self
281    where
282        S: AsyncRead + AsyncWrite + Unpin,
283    {
284        handshake::without_handshake(stream, move |allow_std| {
285            WebSocket::from_partially_read(allow_std, part, role, config)
286        })
287        .await
288    }
289
290    pub(crate) fn new(ws: WebSocket<AllowStd<S>>) -> Self {
291        Self {
292            inner: ws,
293            #[cfg(feature = "futures-03-sink")]
294            closing: false,
295            ended: false,
296            ready: true,
297        }
298    }
299
300    fn with_context<F, R>(&mut self, ctx: Option<(ContextWaker, &mut Context<'_>)>, f: F) -> R
301    where
302        F: FnOnce(&mut WebSocket<AllowStd<S>>) -> R,
303        AllowStd<S>: Read + Write,
304    {
305        #[cfg(feature = "verbose-logging")]
306        trace!("{}:{} WebSocketStream.with_context", file!(), line!());
307        if let Some((kind, ctx)) = ctx {
308            self.inner.get_mut().set_waker(kind, ctx.waker());
309        }
310        f(&mut self.inner)
311    }
312
313    /// Consumes the `WebSocketStream` and returns the underlying stream.
314    pub fn into_inner(self) -> S {
315        self.inner.into_inner().into_inner()
316    }
317
318    /// Returns a shared reference to the inner stream.
319    pub fn get_ref(&self) -> &S
320    where
321        S: AsyncRead + AsyncWrite + Unpin,
322    {
323        self.inner.get_ref().get_ref()
324    }
325
326    /// Returns a mutable reference to the inner stream.
327    pub fn get_mut(&mut self) -> &mut S
328    where
329        S: AsyncRead + AsyncWrite + Unpin,
330    {
331        self.inner.get_mut().get_mut()
332    }
333
334    /// Returns a reference to the configuration of the tungstenite stream.
335    pub fn get_config(&self) -> &WebSocketConfig {
336        self.inner.get_config()
337    }
338
339    /// Close the underlying web socket
340    pub async fn close(&mut self, msg: Option<CloseFrame>) -> Result<(), WsError>
341    where
342        S: AsyncRead + AsyncWrite + Unpin,
343    {
344        self.send(Message::Close(msg)).await
345    }
346
347    /// Splits the websocket stream into separate
348    /// [sender](WebSocketSender) and [receiver](WebSocketReceiver) parts.
349    pub fn split(self) -> (WebSocketSender<S>, WebSocketReceiver<S>) {
350        let shared = Arc::new(Shared(Mutex::new(self)));
351        let sender = WebSocketSender {
352            shared: shared.clone(),
353        };
354
355        let receiver = WebSocketReceiver { shared };
356        (sender, receiver)
357    }
358
359    /// Attempts to reunite the [sender](WebSocketSender) and [receiver](WebSocketReceiver)
360    /// parts back into a single stream. If both parts originate from the same
361    /// [`split`](WebSocketStream::split) call, returns `Ok` with the original stream.
362    /// Otherwise, returns `Err` containing the provided parts.
363    pub fn reunite(
364        sender: WebSocketSender<S>,
365        receiver: WebSocketReceiver<S>,
366    ) -> Result<Self, (WebSocketSender<S>, WebSocketReceiver<S>)> {
367        if sender.is_pair_of(&receiver) {
368            drop(receiver);
369            let stream = Arc::try_unwrap(sender.shared)
370                .ok()
371                .expect("reunite the stream")
372                .into_inner();
373
374            Ok(stream)
375        } else {
376            Err((sender, receiver))
377        }
378    }
379}
380
381impl<S> WebSocketStream<S>
382where
383    S: AsyncRead + AsyncWrite + Unpin,
384{
385    fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Message, WsError>>> {
386        #[cfg(feature = "verbose-logging")]
387        trace!("{}:{} WebSocketStream.poll_next", file!(), line!());
388
389        // The connection has been closed or a critical error has occurred.
390        // We have already returned the error to the user, the `Stream` is unusable,
391        // so we assume that the stream has been "fused".
392        if self.ended {
393            return Poll::Ready(None);
394        }
395
396        match ready!(self.with_context(Some((ContextWaker::Read, cx)), |s| {
397            #[cfg(feature = "verbose-logging")]
398            trace!(
399                "{}:{} WebSocketStream.with_context poll_next -> read()",
400                file!(),
401                line!()
402            );
403            cvt(s.read())
404        })) {
405            Ok(v) => Poll::Ready(Some(Ok(v))),
406            Err(e) => {
407                self.ended = true;
408                if matches!(e, WsError::AlreadyClosed | WsError::ConnectionClosed) {
409                    Poll::Ready(None)
410                } else {
411                    Poll::Ready(Some(Err(e)))
412                }
413            }
414        }
415    }
416
417    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), WsError>> {
418        if self.ready {
419            return Poll::Ready(Ok(()));
420        }
421
422        // Currently blocked so try to flush the blockage away
423        self.with_context(Some((ContextWaker::Write, cx)), |s| cvt(s.flush()))
424            .map(|r| {
425                self.ready = true;
426                r
427            })
428    }
429
430    fn start_send(&mut self, item: Message) -> Result<(), WsError> {
431        match self.with_context(None, |s| s.write(item)) {
432            Ok(()) => {
433                self.ready = true;
434                Ok(())
435            }
436            Err(WsError::Io(err)) if err.kind() == std::io::ErrorKind::WouldBlock => {
437                // the message was accepted and queued so not an error
438                // but `poll_ready` will now start trying to flush the block
439                self.ready = false;
440                Ok(())
441            }
442            Err(e) => {
443                self.ready = true;
444                debug!("websocket start_send error: {}", e);
445                Err(e)
446            }
447        }
448    }
449
450    fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), WsError>> {
451        self.with_context(Some((ContextWaker::Write, cx)), |s| cvt(s.flush()))
452            .map(|r| {
453                self.ready = true;
454                match r {
455                    // WebSocket connection has just been closed. Flushing completed, not an error.
456                    Err(WsError::ConnectionClosed) => Ok(()),
457                    other => other,
458                }
459            })
460    }
461
462    #[cfg(feature = "futures-03-sink")]
463    fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), WsError>> {
464        self.ready = true;
465        let res = if self.closing {
466            // After queueing it, we call `flush` to drive the close handshake to completion.
467            self.with_context(Some((ContextWaker::Write, cx)), |s| s.flush())
468        } else {
469            self.with_context(Some((ContextWaker::Write, cx)), |s| s.close(None))
470        };
471
472        match res {
473            Ok(()) => Poll::Ready(Ok(())),
474            Err(WsError::ConnectionClosed) => Poll::Ready(Ok(())),
475            Err(WsError::Io(err)) if err.kind() == std::io::ErrorKind::WouldBlock => {
476                trace!("WouldBlock");
477                self.closing = true;
478                Poll::Pending
479            }
480            Err(err) => {
481                debug!("websocket close error: {}", err);
482                Poll::Ready(Err(err))
483            }
484        }
485    }
486}
487
488impl<S> Stream for WebSocketStream<S>
489where
490    S: AsyncRead + AsyncWrite + Unpin,
491{
492    type Item = Result<Message, WsError>;
493
494    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
495        self.get_mut().poll_next(cx)
496    }
497}
498
499impl<S> FusedStream for WebSocketStream<S>
500where
501    S: AsyncRead + AsyncWrite + Unpin,
502{
503    fn is_terminated(&self) -> bool {
504        self.ended
505    }
506}
507
508#[cfg(feature = "futures-03-sink")]
509impl<S> futures_util::Sink<Message> for WebSocketStream<S>
510where
511    S: AsyncRead + AsyncWrite + Unpin,
512{
513    type Error = WsError;
514
515    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
516        self.get_mut().poll_ready(cx)
517    }
518
519    fn start_send(self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
520        self.get_mut().start_send(item)
521    }
522
523    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
524        self.get_mut().poll_flush(cx)
525    }
526
527    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
528        self.get_mut().poll_close(cx)
529    }
530}
531
532#[cfg(not(feature = "futures-03-sink"))]
533impl<S> bytes::private::SealedSender for WebSocketStream<S>
534where
535    S: AsyncRead + AsyncWrite + Unpin,
536{
537    fn poll_write(
538        self: Pin<&mut Self>,
539        cx: &mut Context<'_>,
540        buf: &[u8],
541    ) -> Poll<Result<usize, WsError>> {
542        let me = self.get_mut();
543        ready!(me.poll_ready(cx))?;
544        let len = buf.len();
545        me.start_send(Message::binary(buf.to_owned()))?;
546        Poll::Ready(Ok(len))
547    }
548
549    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), WsError>> {
550        self.get_mut().poll_flush(cx)
551    }
552
553    fn poll_close(
554        self: Pin<&mut Self>,
555        cx: &mut Context<'_>,
556        msg: &mut Option<Message>,
557    ) -> Poll<Result<(), WsError>> {
558        let me = self.get_mut();
559        send_helper(me, msg, cx)
560    }
561}
562
563impl<S> WebSocketStream<S> {
564    /// Simple send method to replace `futures_sink::Sink` (till v0.3).
565    pub async fn send(&mut self, msg: Message) -> Result<(), WsError>
566    where
567        S: AsyncRead + AsyncWrite + Unpin,
568    {
569        Send {
570            ws: self,
571            msg: Some(msg),
572        }
573        .await
574    }
575}
576
577struct Send<W> {
578    ws: W,
579    msg: Option<Message>,
580}
581
582/// Performs an asynchronous message send to the websocket.
583fn send_helper<S>(
584    ws: &mut WebSocketStream<S>,
585    msg: &mut Option<Message>,
586    cx: &mut Context<'_>,
587) -> Poll<Result<(), WsError>>
588where
589    S: AsyncRead + AsyncWrite + Unpin,
590{
591    if msg.is_some() {
592        ready!(ws.poll_ready(cx))?;
593        let msg = msg.take().expect("unreachable");
594        ws.start_send(msg)?;
595    }
596
597    ws.poll_flush(cx)
598}
599
600impl<S> std::future::Future for Send<&mut WebSocketStream<S>>
601where
602    S: AsyncRead + AsyncWrite + Unpin,
603{
604    type Output = Result<(), WsError>;
605
606    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
607        let me = self.get_mut();
608        send_helper(me.ws, &mut me.msg, cx)
609    }
610}
611
612impl<S> std::future::Future for Send<&Shared<S>>
613where
614    S: AsyncRead + AsyncWrite + Unpin,
615{
616    type Output = Result<(), WsError>;
617
618    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
619        let me = self.get_mut();
620        let mut ws = me.ws.lock();
621        send_helper(&mut ws, &mut me.msg, cx)
622    }
623}
624
625/// The sender part of a [websocket](WebSocketStream) stream.
626#[derive(Debug)]
627pub struct WebSocketSender<S> {
628    shared: Arc<Shared<S>>,
629}
630
631impl<S> WebSocketSender<S> {
632    /// Send a message via [websocket](WebSocketStream).
633    pub async fn send(&mut self, msg: Message) -> Result<(), WsError>
634    where
635        S: AsyncRead + AsyncWrite + Unpin,
636    {
637        Send {
638            ws: &*self.shared,
639            msg: Some(msg),
640        }
641        .await
642    }
643
644    /// Close the underlying [websocket](WebSocketStream).
645    pub async fn close(&mut self, msg: Option<CloseFrame>) -> Result<(), WsError>
646    where
647        S: AsyncRead + AsyncWrite + Unpin,
648    {
649        self.send(Message::Close(msg)).await
650    }
651
652    /// Checks if this [sender](WebSocketSender) and some [receiver](WebSocketReceiver)
653    /// were split from the same [websocket](WebSocketStream) stream.
654    pub fn is_pair_of(&self, other: &WebSocketReceiver<S>) -> bool {
655        Arc::ptr_eq(&self.shared, &other.shared)
656    }
657}
658
659#[cfg(feature = "futures-03-sink")]
660impl<T> futures_util::Sink<Message> for WebSocketSender<T>
661where
662    T: AsyncRead + AsyncWrite + Unpin,
663{
664    type Error = WsError;
665
666    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
667        self.shared.lock().poll_ready(cx)
668    }
669
670    fn start_send(self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
671        self.shared.lock().start_send(item)
672    }
673
674    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
675        self.shared.lock().poll_flush(cx)
676    }
677
678    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
679        self.shared.lock().poll_close(cx)
680    }
681}
682
683#[cfg(not(feature = "futures-03-sink"))]
684impl<S> bytes::private::SealedSender for WebSocketSender<S>
685where
686    S: AsyncRead + AsyncWrite + Unpin,
687{
688    fn poll_write(
689        self: Pin<&mut Self>,
690        cx: &mut Context<'_>,
691        buf: &[u8],
692    ) -> Poll<Result<usize, WsError>> {
693        let me = self.get_mut();
694        let mut ws = me.shared.lock();
695        ready!(ws.poll_ready(cx))?;
696        let len = buf.len();
697        ws.start_send(Message::binary(buf.to_owned()))?;
698        Poll::Ready(Ok(len))
699    }
700
701    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), WsError>> {
702        self.shared.lock().poll_flush(cx)
703    }
704
705    fn poll_close(
706        self: Pin<&mut Self>,
707        cx: &mut Context<'_>,
708        msg: &mut Option<Message>,
709    ) -> Poll<Result<(), WsError>> {
710        let me = self.get_mut();
711        let mut ws = me.shared.lock();
712        send_helper(&mut ws, msg, cx)
713    }
714}
715
716/// The receiver part of a [websocket](WebSocketStream) stream.
717#[derive(Debug)]
718pub struct WebSocketReceiver<S> {
719    shared: Arc<Shared<S>>,
720}
721
722impl<S> WebSocketReceiver<S> {
723    /// Checks if this [receiver](WebSocketReceiver) and some [sender](WebSocketSender)
724    /// were split from the same [websocket](WebSocketStream) stream.
725    pub fn is_pair_of(&self, other: &WebSocketSender<S>) -> bool {
726        Arc::ptr_eq(&self.shared, &other.shared)
727    }
728}
729
730impl<S> Stream for WebSocketReceiver<S>
731where
732    S: AsyncRead + AsyncWrite + Unpin,
733{
734    type Item = Result<Message, WsError>;
735
736    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
737        self.shared.lock().poll_next(cx)
738    }
739}
740
741impl<S> FusedStream for WebSocketReceiver<S>
742where
743    S: AsyncRead + AsyncWrite + Unpin,
744{
745    fn is_terminated(&self) -> bool {
746        self.shared.lock().ended
747    }
748}
749
750#[derive(Debug)]
751struct Shared<S>(Mutex<WebSocketStream<S>>);
752
753impl<S> Shared<S> {
754    fn lock(&self) -> MutexGuard<'_, WebSocketStream<S>> {
755        self.0.lock().expect("lock shared stream")
756    }
757
758    fn into_inner(self) -> WebSocketStream<S> {
759        self.0.into_inner().expect("get shared stream")
760    }
761}
762
763#[cfg(any(
764    feature = "async-tls",
765    feature = "async-std-runtime",
766    feature = "smol-runtime",
767    feature = "tokio-runtime",
768    feature = "gio-runtime"
769))]
770/// Get a domain from an URL.
771#[inline]
772pub(crate) fn domain(
773    request: &tungstenite::handshake::client::Request,
774) -> Result<String, tungstenite::Error> {
775    request
776        .uri()
777        .host()
778        .map(|host| {
779            // If host is an IPv6 address, it might be surrounded by brackets. These brackets are
780            // *not* part of a valid IP, so they must be stripped out.
781            //
782            // The URI from the request is guaranteed to be valid, so we don't need a separate
783            // check for the closing bracket.
784            let host = if host.starts_with('[') {
785                &host[1..host.len() - 1]
786            } else {
787                host
788            };
789
790            host.to_owned()
791        })
792        .ok_or(tungstenite::Error::Url(
793            tungstenite::error::UrlError::NoHostName,
794        ))
795}
796
797#[cfg(any(
798    feature = "async-std-runtime",
799    feature = "smol-runtime",
800    feature = "tokio-runtime",
801    feature = "gio-runtime"
802))]
803/// Get the port from an URL.
804#[inline]
805pub(crate) fn port(
806    request: &tungstenite::handshake::client::Request,
807) -> Result<u16, tungstenite::Error> {
808    request
809        .uri()
810        .port_u16()
811        .or_else(|| match request.uri().scheme_str() {
812            Some("wss") => Some(443),
813            Some("ws") => Some(80),
814            _ => None,
815        })
816        .ok_or(tungstenite::Error::Url(
817            tungstenite::error::UrlError::UnsupportedUrlScheme,
818        ))
819}
820
821#[cfg(test)]
822mod tests {
823    #[cfg(any(
824        feature = "async-tls",
825        feature = "async-std-runtime",
826        feature = "smol-runtime",
827        feature = "tokio-runtime",
828        feature = "gio-runtime"
829    ))]
830    #[test]
831    fn domain_strips_ipv6_brackets() {
832        use tungstenite::client::IntoClientRequest;
833
834        let request = "ws://[::1]:80".into_client_request().unwrap();
835        assert_eq!(crate::domain(&request).unwrap(), "::1");
836    }
837
838    #[cfg(feature = "handshake")]
839    #[test]
840    fn requests_cannot_contain_invalid_uris() {
841        use tungstenite::client::IntoClientRequest;
842
843        assert!("ws://[".into_client_request().is_err());
844        assert!("ws://[blabla/bla".into_client_request().is_err());
845        assert!("ws://[::1/bla".into_client_request().is_err());
846    }
847}