Skip to main content

tokio_rustls/
lib.rs

1//! Asynchronous TLS/SSL streams for Tokio using [Rustls](https://github.com/rustls/rustls).
2//!
3//! # Why do I need to call `poll_flush`?
4//!
5//! Most TLS implementations will have an internal buffer to improve throughput,
6//! and rustls is no exception.
7//!
8//! When we write data to `TlsStream`, we always write rustls buffer first,
9//! then take out rustls encrypted data packet, and write it to data channel (like TcpStream).
10//! When data channel is pending, some data may remain in rustls buffer.
11//!
12//! `tokio-rustls` To keep it simple and correct, [TlsStream] will behave like `BufWriter`.
13//! For `TlsStream<TcpStream>`, this means that data written by `poll_write` is not guaranteed to be written to `TcpStream`.
14//! You must call `poll_flush` to ensure that it is written to `TcpStream`.
15//!
16//! You should call `poll_flush` at the appropriate time,
17//! such as when a period of `poll_write` write is complete and there is no more data to write.
18//!
19//! ## Why don't we write during `poll_read`?
20//!
21//! We did this in the early days of `tokio-rustls`, but it caused some bugs.
22//! We can solve these bugs through some solutions, but this will cause performance degradation (reverse false wakeup).
23//!
24//! And reverse write will also prevent us implement full duplex in the future.
25//!
26//! see <https://github.com/tokio-rs/tls/issues/40>
27//!
28//! ## Why can't we handle it like `native-tls`?
29//!
30//! When data channel returns to pending, `native-tls` will falsely report the number of bytes it consumes.
31//! This means that if data written by `poll_write` is not actually written to data channel, it will not return `Ready`.
32//! Thus avoiding the call of `poll_flush`.
33//!
34//! But this does not conform to the convention of the `AsyncWrite` trait.
35//! This means that if you give inconsistent data in two `poll_write`, it may cause unexpected behavior.
36//!
37//! see <https://github.com/tokio-rs/tls/issues/41>
38
39#![warn(unreachable_pub, clippy::use_self)]
40
41use std::io;
42#[cfg(unix)]
43use std::os::unix::io::{AsRawFd, RawFd};
44#[cfg(windows)]
45use std::os::windows::io::{AsRawSocket, RawSocket};
46use std::pin::Pin;
47use std::task::{Context, Poll};
48
49pub use rustls;
50use rustls::CommonState;
51use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
52
53macro_rules! ready {
54    ( $e:expr ) => {
55        match $e {
56            std::task::Poll::Ready(t) => t,
57            std::task::Poll::Pending => return std::task::Poll::Pending,
58        }
59    };
60}
61
62pub mod client;
63pub use client::{Connect, FallibleConnect, TlsConnector, TlsConnectorWithAlpn};
64mod common;
65pub mod server;
66pub use server::{Accept, FallibleAccept, LazyConfigAcceptor, StartHandshake, TlsAcceptor};
67
68/// Unified TLS stream type
69///
70/// This abstracts over the inner `client::TlsStream` and `server::TlsStream`, so you can use
71/// a single type to keep both client- and server-initiated TLS-encrypted connections.
72#[allow(clippy::large_enum_variant)] // https://github.com/rust-lang/rust-clippy/issues/9798
73#[derive(Debug)]
74pub enum TlsStream<T> {
75    Client(client::TlsStream<T>),
76    Server(server::TlsStream<T>),
77}
78
79impl<T> TlsStream<T> {
80    pub fn get_ref(&self) -> (&T, &CommonState) {
81        use TlsStream::*;
82        match self {
83            Client(io) => {
84                let (io, session) = io.get_ref();
85                (io, session)
86            }
87            Server(io) => {
88                let (io, session) = io.get_ref();
89                (io, session)
90            }
91        }
92    }
93
94    pub fn get_mut(&mut self) -> (&mut T, &mut CommonState) {
95        use TlsStream::*;
96        match self {
97            Client(io) => {
98                let (io, session) = io.get_mut();
99                (io, &mut *session)
100            }
101            Server(io) => {
102                let (io, session) = io.get_mut();
103                (io, &mut *session)
104            }
105        }
106    }
107}
108
109impl<T> From<client::TlsStream<T>> for TlsStream<T> {
110    fn from(s: client::TlsStream<T>) -> Self {
111        Self::Client(s)
112    }
113}
114
115impl<T> From<server::TlsStream<T>> for TlsStream<T> {
116    fn from(s: server::TlsStream<T>) -> Self {
117        Self::Server(s)
118    }
119}
120
121#[cfg(unix)]
122impl<S> AsRawFd for TlsStream<S>
123where
124    S: AsRawFd,
125{
126    fn as_raw_fd(&self) -> RawFd {
127        self.get_ref().0.as_raw_fd()
128    }
129}
130
131#[cfg(windows)]
132impl<S> AsRawSocket for TlsStream<S>
133where
134    S: AsRawSocket,
135{
136    fn as_raw_socket(&self) -> RawSocket {
137        self.get_ref().0.as_raw_socket()
138    }
139}
140
141impl<T> AsyncRead for TlsStream<T>
142where
143    T: AsyncRead + AsyncWrite + Unpin,
144{
145    #[inline]
146    fn poll_read(
147        self: Pin<&mut Self>,
148        cx: &mut Context<'_>,
149        buf: &mut ReadBuf<'_>,
150    ) -> Poll<io::Result<()>> {
151        match self.get_mut() {
152            Self::Client(x) => Pin::new(x).poll_read(cx, buf),
153            Self::Server(x) => Pin::new(x).poll_read(cx, buf),
154        }
155    }
156}
157
158impl<T> AsyncBufRead for TlsStream<T>
159where
160    T: AsyncRead + AsyncWrite + Unpin,
161{
162    #[inline]
163    fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
164        match self.get_mut() {
165            Self::Client(x) => Pin::new(x).poll_fill_buf(cx),
166            Self::Server(x) => Pin::new(x).poll_fill_buf(cx),
167        }
168    }
169
170    #[inline]
171    fn consume(self: Pin<&mut Self>, amt: usize) {
172        match self.get_mut() {
173            Self::Client(x) => Pin::new(x).consume(amt),
174            Self::Server(x) => Pin::new(x).consume(amt),
175        }
176    }
177}
178
179impl<T> AsyncWrite for TlsStream<T>
180where
181    T: AsyncRead + AsyncWrite + Unpin,
182{
183    #[inline]
184    fn poll_write(
185        self: Pin<&mut Self>,
186        cx: &mut Context<'_>,
187        buf: &[u8],
188    ) -> Poll<io::Result<usize>> {
189        match self.get_mut() {
190            Self::Client(x) => Pin::new(x).poll_write(cx, buf),
191            Self::Server(x) => Pin::new(x).poll_write(cx, buf),
192        }
193    }
194
195    #[inline]
196    fn poll_write_vectored(
197        self: Pin<&mut Self>,
198        cx: &mut Context<'_>,
199        bufs: &[io::IoSlice<'_>],
200    ) -> Poll<io::Result<usize>> {
201        match self.get_mut() {
202            Self::Client(x) => Pin::new(x).poll_write_vectored(cx, bufs),
203            Self::Server(x) => Pin::new(x).poll_write_vectored(cx, bufs),
204        }
205    }
206
207    #[inline]
208    fn is_write_vectored(&self) -> bool {
209        match self {
210            Self::Client(x) => x.is_write_vectored(),
211            Self::Server(x) => x.is_write_vectored(),
212        }
213    }
214
215    #[inline]
216    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
217        match self.get_mut() {
218            Self::Client(x) => Pin::new(x).poll_flush(cx),
219            Self::Server(x) => Pin::new(x).poll_flush(cx),
220        }
221    }
222
223    #[inline]
224    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
225        match self.get_mut() {
226            Self::Client(x) => Pin::new(x).poll_shutdown(cx),
227            Self::Server(x) => Pin::new(x).poll_shutdown(cx),
228        }
229    }
230}