1use super::HttpConnector;
2use crate::{
3 Layer, Service,
4 dns::DnsResolver,
5 error::{BoxError, OpaqueError},
6 extensions::ExtensionsMut,
7 http::{Request, StreamingBody, client::proxy::layer::HttpProxyConnector},
8 net::client::{
9 EstablishedClientConnection,
10 pool::{
11 LruDropPool, PooledConnector,
12 http::{BasicHttpConId, BasicHttpConnIdentifier, HttpPooledConnectorConfig},
13 },
14 },
15 tcp::client::service::TcpConnector,
16};
17use std::{marker::PhantomData, time::Duration};
18
19#[cfg(feature = "boring")]
20use crate::tls::boring::client as boring_client;
21
22#[cfg(feature = "rustls")]
23use crate::tls::rustls::client as rustls_client;
24
25#[cfg(any(feature = "rustls", feature = "boring"))]
26use crate::http::layer::version_adapter::RequestVersionAdapter;
27
28#[cfg(feature = "socks5")]
29use crate::{http::client::proxy_connector::ProxyConnector, proxy::socks5::Socks5ProxyConnector};
30
31#[derive(Default)]
33pub struct EasyHttpWebClientBuilder<C = (), S = ()> {
34 connector: C,
35 _phantom: PhantomData<S>,
36}
37
38#[non_exhaustive]
39#[derive(Debug)]
40pub struct TransportStage;
41#[non_exhaustive]
42#[derive(Debug)]
43pub struct ProxyTunnelStage;
44#[non_exhaustive]
45#[derive(Debug)]
46pub struct ProxyStage;
47#[non_exhaustive]
48#[derive(Debug)]
49pub struct TlsStage;
50#[non_exhaustive]
51#[derive(Debug)]
52pub struct HttpStage;
53#[non_exhaustive]
54#[derive(Debug)]
55pub struct PoolStage;
56
57impl EasyHttpWebClientBuilder {
58 #[must_use]
59 pub fn new() -> Self {
60 Self::default()
61 }
62
63 #[must_use]
64 pub fn with_default_transport_connector(
65 self,
66 ) -> EasyHttpWebClientBuilder<TcpConnector, TransportStage> {
67 let connector = TcpConnector::default();
68 EasyHttpWebClientBuilder {
69 connector,
70 _phantom: PhantomData,
71 }
72 }
73
74 pub fn with_custom_transport_connector<C>(
76 self,
77 connector: C,
78 ) -> EasyHttpWebClientBuilder<C, TransportStage> {
79 EasyHttpWebClientBuilder {
80 connector,
81 _phantom: PhantomData,
82 }
83 }
84}
85
86impl<T, Stage> EasyHttpWebClientBuilder<T, Stage> {
87 pub fn with_custom_connector<L>(
92 self,
93 connector_layer: L,
94 ) -> EasyHttpWebClientBuilder<L::Service, Stage>
95 where
96 L: Layer<T>,
97 {
98 let connector = connector_layer.into_layer(self.connector);
99
100 EasyHttpWebClientBuilder {
101 connector,
102 _phantom: PhantomData,
103 }
104 }
105}
106
107impl EasyHttpWebClientBuilder<TcpConnector, TransportStage> {
108 pub fn with_dns_resolver<T: DnsResolver + Clone>(
110 self,
111 resolver: T,
112 ) -> EasyHttpWebClientBuilder<TcpConnector<T>, TransportStage> {
113 let connector = self.connector.with_dns(resolver);
114 EasyHttpWebClientBuilder {
115 connector,
116 _phantom: PhantomData,
117 }
118 }
119}
120
121impl<T> EasyHttpWebClientBuilder<T, TransportStage> {
122 #[cfg(any(feature = "rustls", feature = "boring"))]
123 pub fn with_custom_tls_proxy_connector<L>(
125 self,
126 connector_layer: L,
127 ) -> EasyHttpWebClientBuilder<L::Service, ProxyTunnelStage>
128 where
129 L: Layer<T>,
130 {
131 let connector = connector_layer.into_layer(self.connector);
132 EasyHttpWebClientBuilder {
133 connector,
134 _phantom: PhantomData,
135 }
136 }
137
138 #[cfg(feature = "boring")]
139 #[cfg_attr(docsrs, doc(cfg(feature = "boring")))]
140 pub fn with_tls_proxy_support_using_boringssl(
146 self,
147 ) -> EasyHttpWebClientBuilder<
148 boring_client::TlsConnector<T, boring_client::ConnectorKindTunnel>,
149 ProxyTunnelStage,
150 > {
151 let connector = boring_client::TlsConnector::tunnel(self.connector, None);
152 EasyHttpWebClientBuilder {
153 connector,
154 _phantom: PhantomData,
155 }
156 }
157
158 #[cfg(feature = "boring")]
159 #[cfg_attr(docsrs, doc(cfg(feature = "boring")))]
160 pub fn with_tls_proxy_support_using_boringssl_config(
166 self,
167 config: std::sync::Arc<boring_client::TlsConnectorDataBuilder>,
168 ) -> EasyHttpWebClientBuilder<
169 boring_client::TlsConnector<T, boring_client::ConnectorKindTunnel>,
170 ProxyTunnelStage,
171 > {
172 let connector =
173 boring_client::TlsConnector::tunnel(self.connector, None).with_connector_data(config);
174 EasyHttpWebClientBuilder {
175 connector,
176 _phantom: PhantomData,
177 }
178 }
179
180 #[cfg(feature = "rustls")]
181 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
182 pub fn with_tls_proxy_support_using_rustls(
188 self,
189 ) -> EasyHttpWebClientBuilder<
190 rustls_client::TlsConnector<T, rustls_client::ConnectorKindTunnel>,
191 ProxyTunnelStage,
192 > {
193 let connector = rustls_client::TlsConnector::tunnel(self.connector, None);
194
195 EasyHttpWebClientBuilder {
196 connector,
197 _phantom: PhantomData,
198 }
199 }
200
201 #[cfg(feature = "rustls")]
202 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
203 pub fn with_tls_proxy_support_using_rustls_config(
209 self,
210 config: rustls_client::TlsConnectorData,
211 ) -> EasyHttpWebClientBuilder<
212 rustls_client::TlsConnector<T, rustls_client::ConnectorKindTunnel>,
213 ProxyTunnelStage,
214 > {
215 let connector =
216 rustls_client::TlsConnector::tunnel(self.connector, None).with_connector_data(config);
217
218 EasyHttpWebClientBuilder {
219 connector,
220 _phantom: PhantomData,
221 }
222 }
223
224 pub fn without_tls_proxy_support(self) -> EasyHttpWebClientBuilder<T, ProxyTunnelStage> {
230 EasyHttpWebClientBuilder {
231 connector: self.connector,
232 _phantom: PhantomData,
233 }
234 }
235}
236
237impl<T> EasyHttpWebClientBuilder<T, ProxyTunnelStage> {
238 pub fn with_custom_proxy_connector<L>(
240 self,
241 connector_layer: L,
242 ) -> EasyHttpWebClientBuilder<L::Service, ProxyStage>
243 where
244 L: Layer<T>,
245 {
246 let connector = connector_layer.into_layer(self.connector);
247 EasyHttpWebClientBuilder {
248 connector,
249 _phantom: PhantomData,
250 }
251 }
252
253 #[cfg(feature = "socks5")]
254 #[cfg_attr(docsrs, doc(cfg(feature = "socks5")))]
255 pub fn with_proxy_support(
263 self,
264 ) -> EasyHttpWebClientBuilder<ProxyConnector<std::sync::Arc<T>>, ProxyStage> {
265 use rama_http_backend::client::proxy::layer::HttpProxyConnectorLayer;
266 use rama_socks5::Socks5ProxyConnectorLayer;
267
268 let connector = ProxyConnector::optional(
269 self.connector,
270 Socks5ProxyConnectorLayer::required(),
271 HttpProxyConnectorLayer::required(),
272 );
273
274 EasyHttpWebClientBuilder {
275 connector,
276 _phantom: PhantomData,
277 }
278 }
279
280 #[cfg(not(feature = "socks5"))]
281 pub fn with_proxy_support(self) -> EasyHttpWebClientBuilder<HttpProxyConnector<T>, ProxyStage> {
291 self.with_http_proxy_support()
292 }
293
294 pub fn with_http_proxy_support(
302 self,
303 ) -> EasyHttpWebClientBuilder<HttpProxyConnector<T>, ProxyStage> {
304 let connector = HttpProxyConnector::optional(self.connector);
305
306 EasyHttpWebClientBuilder {
307 connector,
308 _phantom: PhantomData,
309 }
310 }
311
312 #[cfg(feature = "socks5")]
313 #[cfg_attr(docsrs, doc(cfg(feature = "socks5")))]
314 pub fn with_socks5_proxy_support(
318 self,
319 ) -> EasyHttpWebClientBuilder<Socks5ProxyConnector<T>, ProxyStage> {
320 let connector = Socks5ProxyConnector::optional(self.connector);
321
322 EasyHttpWebClientBuilder {
323 connector,
324 _phantom: PhantomData,
325 }
326 }
327
328 pub fn without_proxy_support(self) -> EasyHttpWebClientBuilder<T, ProxyStage> {
330 EasyHttpWebClientBuilder {
331 connector: self.connector,
332 _phantom: PhantomData,
333 }
334 }
335}
336
337impl<T> EasyHttpWebClientBuilder<T, ProxyStage> {
338 #[cfg(any(feature = "rustls", feature = "boring"))]
339 pub fn with_custom_tls_connector<L>(
346 self,
347 connector_layer: L,
348 ) -> EasyHttpWebClientBuilder<L::Service, TlsStage>
349 where
350 L: Layer<T>,
351 {
352 let connector = connector_layer.into_layer(self.connector);
353
354 EasyHttpWebClientBuilder {
355 connector,
356 _phantom: PhantomData,
357 }
358 }
359
360 #[cfg(feature = "boring")]
361 #[cfg_attr(docsrs, doc(cfg(feature = "boring")))]
362 pub fn with_tls_support_using_boringssl(
368 self,
369 config: Option<std::sync::Arc<boring_client::TlsConnectorDataBuilder>>,
370 ) -> EasyHttpWebClientBuilder<RequestVersionAdapter<boring_client::TlsConnector<T>>, TlsStage>
371 {
372 let connector =
373 boring_client::TlsConnector::auto(self.connector).maybe_with_connector_data(config);
374 let connector = RequestVersionAdapter::new(connector);
375
376 EasyHttpWebClientBuilder {
377 connector,
378 _phantom: PhantomData,
379 }
380 }
381
382 #[cfg(feature = "rustls")]
383 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
384 pub fn with_tls_support_using_rustls(
390 self,
391 config: Option<rustls_client::TlsConnectorData>,
392 ) -> EasyHttpWebClientBuilder<RequestVersionAdapter<rustls_client::TlsConnector<T>>, TlsStage>
393 {
394 let connector =
395 rustls_client::TlsConnector::auto(self.connector).maybe_with_connector_data(config);
396 let connector = RequestVersionAdapter::new(connector);
397
398 EasyHttpWebClientBuilder {
399 connector,
400 _phantom: PhantomData,
401 }
402 }
403
404 pub fn without_tls_support(self) -> EasyHttpWebClientBuilder<T, TlsStage> {
406 EasyHttpWebClientBuilder {
407 connector: self.connector,
408 _phantom: PhantomData,
409 }
410 }
411}
412
413impl<T> EasyHttpWebClientBuilder<T, TlsStage> {
414 pub fn with_default_http_connector(
416 self,
417 ) -> EasyHttpWebClientBuilder<HttpConnector<T>, HttpStage> {
418 let connector = HttpConnector::new(self.connector);
419
420 EasyHttpWebClientBuilder {
421 connector,
422 _phantom: PhantomData,
423 }
424 }
425
426 pub fn with_custom_http_connector<L>(
428 self,
429 connector_layer: L,
430 ) -> EasyHttpWebClientBuilder<L::Service, HttpStage>
431 where
432 L: Layer<T>,
433 {
434 let connector = connector_layer.into_layer(self.connector);
435
436 EasyHttpWebClientBuilder {
437 connector,
438 _phantom: PhantomData,
439 }
440 }
441}
442
443impl<T, I> EasyHttpWebClientBuilder<HttpConnector<T, I>, HttpStage> {
444 pub fn with_svc_req_inspector<U>(
446 self,
447 http_req_inspector: U,
448 ) -> EasyHttpWebClientBuilder<HttpConnector<T, U>, HttpStage> {
449 EasyHttpWebClientBuilder {
450 connector: self.connector.with_svc_req_inspector(http_req_inspector),
451 _phantom: PhantomData,
452 }
453 }
454}
455
456type DefaultConnectionPoolBuilder<T, C> = EasyHttpWebClientBuilder<
457 PooledConnector<T, LruDropPool<C, BasicHttpConId>, BasicHttpConnIdentifier>,
458 PoolStage,
459>;
460
461impl<T> EasyHttpWebClientBuilder<T, HttpStage> {
462 pub fn with_connection_pool<C>(
474 self,
475 config: HttpPooledConnectorConfig,
476 ) -> Result<DefaultConnectionPoolBuilder<T, C>, OpaqueError> {
477 let connector = config.build_connector(self.connector)?;
478
479 Ok(EasyHttpWebClientBuilder {
480 connector,
481 _phantom: PhantomData,
482 })
483 }
484
485 pub fn with_custom_connection_pool<P, R>(
492 self,
493 pool: P,
494 req_to_conn_id: R,
495 wait_for_pool_timeout: Option<Duration>,
496 ) -> EasyHttpWebClientBuilder<PooledConnector<T, P, R>, PoolStage> {
497 let connector = PooledConnector::new(self.connector, pool, req_to_conn_id)
498 .maybe_with_wait_for_pool_timeout(wait_for_pool_timeout);
499
500 EasyHttpWebClientBuilder {
501 connector,
502 _phantom: PhantomData,
503 }
504 }
505}
506
507impl<T, S> EasyHttpWebClientBuilder<T, S> {
508 pub fn build<Body, ModifiedBody, ConnResponse>(
510 self,
511 ) -> super::EasyHttpWebClient<Body, T::Response>
512 where
513 Body: StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
514 ModifiedBody:
515 StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
516 T: Service<
517 Request<Body>,
518 Response = EstablishedClientConnection<ConnResponse, Request<ModifiedBody>>,
519 Error = BoxError,
520 >,
521 ConnResponse: ExtensionsMut,
522 {
523 super::EasyHttpWebClient::new(self.connector.boxed())
524 }
525}