tls_api/
connector.rs

1use std::marker;
2
3use crate::connector_box::TlsConnectorBox;
4use crate::connector_box::TlsConnectorTypeImpl;
5use crate::socket::AsyncSocket;
6use crate::stream::TlsStream;
7use crate::BoxFuture;
8use crate::ImplInfo;
9use crate::TlsConnectorType;
10use crate::TlsStreamDyn;
11use crate::TlsStreamWithSocket;
12
13/// A builder for `TlsConnector`s.
14pub trait TlsConnectorBuilder: Sized + Sync + Send + 'static {
15    /// Result of connector to be build.
16    type Connector: TlsConnector;
17
18    /// Type of the underlying builder.
19    ///
20    /// In the world of HKT this would be:
21    ///
22    /// ```ignore
23    /// type TlsStream<S: TlsStreamDyn> : TlsStreamWithSocketDyn<S>;
24    /// ```
25    ///
26    /// Note each implementation has `accept_impl` function
27    /// which returns more specific type, providing both access to implementation details
28    /// and the underlying socket.
29    type Underlying;
30
31    /// Get the underlying builder.
32    ///
33    /// API intentionally exposes the underlying acceptor builder to allow fine tuning
34    /// not possible in common API.
35    fn underlying_mut(&mut self) -> &mut Self::Underlying;
36
37    /// Set ALPN-protocols to negotiate.
38    ///
39    /// This operations fails is not [`TlsConnector::SUPPORTS_ALPN`].
40    fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()>;
41
42    /// Should hostname verification be performed?
43    /// Use carefully, it opens the door to MITM attacks.
44    fn set_verify_hostname(&mut self, verify: bool) -> anyhow::Result<()>;
45
46    /// Add trusted root certificate. By default connector supports only
47    /// global trusted root.
48    ///
49    /// Param is DER-encoded X.509 certificate.
50    fn add_root_certificate(&mut self, cert: &[u8]) -> anyhow::Result<()>;
51
52    /// Finish the acceptor construction.
53    fn build(self) -> anyhow::Result<Self::Connector>;
54}
55
56/// A builder for client-side TLS connections.
57pub trait TlsConnector: Sized + Sync + Send + 'static {
58    /// Type of the builder for this connector.
59    type Builder: TlsConnectorBuilder<Connector = Self>;
60
61    /// Type of the underlying connector.
62    type Underlying;
63
64    /// `crate::TlsStream<tls_api::AsyncSocketBox>`.
65    ///
66    /// In the world of HKT this would be:
67    ///
68    /// ```ignore
69    /// type TlsStream<S: TlsStreamDyn> : TlsStreamWithSocketDyn<S>;
70    /// ```
71    type TlsStream: TlsStreamDyn;
72
73    /// Get the underlying builder.
74    ///
75    /// API intentionally exposes the underlying acceptor builder to allow fine tuning
76    /// not possible in common API.
77    fn underlying_mut(&mut self) -> &mut Self::Underlying;
78
79    /// Is it implemented? When `false` all operations return an error.
80    ///
81    /// At the moment of writing, there are two crates which return `false` here:
82    /// * `tls-api-stub`, dummy implementation is not meant to be instantiated
83    /// * `tls-api-security-framework`, `true` only on macOS and iOS, `false` elsewhere
84    const IMPLEMENTED: bool;
85
86    /// Whether this implementation supports ALPN negotiation.
87    const SUPPORTS_ALPN: bool;
88
89    /// Implementation info.
90    fn info() -> ImplInfo;
91
92    /// New builder for the acceptor.
93    fn builder() -> anyhow::Result<Self::Builder>;
94
95    /// Dynamic (without type parameter) version of the connector.
96    ///
97    /// This function returns a connector type, which can be used to constructor connectors.
98    const TYPE_DYN: &'static dyn TlsConnectorType =
99        &TlsConnectorTypeImpl::<Self>(marker::PhantomData);
100
101    /// Dynamic (without type parameter) version of the connector.
102    fn into_dyn(self) -> TlsConnectorBox {
103        TlsConnectorBox::new(self)
104    }
105
106    /// Connect using default settings.
107    ///
108    /// Shortcut.
109    fn connect_default<S>(domain: &str, stream: S) -> BoxFuture<'_, anyhow::Result<TlsStream>>
110    where
111        S: AsyncSocket,
112    {
113        BoxFuture::new(async move {
114            let connector = Self::builder()?.build()?;
115            connector.connect(domain, stream).await
116        })
117    }
118
119    /// Connect.
120    ///
121    /// Returned future is resolved when the TLS-negotiation completes,
122    /// and the stream is ready to send and receive.
123    ///
124    /// This is like the function you want to use.
125    fn connect<'a, S>(
126        &'a self,
127        domain: &'a str,
128        stream: S,
129    ) -> BoxFuture<'a, anyhow::Result<TlsStream>>
130    where
131        S: AsyncSocket,
132    {
133        BoxFuture::new(async move {
134            self.connect_with_socket(domain, stream)
135                .await
136                .map(TlsStream::new)
137        })
138    }
139
140    /// Connect.
141    ///
142    /// Returned future is resolved when the TLS-negotiation completes,
143    /// and the stream is ready to send and receive.
144    ///
145    /// This function returns a stream which provides access to the underlying socket.
146    ///
147    /// Practically, [`connect`](Self::connect) is usually needed.
148    fn connect_with_socket<'a, S>(
149        &'a self,
150        domain: &'a str,
151        stream: S,
152    ) -> BoxFuture<'a, anyhow::Result<TlsStreamWithSocket<S>>>
153    where
154        S: AsyncSocket;
155
156    /// Connect.
157    ///
158    /// Returned future is resolved when the TLS-negotiation completes,
159    /// and the stream is ready to send and receive.
160    ///
161    /// This version returns a stream of type of the underlying implementation,
162    /// which may provide access to the implementation details.
163    ///
164    /// Practically, [`connect`](Self::connect) is usually needed.
165    fn connect_impl_tls_stream<'a, S>(
166        &'a self,
167        domain: &'a str,
168        stream: S,
169    ) -> BoxFuture<'a, anyhow::Result<Self::TlsStream>>
170    where
171        S: AsyncSocket;
172}
173
174/// Common part of all connectors. Poor man replacement for HKT.
175#[macro_export]
176macro_rules! spi_connector_common {
177    ($stream: ty) => {
178        fn connect_with_socket<'a, S>(
179            &'a self,
180            domain: &'a str,
181            stream: S,
182        ) -> $crate::BoxFuture<'a, anyhow::Result<$crate::TlsStreamWithSocket<S>>>
183        where
184            S: $crate::AsyncSocket,
185        {
186            $crate::BoxFuture::new(async move {
187                let crate_tls_stream: $stream = self.connect_impl(domain, stream).await?;
188                Ok($crate::TlsStreamWithSocket::new(crate_tls_stream))
189            })
190        }
191
192        fn connect_impl_tls_stream<'a, S>(
193            &'a self,
194            domain: &'a str,
195            stream: S,
196        ) -> tls_api::BoxFuture<'a, anyhow::Result<Self::TlsStream>>
197        where
198            S: AsyncSocket,
199        {
200            tls_api::BoxFuture::new(self.connect_impl(domain, tls_api::AsyncSocketBox::new(stream)))
201        }
202    };
203}