1use super::{HttpConnector, http_inspector::HttpVersionAdapter};
2use crate::{
3 Layer, Service,
4 dns::DnsResolver,
5 error::{BoxError, OpaqueError},
6 http::{Request, client::proxy::layer::HttpProxyConnector, dep::http_body},
7 net::client::{
8 EstablishedClientConnection,
9 pool::{
10 LruDropPool, PooledConnector,
11 http::{BasicHttpConId, BasicHttpConnIdentifier, HttpPooledConnectorConfig},
12 },
13 },
14 tcp::client::service::TcpConnector,
15};
16use std::{marker::PhantomData, time::Duration};
17
18#[cfg(feature = "boring")]
19use crate::tls::boring::client as boring_client;
20
21#[cfg(feature = "rustls")]
22use crate::tls::rustls::client as rustls_client;
23
24#[cfg(any(feature = "rustls", feature = "boring"))]
25use crate::http::client::http_inspector::HttpsAlpnModifier;
26
27#[cfg(feature = "socks5")]
28use crate::{http::client::proxy_connector::ProxyConnector, proxy::socks5::Socks5ProxyConnector};
29
30#[derive(Default)]
32pub struct EasyHttpWebClientBuilder<C = (), S = ()> {
33 connector: C,
34 _phantom: PhantomData<S>,
35}
36
37#[non_exhaustive]
38#[derive(Debug)]
39pub struct TransportStage;
40#[non_exhaustive]
41#[derive(Debug)]
42pub struct ProxyTunnelStage;
43#[non_exhaustive]
44#[derive(Debug)]
45pub struct ProxyStage;
46#[non_exhaustive]
47#[derive(Debug)]
48pub struct HttpStage;
49#[non_exhaustive]
50#[derive(Debug)]
51pub struct PoolStage;
52
53impl EasyHttpWebClientBuilder {
54 #[must_use]
55 pub fn new() -> Self {
56 Self::default()
57 }
58
59 #[must_use]
60 pub fn with_default_transport_connector(
61 self,
62 ) -> EasyHttpWebClientBuilder<TcpConnector, TransportStage> {
63 let connector = TcpConnector::default();
64 EasyHttpWebClientBuilder {
65 connector,
66 _phantom: PhantomData,
67 }
68 }
69
70 pub fn with_custom_transport_connector<C>(
72 self,
73 connector: C,
74 ) -> EasyHttpWebClientBuilder<C, TransportStage> {
75 EasyHttpWebClientBuilder {
76 connector,
77 _phantom: PhantomData,
78 }
79 }
80}
81
82impl EasyHttpWebClientBuilder<TcpConnector, TransportStage> {
83 pub fn with_dns_resolver<T: DnsResolver + Clone>(
85 self,
86 resolver: T,
87 ) -> EasyHttpWebClientBuilder<TcpConnector<T>, TransportStage> {
88 let connector = self.connector.with_dns(resolver);
89 EasyHttpWebClientBuilder {
90 connector,
91 _phantom: PhantomData,
92 }
93 }
94}
95
96impl<T> EasyHttpWebClientBuilder<T, TransportStage> {
97 #[cfg(any(feature = "rustls", feature = "boring"))]
98 pub fn with_custom_tls_proxy_connector<L>(
100 self,
101 connector_layer: L,
102 ) -> EasyHttpWebClientBuilder<L::Service, ProxyTunnelStage>
103 where
104 L: Layer<T>,
105 {
106 let connector = connector_layer.into_layer(self.connector);
107 EasyHttpWebClientBuilder {
108 connector,
109 _phantom: PhantomData,
110 }
111 }
112
113 #[cfg(feature = "boring")]
114 pub fn with_tls_proxy_support_using_boringssl(
120 self,
121 ) -> EasyHttpWebClientBuilder<
122 boring_client::TlsConnector<T, boring_client::ConnectorKindTunnel>,
123 ProxyTunnelStage,
124 > {
125 let connector = boring_client::TlsConnector::tunnel(self.connector, None);
126 EasyHttpWebClientBuilder {
127 connector,
128 _phantom: PhantomData,
129 }
130 }
131
132 #[cfg(feature = "boring")]
133 pub fn with_tls_proxy_support_using_boringssl_config(
139 self,
140 config: std::sync::Arc<boring_client::TlsConnectorDataBuilder>,
141 ) -> EasyHttpWebClientBuilder<
142 boring_client::TlsConnector<T, boring_client::ConnectorKindTunnel>,
143 ProxyTunnelStage,
144 > {
145 let connector =
146 boring_client::TlsConnector::tunnel(self.connector, None).with_connector_data(config);
147 EasyHttpWebClientBuilder {
148 connector,
149 _phantom: PhantomData,
150 }
151 }
152
153 #[cfg(feature = "rustls")]
154 pub fn with_tls_proxy_support_using_rustls(
160 self,
161 ) -> EasyHttpWebClientBuilder<
162 rustls_client::TlsConnector<T, rustls_client::ConnectorKindTunnel>,
163 ProxyTunnelStage,
164 > {
165 let connector = rustls_client::TlsConnector::tunnel(self.connector, None);
166
167 EasyHttpWebClientBuilder {
168 connector,
169 _phantom: PhantomData,
170 }
171 }
172
173 #[cfg(feature = "rustls")]
174 pub fn with_tls_proxy_support_using_rustls_config(
180 self,
181 config: rustls_client::TlsConnectorData,
182 ) -> EasyHttpWebClientBuilder<
183 rustls_client::TlsConnector<T, rustls_client::ConnectorKindTunnel>,
184 ProxyTunnelStage,
185 > {
186 let connector =
187 rustls_client::TlsConnector::tunnel(self.connector, None).with_connector_data(config);
188
189 EasyHttpWebClientBuilder {
190 connector,
191 _phantom: PhantomData,
192 }
193 }
194
195 pub fn without_tls_proxy_support(self) -> EasyHttpWebClientBuilder<T, ProxyTunnelStage> {
201 EasyHttpWebClientBuilder {
202 connector: self.connector,
203 _phantom: PhantomData,
204 }
205 }
206}
207
208impl<T> EasyHttpWebClientBuilder<T, ProxyTunnelStage> {
209 pub fn with_custom_proxy_connector<L>(
211 self,
212 connector_layer: L,
213 ) -> EasyHttpWebClientBuilder<L::Service, ProxyStage>
214 where
215 L: Layer<T>,
216 {
217 let connector = connector_layer.into_layer(self.connector);
218 EasyHttpWebClientBuilder {
219 connector,
220 _phantom: PhantomData,
221 }
222 }
223
224 #[cfg(feature = "socks5")]
225 pub fn with_proxy_support(
233 self,
234 ) -> EasyHttpWebClientBuilder<ProxyConnector<std::sync::Arc<T>>, ProxyStage> {
235 use rama_http_backend::client::proxy::layer::HttpProxyConnectorLayer;
236 use rama_socks5::Socks5ProxyConnectorLayer;
237
238 let connector = ProxyConnector::optional(
239 self.connector,
240 Socks5ProxyConnectorLayer::required(),
241 HttpProxyConnectorLayer::required(),
242 );
243
244 EasyHttpWebClientBuilder {
245 connector,
246 _phantom: PhantomData,
247 }
248 }
249
250 #[cfg(not(feature = "socks5"))]
251 pub fn with_proxy_support(self) -> EasyHttpWebClientBuilder<HttpProxyConnector<T>, ProxyStage> {
261 self.with_http_proxy_support()
262 }
263
264 pub fn with_http_proxy_support(
272 self,
273 ) -> EasyHttpWebClientBuilder<HttpProxyConnector<T>, ProxyStage> {
274 let connector = HttpProxyConnector::optional(self.connector);
275
276 EasyHttpWebClientBuilder {
277 connector,
278 _phantom: PhantomData,
279 }
280 }
281
282 #[cfg(feature = "socks5")]
283 pub fn with_socks5_proxy_support(
287 self,
288 ) -> EasyHttpWebClientBuilder<Socks5ProxyConnector<T>, ProxyStage> {
289 let connector = Socks5ProxyConnector::optional(self.connector);
290
291 EasyHttpWebClientBuilder {
292 connector,
293 _phantom: PhantomData,
294 }
295 }
296
297 pub fn without_proxy_support(self) -> EasyHttpWebClientBuilder<T, ProxyStage> {
299 EasyHttpWebClientBuilder {
300 connector: self.connector,
301 _phantom: PhantomData,
302 }
303 }
304}
305
306impl<T> EasyHttpWebClientBuilder<T, ProxyStage> {
307 #[cfg(any(feature = "rustls", feature = "boring"))]
308 pub fn with_custom_tls_connector<L>(
322 self,
323 connector_layer: L,
324 ) -> EasyHttpWebClientBuilder<
325 HttpConnector<L::Service, (HttpsAlpnModifier, HttpVersionAdapter)>,
326 HttpStage,
327 >
328 where
329 L: Layer<T>,
330 {
331 let connector = connector_layer.into_layer(self.connector);
332
333 let connector = HttpConnector::new(connector)
334 .with_jit_req_inspector((HttpsAlpnModifier::default(), HttpVersionAdapter::default()));
335
336 EasyHttpWebClientBuilder {
337 connector,
338 _phantom: PhantomData,
339 }
340 }
341
342 #[cfg(feature = "boring")]
343 pub fn with_tls_support_using_boringssl(
357 self,
358 config: Option<std::sync::Arc<boring_client::TlsConnectorDataBuilder>>,
359 ) -> EasyHttpWebClientBuilder<
360 HttpConnector<boring_client::TlsConnector<T>, (HttpsAlpnModifier, HttpVersionAdapter)>,
361 HttpStage,
362 > {
363 let connector =
364 boring_client::TlsConnector::auto(self.connector).maybe_with_connector_data(config);
365
366 let connector = HttpConnector::new(connector)
367 .with_jit_req_inspector((HttpsAlpnModifier::default(), HttpVersionAdapter::default()));
368
369 EasyHttpWebClientBuilder {
370 connector,
371 _phantom: PhantomData,
372 }
373 }
374
375 #[cfg(feature = "rustls")]
376 pub fn with_tls_support_using_rustls(
390 self,
391 config: Option<rustls_client::TlsConnectorData>,
392 ) -> EasyHttpWebClientBuilder<
393 HttpConnector<rustls_client::TlsConnector<T>, (HttpsAlpnModifier, HttpVersionAdapter)>,
394 HttpStage,
395 > {
396 let connector =
397 rustls_client::TlsConnector::auto(self.connector).maybe_with_connector_data(config);
398
399 let connector = HttpConnector::new(connector)
400 .with_jit_req_inspector((HttpsAlpnModifier::default(), HttpVersionAdapter::default()));
401
402 EasyHttpWebClientBuilder {
403 connector,
404 _phantom: PhantomData,
405 }
406 }
407
408 pub fn without_tls_support(
419 self,
420 ) -> EasyHttpWebClientBuilder<HttpConnector<T, HttpVersionAdapter>, HttpStage> {
421 let connector = HttpConnector::new(self.connector)
422 .with_jit_req_inspector(HttpVersionAdapter::default());
423
424 EasyHttpWebClientBuilder {
425 connector,
426 _phantom: PhantomData,
427 }
428 }
429}
430
431impl<T, I1, I2> EasyHttpWebClientBuilder<HttpConnector<T, I1, I2>, HttpStage> {
432 pub fn with_advanced_jit_req_inspector<I>(
437 self,
438 http_req_inspector: I,
439 ) -> EasyHttpWebClientBuilder<HttpConnector<T, I, I2>, HttpStage> {
440 EasyHttpWebClientBuilder {
441 connector: self.connector.with_jit_req_inspector(http_req_inspector),
442 _phantom: PhantomData,
443 }
444 }
445
446 pub fn without_jit_req_inspector(
451 self,
452 ) -> EasyHttpWebClientBuilder<HttpConnector<T, (), I2>, HttpStage> {
453 EasyHttpWebClientBuilder {
454 connector: self.connector.with_jit_req_inspector(()),
455 _phantom: PhantomData,
456 }
457 }
458
459 #[cfg(any(feature = "rustls", feature = "boring"))]
460 pub fn with_jit_req_inspector<I>(
474 self,
475 http_req_inspector: I,
476 ) -> EasyHttpWebClientBuilder<
477 HttpConnector<T, (HttpsAlpnModifier, HttpVersionAdapter, I), I2>,
478 HttpStage,
479 > {
480 EasyHttpWebClientBuilder {
481 connector: self.connector.with_jit_req_inspector((
482 HttpsAlpnModifier::default(),
483 HttpVersionAdapter::default(),
484 http_req_inspector,
485 )),
486 _phantom: PhantomData,
487 }
488 }
489
490 #[cfg(not(any(feature = "rustls", feature = "boring")))]
491 pub fn with_jit_req_inspector<I>(
502 self,
503 http_req_inspector: I,
504 ) -> EasyHttpWebClientBuilder<HttpConnector<T, (HttpVersionAdapter, I), I2>, HttpStage> {
505 EasyHttpWebClientBuilder {
506 connector: self
507 .connector
508 .with_jit_req_inspector((HttpVersionAdapter::default(), http_req_inspector)),
509 _phantom: PhantomData,
510 }
511 }
512
513 pub fn with_svc_req_inspector<I>(
515 self,
516 http_req_inspector: I,
517 ) -> EasyHttpWebClientBuilder<HttpConnector<T, I1, I>, HttpStage> {
518 EasyHttpWebClientBuilder {
519 connector: self.connector.with_svc_req_inspector(http_req_inspector),
520 _phantom: PhantomData,
521 }
522 }
523}
524
525type DefaultConnectionPoolBuilder<T, C> = EasyHttpWebClientBuilder<
526 PooledConnector<T, LruDropPool<C, BasicHttpConId>, BasicHttpConnIdentifier>,
527 PoolStage,
528>;
529
530impl<T> EasyHttpWebClientBuilder<T, HttpStage> {
531 pub fn with_connection_pool<C>(
543 self,
544 config: HttpPooledConnectorConfig,
545 ) -> Result<DefaultConnectionPoolBuilder<T, C>, OpaqueError> {
546 let connector = config.build_connector(self.connector)?;
547
548 Ok(EasyHttpWebClientBuilder {
549 connector,
550 _phantom: PhantomData,
551 })
552 }
553
554 pub fn with_custom_connection_pool<P, R>(
561 self,
562 pool: P,
563 req_to_conn_id: R,
564 wait_for_pool_timeout: Option<Duration>,
565 ) -> EasyHttpWebClientBuilder<PooledConnector<T, P, R>, PoolStage> {
566 let connector = PooledConnector::new(self.connector, pool, req_to_conn_id)
567 .maybe_with_wait_for_pool_timeout(wait_for_pool_timeout);
568
569 EasyHttpWebClientBuilder {
570 connector,
571 _phantom: PhantomData,
572 }
573 }
574}
575
576impl<T, S> EasyHttpWebClientBuilder<T, S> {
577 pub fn build<Body, ModifiedBody, ConnResponse>(
579 self,
580 ) -> super::EasyHttpWebClient<Body, T::Response>
581 where
582 Body: http_body::Body<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
583 ModifiedBody:
584 http_body::Body<Data: Send + 'static, Error: Into<BoxError>> + Unpin + Send + 'static,
585 T: Service<
586 Request<Body>,
587 Response = EstablishedClientConnection<ConnResponse, Request<ModifiedBody>>,
588 Error = BoxError,
589 >,
590 {
591 super::EasyHttpWebClient::new(self.connector.boxed())
592 }
593}