1use rama_core::rt::Executor;
2
3use super::{
4 BasicHttpConId, BasicHttpConnIdentifier, BindBodyToConnector, HttpConnector,
5 HttpPooledConnectorConfig,
6};
7use crate::{
8 Layer, Service,
9 dns::client::{DnsConnectorLayer, resolver::DnsAddressResolver},
10 error::BoxError,
11 extensions::ExtensionsRef,
12 http::{
13 Request, StreamingBody, client::proxy::layer::HttpProxyConnector,
14 layer::version_adapter::RequestVersionAdapter,
15 },
16 net::client::{
17 ConnectorService, EstablishedClientConnection,
18 pool::{MultiplexPool, PooledConnector},
19 },
20 tcp::client::service::TcpConnector,
21};
22use std::{marker::PhantomData, time::Duration};
23
24#[cfg(feature = "boring")]
25use crate::tls::boring::client as boring_client;
26
27#[cfg(any(feature = "rustls", feature = "boring"))]
28use crate::tls::client::TlsClientConfig;
29#[cfg(feature = "rustls")]
30use crate::tls::rustls::client as rustls_client;
31
32#[cfg(feature = "socks5")]
33use crate::{http::client::proxy_connector::ProxyConnector, proxy::socks5::Socks5ProxyConnector};
34
35#[derive(Default)]
37pub struct EasyHttpConnectorBuilder<C = (), S = ()> {
38 connector: C,
39 _phantom: PhantomData<S>,
40}
41
42#[non_exhaustive]
43#[derive(Debug)]
44pub struct TransportStage;
45#[non_exhaustive]
46#[derive(Debug)]
47pub struct DnsStage;
48#[non_exhaustive]
49#[derive(Debug)]
50pub struct ProxyTunnelStage;
51#[non_exhaustive]
52#[derive(Debug)]
53pub struct ProxyStage;
54#[non_exhaustive]
55#[derive(Debug)]
56pub struct TlsStage;
57#[non_exhaustive]
58#[derive(Debug)]
59pub struct HttpStage;
60#[non_exhaustive]
61#[derive(Debug)]
62pub struct PoolStage;
63
64impl EasyHttpConnectorBuilder {
65 #[must_use]
66 pub fn new() -> Self {
67 Self::default()
68 }
69
70 #[must_use]
71 pub fn with_default_transport_connector(
72 self,
73 ) -> EasyHttpConnectorBuilder<TcpConnector, TransportStage> {
74 let connector = TcpConnector::default();
75 EasyHttpConnectorBuilder {
76 connector,
77 _phantom: PhantomData,
78 }
79 }
80
81 pub fn with_custom_transport_connector<C>(
83 self,
84 connector: C,
85 ) -> EasyHttpConnectorBuilder<C, TransportStage> {
86 EasyHttpConnectorBuilder {
87 connector,
88 _phantom: PhantomData,
89 }
90 }
91}
92
93impl<T, Stage> EasyHttpConnectorBuilder<T, Stage> {
94 pub fn with_custom_connector<L>(
99 self,
100 connector_layer: L,
101 ) -> EasyHttpConnectorBuilder<L::Service, Stage>
102 where
103 L: Layer<T>,
104 {
105 self.map_connector(|c| connector_layer.into_layer(c))
106 }
107
108 pub fn map_connector<T2>(
113 self,
114 map_fn: impl FnOnce(T) -> T2,
115 ) -> EasyHttpConnectorBuilder<T2, Stage> {
116 let connector = map_fn(self.connector);
117 EasyHttpConnectorBuilder {
118 connector,
119 _phantom: PhantomData,
120 }
121 }
122}
123
124impl<T> EasyHttpConnectorBuilder<T, TransportStage> {
125 pub fn with_default_dns_connector(
127 self,
128 ) -> EasyHttpConnectorBuilder<crate::dns::client::DnsConnector<T>, DnsStage> {
129 self.with_dns_connector(DnsConnectorLayer::new())
130 }
131
132 pub fn with_dns_address_resolver<R: DnsAddressResolver + Clone>(
134 self,
135 resolver: R,
136 ) -> EasyHttpConnectorBuilder<crate::dns::client::DnsConnector<T, R>, DnsStage> {
137 self.with_dns_connector(DnsConnectorLayer::with_resolver(resolver))
138 }
139
140 pub fn without_dns_connector(
145 self,
146 ) -> EasyHttpConnectorBuilder<crate::dns::client::DnsConnector<T>, DnsStage> {
147 self.with_dns_connector(DnsConnectorLayer::new())
148 }
149
150 pub fn with_dns_connector<L>(
152 self,
153 connector_layer: L,
154 ) -> EasyHttpConnectorBuilder<L::Service, DnsStage>
155 where
156 L: Layer<T>,
157 {
158 let connector = connector_layer.into_layer(self.connector);
159 EasyHttpConnectorBuilder {
160 connector,
161 _phantom: PhantomData,
162 }
163 }
164}
165
166impl<T> EasyHttpConnectorBuilder<T, DnsStage> {
167 #[cfg(any(feature = "rustls", feature = "boring"))]
168 pub fn with_custom_tls_proxy_connector<L>(
170 self,
171 connector_layer: L,
172 ) -> EasyHttpConnectorBuilder<L::Service, ProxyTunnelStage>
173 where
174 L: Layer<T>,
175 {
176 let connector = connector_layer.into_layer(self.connector);
177 EasyHttpConnectorBuilder {
178 connector,
179 _phantom: PhantomData,
180 }
181 }
182
183 #[cfg(feature = "boring")]
184 #[cfg_attr(docsrs, doc(cfg(feature = "boring")))]
185 pub fn with_tls_proxy_support_using_boringssl(
191 self,
192 ) -> EasyHttpConnectorBuilder<
193 boring_client::TlsConnector<T, boring_client::ConnectorKindTunnel>,
194 ProxyTunnelStage,
195 > {
196 let connector = boring_client::TlsConnector::tunnel(self.connector, None);
197 EasyHttpConnectorBuilder {
198 connector,
199 _phantom: PhantomData,
200 }
201 }
202
203 #[cfg(feature = "boring")]
204 #[cfg_attr(docsrs, doc(cfg(feature = "boring")))]
205 pub fn with_tls_proxy_support_using_boringssl_config(
211 self,
212 config: TlsClientConfig,
213 ) -> EasyHttpConnectorBuilder<
214 boring_client::TlsConnector<T, boring_client::ConnectorKindTunnel>,
215 ProxyTunnelStage,
216 > {
217 let connector =
218 boring_client::TlsConnector::tunnel(self.connector, None).with_base_config(config);
219 EasyHttpConnectorBuilder {
220 connector,
221 _phantom: PhantomData,
222 }
223 }
224
225 #[cfg(feature = "rustls")]
226 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
227 pub fn with_tls_proxy_support_using_rustls(
233 self,
234 ) -> EasyHttpConnectorBuilder<
235 rustls_client::TlsConnector<T, rustls_client::ConnectorKindTunnel>,
236 ProxyTunnelStage,
237 > {
238 let connector = rustls_client::TlsConnector::tunnel(self.connector, None);
239
240 EasyHttpConnectorBuilder {
241 connector,
242 _phantom: PhantomData,
243 }
244 }
245
246 #[cfg(feature = "rustls")]
247 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
248 pub fn with_tls_proxy_support_using_rustls_config(
254 self,
255 config: TlsClientConfig,
256 ) -> EasyHttpConnectorBuilder<
257 rustls_client::TlsConnector<T, rustls_client::ConnectorKindTunnel>,
258 ProxyTunnelStage,
259 > {
260 let connector =
261 rustls_client::TlsConnector::tunnel(self.connector, None).with_base_config(config);
262
263 EasyHttpConnectorBuilder {
264 connector,
265 _phantom: PhantomData,
266 }
267 }
268
269 pub fn without_tls_proxy_support(self) -> EasyHttpConnectorBuilder<T, ProxyTunnelStage> {
275 EasyHttpConnectorBuilder {
276 connector: self.connector,
277 _phantom: PhantomData,
278 }
279 }
280}
281
282impl<T> EasyHttpConnectorBuilder<T, ProxyTunnelStage> {
283 pub fn with_custom_proxy_connector<L>(
285 self,
286 connector_layer: L,
287 ) -> EasyHttpConnectorBuilder<L::Service, ProxyStage>
288 where
289 L: Layer<T>,
290 {
291 let connector = connector_layer.into_layer(self.connector);
292 EasyHttpConnectorBuilder {
293 connector,
294 _phantom: PhantomData,
295 }
296 }
297
298 #[cfg(not(feature = "socks5"))]
299 pub fn with_proxy_support(self) -> EasyHttpConnectorBuilder<HttpProxyConnector<T>, ProxyStage> {
309 self.with_http_proxy_support()
310 }
311
312 pub fn with_http_proxy_support(
320 self,
321 ) -> EasyHttpConnectorBuilder<HttpProxyConnector<T>, ProxyStage> {
322 let connector = HttpProxyConnector::optional(self.connector);
323
324 EasyHttpConnectorBuilder {
325 connector,
326 _phantom: PhantomData,
327 }
328 }
329
330 #[cfg(feature = "socks5")]
331 #[cfg_attr(docsrs, doc(cfg(feature = "socks5")))]
332 pub fn with_socks5_proxy_support(
336 self,
337 ) -> EasyHttpConnectorBuilder<Socks5ProxyConnector<T>, ProxyStage> {
338 let connector = Socks5ProxyConnector::optional(self.connector);
339
340 EasyHttpConnectorBuilder {
341 connector,
342 _phantom: PhantomData,
343 }
344 }
345
346 pub fn without_proxy_support(self) -> EasyHttpConnectorBuilder<T, ProxyStage> {
348 EasyHttpConnectorBuilder {
349 connector: self.connector,
350 _phantom: PhantomData,
351 }
352 }
353}
354
355impl<T: Clone> EasyHttpConnectorBuilder<T, ProxyTunnelStage> {
356 #[cfg(feature = "socks5")]
357 #[cfg_attr(docsrs, doc(cfg(feature = "socks5")))]
358 pub fn with_proxy_support(self) -> EasyHttpConnectorBuilder<ProxyConnector<T>, ProxyStage> {
366 use rama_http_backend::client::proxy::layer::HttpProxyConnectorLayer;
367 use rama_socks5::Socks5ProxyConnectorLayer;
368
369 let connector = ProxyConnector::optional(
370 self.connector,
371 Socks5ProxyConnectorLayer::required(),
372 HttpProxyConnectorLayer::required(),
373 );
374
375 EasyHttpConnectorBuilder {
376 connector,
377 _phantom: PhantomData,
378 }
379 }
380}
381
382impl<T> EasyHttpConnectorBuilder<T, ProxyStage> {
383 #[cfg(any(feature = "rustls", feature = "boring"))]
384 pub fn with_custom_tls_connector<L>(
391 self,
392 connector_layer: L,
393 ) -> EasyHttpConnectorBuilder<L::Service, TlsStage>
394 where
395 L: Layer<T>,
396 {
397 let connector = connector_layer.into_layer(self.connector);
398
399 EasyHttpConnectorBuilder {
400 connector,
401 _phantom: PhantomData,
402 }
403 }
404
405 #[cfg(feature = "boring")]
406 #[cfg_attr(docsrs, doc(cfg(feature = "boring")))]
407 pub fn with_tls_support_using_boringssl(
413 self,
414 config: TlsClientConfig,
415 ) -> EasyHttpConnectorBuilder<RequestVersionAdapter<boring_client::TlsConnector<T>>, TlsStage>
416 {
417 let connector = boring_client::TlsConnector::auto(self.connector).with_base_config(config);
418 let connector = RequestVersionAdapter::new(connector);
419
420 EasyHttpConnectorBuilder {
421 connector,
422 _phantom: PhantomData,
423 }
424 }
425
426 #[cfg(feature = "boring")]
427 #[cfg_attr(docsrs, doc(cfg(feature = "boring")))]
428 pub fn with_tls_support_using_boringssl_and_default_http_version(
437 self,
438 config: TlsClientConfig,
439 default_http_version: rama_http::Version,
440 ) -> EasyHttpConnectorBuilder<RequestVersionAdapter<boring_client::TlsConnector<T>>, TlsStage>
441 {
442 let connector = boring_client::TlsConnector::auto(self.connector).with_base_config(config);
443 let connector =
444 RequestVersionAdapter::new(connector).with_default_version(default_http_version);
445
446 EasyHttpConnectorBuilder {
447 connector,
448 _phantom: PhantomData,
449 }
450 }
451
452 #[cfg(feature = "rustls")]
453 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
454 pub fn with_tls_support_using_rustls(
460 self,
461 config: TlsClientConfig,
462 ) -> EasyHttpConnectorBuilder<RequestVersionAdapter<rustls_client::TlsConnector<T>>, TlsStage>
463 {
464 let connector = rustls_client::TlsConnector::auto(self.connector).with_base_config(config);
465 let connector = RequestVersionAdapter::new(connector);
466
467 EasyHttpConnectorBuilder {
468 connector,
469 _phantom: PhantomData,
470 }
471 }
472
473 #[cfg(feature = "rustls")]
474 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
475 pub fn with_tls_support_using_rustls_and_default_http_version(
484 self,
485 config: TlsClientConfig,
486 default_http_version: rama_http::Version,
487 ) -> EasyHttpConnectorBuilder<RequestVersionAdapter<rustls_client::TlsConnector<T>>, TlsStage>
488 {
489 let connector = rustls_client::TlsConnector::auto(self.connector).with_base_config(config);
490 let connector =
491 RequestVersionAdapter::new(connector).with_default_version(default_http_version);
492
493 EasyHttpConnectorBuilder {
494 connector,
495 _phantom: PhantomData,
496 }
497 }
498
499 pub fn without_tls_support(self) -> EasyHttpConnectorBuilder<T, TlsStage> {
501 EasyHttpConnectorBuilder {
502 connector: self.connector,
503 _phantom: PhantomData,
504 }
505 }
506}
507
508impl<T> EasyHttpConnectorBuilder<T, TlsStage> {
509 pub fn with_default_http_connector<Body>(
511 self,
512 exec: Executor,
513 ) -> EasyHttpConnectorBuilder<HttpConnector<T, Body>, HttpStage> {
514 let connector = HttpConnector::new(self.connector, exec);
515
516 EasyHttpConnectorBuilder {
517 connector,
518 _phantom: PhantomData,
519 }
520 }
521
522 pub fn with_custom_http_connector<L>(
524 self,
525 connector_layer: L,
526 ) -> EasyHttpConnectorBuilder<L::Service, HttpStage>
527 where
528 L: Layer<T>,
529 {
530 let connector = connector_layer.into_layer(self.connector);
531
532 EasyHttpConnectorBuilder {
533 connector,
534 _phantom: PhantomData,
535 }
536 }
537}
538
539type DefaultConnectionPoolBuilder<T> = EasyHttpConnectorBuilder<
540 RequestVersionAdapter<
541 BindBodyToConnector<
542 PooledConnector<
543 T,
544 MultiplexPool<<T as ConnectorService<Request>>::Connection, BasicHttpConId>,
545 BasicHttpConnIdentifier,
546 >,
547 >,
548 >,
549 PoolStage,
550>;
551
552impl<T> EasyHttpConnectorBuilder<T, HttpStage> {
553 pub fn try_with_connection_pool(
569 self,
570 config: HttpPooledConnectorConfig,
571 ) -> Result<DefaultConnectionPoolBuilder<T>, BoxError>
572 where
573 T: ConnectorService<Request>,
574 {
575 let connector = config.build_connector(self.connector)?;
576 let connector = RequestVersionAdapter::new(connector);
577
578 Ok(EasyHttpConnectorBuilder {
579 connector,
580 _phantom: PhantomData,
581 })
582 }
583
584 #[inline(always)]
585 pub fn try_with_default_connection_pool(
593 self,
594 ) -> Result<DefaultConnectionPoolBuilder<T>, BoxError>
595 where
596 T: ConnectorService<Request>,
597 {
598 self.try_with_connection_pool(Default::default())
599 }
600
601 pub fn with_custom_connection_pool<P, R>(
612 self,
613 pool: P,
614 req_to_conn_id: R,
615 wait_for_pool_timeout: Option<Duration>,
616 ) -> EasyHttpConnectorBuilder<PooledConnector<T, P, R>, PoolStage> {
617 let connector = PooledConnector::new(self.connector, pool, req_to_conn_id)
618 .maybe_with_wait_for_pool_timeout(wait_for_pool_timeout);
619
620 EasyHttpConnectorBuilder {
621 connector,
622 _phantom: PhantomData,
623 }
624 }
625}
626
627impl<T, S> EasyHttpConnectorBuilder<T, S> {
628 pub fn build_client<Body, ModifiedBody, ConnResponse>(
630 self,
631 ) -> super::EasyHttpWebClient<Body, T::Output, ()>
632 where
633 Body: StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
634 ModifiedBody:
635 StreamingBody<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
636 T: Service<
637 Request<Body>,
638 Output = EstablishedClientConnection<ConnResponse, Request<ModifiedBody>>,
639 Error: Into<BoxError>,
640 >,
641 ConnResponse: ExtensionsRef,
642 {
643 super::EasyHttpWebClient::new(self.connector)
644 }
645
646 pub fn build_connector(self) -> T {
648 self.connector
649 }
650}