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}