Skip to main content

compio_tls/
adapter.rs

1use std::{fmt::Debug, io};
2
3use futures_util::{AsyncRead, AsyncWrite};
4
5use crate::TlsStream;
6
7#[derive(Clone)]
8enum TlsConnectorInner {
9    #[cfg(feature = "native-tls")]
10    NativeTls(crate::native::TlsConnector),
11    #[cfg(feature = "rustls")]
12    Rustls(futures_rustls::TlsConnector),
13    #[cfg(feature = "py-dynamic-openssl")]
14    PyDynamicOpenSsl(crate::py_ossl::TlsConnector),
15    #[cfg(not(any(
16        feature = "native-tls",
17        feature = "rustls",
18        feature = "py-dynamic-openssl"
19    )))]
20    None(std::convert::Infallible),
21}
22
23impl Debug for TlsConnectorInner {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            #[cfg(feature = "native-tls")]
27            Self::NativeTls(_) => f.debug_tuple("NativeTls").finish(),
28            #[cfg(feature = "rustls")]
29            Self::Rustls(_) => f.debug_tuple("Rustls").finish(),
30            #[cfg(feature = "py-dynamic-openssl")]
31            Self::PyDynamicOpenSsl(_) => f.debug_tuple("PyDynamicOpenSsl").finish(),
32            #[cfg(not(any(
33                feature = "native-tls",
34                feature = "rustls",
35                feature = "py-dynamic-openssl"
36            )))]
37            Self::None(f) => match *f {},
38        }
39    }
40}
41
42/// A wrapper around a [`native_tls::TlsConnector`] or [`rustls::ClientConfig`],
43/// providing an async `connect` method.
44#[derive(Debug, Clone)]
45pub struct TlsConnector(TlsConnectorInner);
46
47#[cfg(feature = "native-tls")]
48impl From<native_tls::TlsConnector> for TlsConnector {
49    fn from(value: native_tls::TlsConnector) -> Self {
50        Self(TlsConnectorInner::NativeTls(value.into()))
51    }
52}
53
54#[cfg(feature = "rustls")]
55impl From<std::sync::Arc<rustls::ClientConfig>> for TlsConnector {
56    fn from(value: std::sync::Arc<rustls::ClientConfig>) -> Self {
57        Self(TlsConnectorInner::Rustls(value.into()))
58    }
59}
60
61#[cfg(feature = "py-dynamic-openssl")]
62#[doc(hidden)]
63impl From<compio_py_dynamic_openssl::SSLContext> for TlsConnector {
64    fn from(value: compio_py_dynamic_openssl::SSLContext) -> Self {
65        Self(TlsConnectorInner::PyDynamicOpenSsl(value.into()))
66    }
67}
68
69impl TlsConnector {
70    /// Connects the provided stream with this connector, assuming the provided
71    /// domain.
72    ///
73    /// This function will internally call `TlsConnector::connect` to connect
74    /// the stream and returns a future representing the resolution of the
75    /// connection operation. The returned future will resolve to either
76    /// `TlsStream<S>` or `Error` depending if it's successful or not.
77    ///
78    /// This is typically used for clients who have already established, for
79    /// example, a TCP connection to a remote server. That stream is then
80    /// provided here to perform the client half of a connection to a
81    /// TLS-powered server.
82    pub async fn connect<S>(&self, domain: &str, stream: S) -> io::Result<TlsStream<S>>
83    where
84        S: AsyncRead + AsyncWrite + Unpin,
85    {
86        match &self.0 {
87            #[cfg(feature = "native-tls")]
88            TlsConnectorInner::NativeTls(c) => {
89                let client = c.connect(domain, stream).await?;
90                Ok(TlsStream::from(client))
91            }
92            #[cfg(feature = "rustls")]
93            TlsConnectorInner::Rustls(c) => {
94                let client = c
95                    .connect(
96                        domain.to_string().try_into().map_err(io::Error::other)?,
97                        stream,
98                    )
99                    .await?;
100                Ok(TlsStream::from(client))
101            }
102            #[cfg(feature = "py-dynamic-openssl")]
103            TlsConnectorInner::PyDynamicOpenSsl(c) => {
104                let client = c.connect(domain, stream).await?;
105                Ok(TlsStream::from(client))
106            }
107            #[cfg(not(any(
108                feature = "native-tls",
109                feature = "rustls",
110                feature = "py-dynamic-openssl"
111            )))]
112            TlsConnectorInner::None(f) => match *f {},
113        }
114    }
115}
116
117#[derive(Clone)]
118enum TlsAcceptorInner {
119    #[cfg(feature = "native-tls")]
120    NativeTls(crate::native::TlsAcceptor),
121    #[cfg(feature = "rustls")]
122    Rustls(futures_rustls::TlsAcceptor),
123    #[cfg(feature = "py-dynamic-openssl")]
124    PyDynamicOpenSsl(crate::py_ossl::TlsAcceptor),
125    #[cfg(not(any(
126        feature = "native-tls",
127        feature = "rustls",
128        feature = "py-dynamic-openssl"
129    )))]
130    None(std::convert::Infallible),
131}
132
133/// A wrapper around a [`native_tls::TlsAcceptor`] or [`rustls::ServerConfig`],
134/// providing an async `accept` method.
135///
136/// [`native_tls::TlsAcceptor`]: https://docs.rs/native-tls/latest/native_tls/struct.TlsAcceptor.html
137/// [`rustls::ServerConfig`]: https://docs.rs/rustls/latest/rustls/server/struct.ServerConfig.html
138#[derive(Clone)]
139pub struct TlsAcceptor(TlsAcceptorInner);
140
141#[cfg(feature = "native-tls")]
142impl From<native_tls::TlsAcceptor> for TlsAcceptor {
143    fn from(value: native_tls::TlsAcceptor) -> Self {
144        Self(TlsAcceptorInner::NativeTls(value.into()))
145    }
146}
147
148#[cfg(feature = "rustls")]
149impl From<std::sync::Arc<rustls::ServerConfig>> for TlsAcceptor {
150    fn from(value: std::sync::Arc<rustls::ServerConfig>) -> Self {
151        Self(TlsAcceptorInner::Rustls(value.into()))
152    }
153}
154
155#[cfg(feature = "py-dynamic-openssl")]
156impl From<compio_py_dynamic_openssl::SSLContext> for TlsAcceptor {
157    fn from(value: compio_py_dynamic_openssl::SSLContext) -> Self {
158        Self(TlsAcceptorInner::PyDynamicOpenSsl(value.into()))
159    }
160}
161
162impl TlsAcceptor {
163    /// Accepts a new client connection with the provided stream.
164    ///
165    /// This function will internally call `TlsAcceptor::accept` to connect
166    /// the stream and returns a future representing the resolution of the
167    /// connection operation. The returned future will resolve to either
168    /// `TlsStream<S>` or `Error` depending if it's successful or not.
169    ///
170    /// This is typically used after a new socket has been accepted from a
171    /// `TcpListener`. That socket is then passed to this function to perform
172    /// the server half of accepting a client connection.
173    pub async fn accept<S>(&self, stream: S) -> io::Result<TlsStream<S>>
174    where
175        S: AsyncRead + AsyncWrite + Unpin,
176    {
177        match &self.0 {
178            #[cfg(feature = "native-tls")]
179            TlsAcceptorInner::NativeTls(c) => {
180                let server = c.accept(stream).await?;
181                Ok(TlsStream::from(server))
182            }
183            #[cfg(feature = "rustls")]
184            TlsAcceptorInner::Rustls(c) => {
185                let server = c.accept(stream).await?;
186                Ok(TlsStream::from(server))
187            }
188            #[cfg(feature = "py-dynamic-openssl")]
189            TlsAcceptorInner::PyDynamicOpenSsl(a) => {
190                let server = a.accept(stream).await?;
191                Ok(TlsStream::from(server))
192            }
193            #[cfg(not(any(
194                feature = "native-tls",
195                feature = "rustls",
196                feature = "py-dynamic-openssl"
197            )))]
198            TlsAcceptorInner::None(f) => match *f {},
199        }
200    }
201}