1use http::{HeaderMap, HeaderValue, Uri};
8use hyper_util::client::legacy::connect::HttpConnector;
9use hyper_util::client::legacy::Client as HyperClient;
10use hyper_util::rt::TokioExecutor;
11use oxihttp_core::OxiHttpError;
12use std::sync::Arc;
13use std::time::Duration;
14
15use crate::middleware::ClientMiddleware;
16use crate::proxy::{ProxyConnector, ProxyKind};
17use crate::redirect::RedirectPolicy;
18use crate::resolver::BoxResolver;
19use crate::retry::RetryPolicy;
20
21#[cfg(feature = "socks")]
22use crate::proxy::Socks5Connector;
23
24#[cfg(feature = "tls")]
25use crate::connector::OxiHttpsConnector;
26#[cfg(feature = "tls")]
27use crate::tls;
28
29use super::Client;
31#[cfg(feature = "tls")]
32use super::TlsRebuildConfig;
33
34#[derive(Debug, Clone, Default)]
40pub struct Http2Settings {
41 pub initial_stream_window_size: Option<u32>,
43 pub initial_connection_window_size: Option<u32>,
45 pub adaptive_window: Option<bool>,
47 pub keep_alive_interval: Option<std::time::Duration>,
49 pub keep_alive_timeout: Option<std::time::Duration>,
51 pub max_frame_size: Option<u32>,
53 pub max_concurrent_reset_streams: Option<usize>,
55 pub max_send_buf_size: Option<usize>,
57}
58
59pub(crate) fn apply_http2_settings(
64 builder: &mut hyper_util::client::legacy::Builder,
65 settings: &Http2Settings,
66) {
67 if let Some(sz) = settings.initial_stream_window_size {
68 builder.http2_initial_stream_window_size(sz);
69 }
70 if let Some(sz) = settings.initial_connection_window_size {
71 builder.http2_initial_connection_window_size(sz);
72 }
73 if let Some(adaptive) = settings.adaptive_window {
74 builder.http2_adaptive_window(adaptive);
75 }
76 if let Some(interval) = settings.keep_alive_interval {
77 builder.http2_keep_alive_interval(interval);
78 }
79 if let Some(timeout) = settings.keep_alive_timeout {
80 builder.http2_keep_alive_timeout(timeout);
81 }
82 if let Some(sz) = settings.max_frame_size {
83 builder.http2_max_frame_size(sz);
84 }
85 if let Some(n) = settings.max_concurrent_reset_streams {
86 builder.http2_max_concurrent_reset_streams(n);
87 }
88 if let Some(sz) = settings.max_send_buf_size {
89 builder.http2_max_send_buf_size(sz);
90 }
91}
92
93pub struct ClientBuilder {
99 pub(super) pool_max_idle_per_host: Option<usize>,
100 pub(super) pool_idle_timeout: Option<Duration>,
101 pub(super) connect_timeout: Option<Duration>,
102 pub(super) read_timeout: Option<Duration>,
103 pub(super) redirect_policy: RedirectPolicy,
104 pub(super) retry_policy: Option<RetryPolicy>,
105 pub(super) default_headers: HeaderMap,
106 pub(super) user_agent: Option<String>,
107 pub(super) decompression: bool,
108 pub(super) middleware: Vec<Arc<dyn ClientMiddleware>>,
110 pub(super) proxy: Option<ProxyKind>,
112 pub(super) cookie_jar: Option<Arc<std::sync::Mutex<oxihttp_core::CookieJar>>>,
114 pub(super) http2_settings: Option<Http2Settings>,
116 pub(super) tcp_nodelay: Option<bool>,
118 pub(super) tcp_keepalive: Option<Duration>,
120 pub(super) resolver: Option<Arc<dyn crate::resolver::DnsResolver>>,
122 #[cfg(feature = "tls")]
124 pub(super) trusted_certs_der: Vec<Vec<u8>>,
125 #[cfg(feature = "tls")]
126 pub(super) alpn: Vec<String>,
127 #[cfg(feature = "tls")]
128 pub(super) accept_invalid_certs: bool,
129 #[cfg(feature = "tls")]
130 pub(super) use_webpki_roots: bool,
131 #[cfg(feature = "tls")]
133 pub(super) key_log_path: Option<std::path::PathBuf>,
134 #[cfg(feature = "tls")]
136 pub(super) early_data: bool,
137 #[cfg(feature = "tls")]
140 pub(super) custom_cert_verifier:
141 Option<std::sync::Arc<dyn rustls::client::danger::ServerCertVerifier>>,
142}
143
144impl ClientBuilder {
145 pub fn new() -> Self {
147 Self {
148 pool_max_idle_per_host: None,
149 pool_idle_timeout: None,
150 connect_timeout: None,
151 read_timeout: None,
152 redirect_policy: RedirectPolicy::default(),
153 retry_policy: None,
154 default_headers: HeaderMap::new(),
155 user_agent: None,
156 decompression: false,
157 middleware: Vec::new(),
158 proxy: None,
159 cookie_jar: None,
160 http2_settings: None,
161 tcp_nodelay: None,
162 tcp_keepalive: None,
163 resolver: None,
164 #[cfg(feature = "tls")]
165 trusted_certs_der: Vec::new(),
166 #[cfg(feature = "tls")]
167 alpn: Vec::new(),
168 #[cfg(feature = "tls")]
169 accept_invalid_certs: false,
170 #[cfg(feature = "tls")]
171 use_webpki_roots: false,
172 #[cfg(feature = "tls")]
173 key_log_path: None,
174 #[cfg(feature = "tls")]
175 early_data: false,
176 #[cfg(feature = "tls")]
177 custom_cert_verifier: None,
178 }
179 }
180
181 pub fn pool_max_idle_per_host(mut self, n: usize) -> Self {
183 self.pool_max_idle_per_host = Some(n);
184 self
185 }
186
187 pub fn pool_idle_timeout(mut self, duration: Duration) -> Self {
189 self.pool_idle_timeout = Some(duration);
190 self
191 }
192
193 pub fn connect_timeout(mut self, duration: Duration) -> Self {
195 self.connect_timeout = Some(duration);
196 self
197 }
198
199 pub fn read_timeout(mut self, duration: Duration) -> Self {
201 self.read_timeout = Some(duration);
202 self
203 }
204
205 pub fn redirect_policy(mut self, policy: RedirectPolicy) -> Self {
207 self.redirect_policy = policy;
208 self
209 }
210
211 pub fn retry_policy(mut self, policy: RetryPolicy) -> Self {
213 self.retry_policy = Some(policy);
214 self
215 }
216
217 pub fn default_headers(mut self, headers: HeaderMap) -> Self {
219 self.default_headers = headers;
220 self
221 }
222
223 pub fn user_agent(mut self, agent: impl Into<String>) -> Self {
225 self.user_agent = Some(agent.into());
226 self
227 }
228
229 pub fn with_decompression(mut self, enabled: bool) -> Self {
236 self.decompression = enabled;
237 self
238 }
239
240 pub fn with_middleware<M: ClientMiddleware + 'static>(mut self, m: M) -> Self {
261 self.middleware.push(Arc::new(m));
262 self
263 }
264
265 pub fn with_layer<M: ClientMiddleware + 'static>(self, m: M) -> Self {
271 self.with_middleware(m)
272 }
273
274 pub fn with_cookie_jar(mut self, jar: Arc<std::sync::Mutex<oxihttp_core::CookieJar>>) -> Self {
278 self.cookie_jar = Some(jar);
279 self
280 }
281
282 pub fn with_new_cookie_jar(mut self) -> Self {
284 self.cookie_jar = Some(Arc::new(std::sync::Mutex::new(
285 oxihttp_core::CookieJar::new(),
286 )));
287 self
288 }
289
290 pub fn with_http2_settings(mut self, settings: Http2Settings) -> Self {
294 self.http2_settings = Some(settings);
295 self
296 }
297
298 pub fn with_tcp_nodelay(mut self, nodelay: bool) -> Self {
300 self.tcp_nodelay = Some(nodelay);
301 self
302 }
303
304 pub fn with_tcp_keepalive(mut self, duration: Duration) -> Self {
306 self.tcp_keepalive = Some(duration);
307 self
308 }
309
310 pub fn with_resolver<R: crate::resolver::DnsResolver>(mut self, r: R) -> Self {
317 self.resolver = Some(Arc::new(r));
318 self
319 }
320
321 pub fn with_http_proxy(mut self, uri: Uri) -> Self {
328 self.proxy = Some(ProxyKind::HttpConnect(uri));
329 self
330 }
331
332 #[cfg(feature = "socks")]
337 pub fn with_socks5_proxy(mut self, uri: Uri) -> Self {
338 self.proxy = Some(ProxyKind::Socks5(uri));
339 self
340 }
341
342 pub fn build_proxy(self) -> Result<Client<ProxyConnector>, OxiHttpError> {
346 let proxy_uri = match self.proxy.as_ref() {
347 Some(ProxyKind::HttpConnect(u)) => u.clone(),
348 #[cfg(feature = "socks")]
349 Some(ProxyKind::Socks5(_)) => {
350 return Err(OxiHttpError::ConnectionPool(
351 "SOCKS5 proxy configured; use build_socks5_proxy() instead".into(),
352 ))
353 }
354 None => {
355 return Err(OxiHttpError::ConnectionPool(
356 "no proxy configured; call with_http_proxy() first".into(),
357 ))
358 }
359 };
360 let connector = ProxyConnector::new(proxy_uri, self.connect_timeout);
361 let mut builder = HyperClient::builder(TokioExecutor::new());
362 if let Some(n) = self.pool_max_idle_per_host {
363 builder.pool_max_idle_per_host(n);
364 }
365 if let Some(dur) = self.pool_idle_timeout {
366 builder.pool_idle_timeout(dur);
367 }
368 if let Some(ref h2) = self.http2_settings {
369 apply_http2_settings(&mut builder, h2);
370 }
371 let inner = builder.build(connector);
372 let mut default_headers = self.default_headers;
373 if let Some(agent) = &self.user_agent {
374 if let Ok(val) = HeaderValue::from_str(agent) {
375 default_headers.insert(http::header::USER_AGENT, val);
376 }
377 }
378 Ok(Client {
379 inner,
380 redirect_policy: self.redirect_policy,
381 retry_policy: self.retry_policy,
382 default_headers,
383 connect_timeout: self.connect_timeout,
384 read_timeout: self.read_timeout,
385 decompression: self.decompression,
386 middleware: self.middleware,
387 cookie_jar: self.cookie_jar.clone(),
388 #[cfg(feature = "tls")]
389 tls_rebuild: None,
390 })
391 }
392
393 #[cfg(feature = "socks")]
397 pub fn build_socks5_proxy(self) -> Result<Client<Socks5Connector>, OxiHttpError> {
398 let proxy_uri = match self.proxy.as_ref() {
399 Some(ProxyKind::Socks5(u)) => u.clone(),
400 Some(ProxyKind::HttpConnect(_)) => {
401 return Err(OxiHttpError::ConnectionPool(
402 "HTTP CONNECT proxy configured; use build_proxy() instead".into(),
403 ))
404 }
405 None => {
406 return Err(OxiHttpError::ConnectionPool(
407 "no proxy configured; call with_socks5_proxy() first".into(),
408 ))
409 }
410 };
411 let connector = Socks5Connector::new(proxy_uri, self.connect_timeout);
412 let mut builder = HyperClient::builder(TokioExecutor::new());
413 if let Some(n) = self.pool_max_idle_per_host {
414 builder.pool_max_idle_per_host(n);
415 }
416 if let Some(dur) = self.pool_idle_timeout {
417 builder.pool_idle_timeout(dur);
418 }
419 if let Some(ref h2) = self.http2_settings {
420 apply_http2_settings(&mut builder, h2);
421 }
422 let inner = builder.build(connector);
423 let mut default_headers = self.default_headers;
424 if let Some(agent) = &self.user_agent {
425 if let Ok(val) = HeaderValue::from_str(agent) {
426 default_headers.insert(http::header::USER_AGENT, val);
427 }
428 }
429 Ok(Client {
430 inner,
431 redirect_policy: self.redirect_policy,
432 retry_policy: self.retry_policy,
433 default_headers,
434 connect_timeout: self.connect_timeout,
435 read_timeout: self.read_timeout,
436 decompression: self.decompression,
437 middleware: self.middleware,
438 cookie_jar: self.cookie_jar.clone(),
439 #[cfg(feature = "tls")]
440 tls_rebuild: None,
441 })
442 }
443
444 #[cfg(feature = "tls")]
450 pub fn build_proxy_https(
451 self,
452 ) -> Result<Client<OxiHttpsConnector<ProxyConnector>>, OxiHttpError> {
453 let proxy_uri = match self.proxy.as_ref() {
454 Some(ProxyKind::HttpConnect(u)) => u.clone(),
455 #[cfg(feature = "socks")]
456 Some(ProxyKind::Socks5(_)) => {
457 return Err(OxiHttpError::ConnectionPool(
458 "SOCKS5 proxy configured; use build_socks5_proxy_https() instead".into(),
459 ))
460 }
461 None => {
462 return Err(OxiHttpError::ConnectionPool(
463 "no proxy configured; call with_http_proxy() first".into(),
464 ))
465 }
466 };
467
468 let tls_connector = self.build_tls_connector_inner()?;
469
470 let http_connector = ProxyConnector::new(proxy_uri, self.connect_timeout);
471 let https_connector = OxiHttpsConnector::new(http_connector, tls_connector);
472
473 let mut builder = HyperClient::builder(TokioExecutor::new());
474 if let Some(n) = self.pool_max_idle_per_host {
475 builder.pool_max_idle_per_host(n);
476 }
477 if let Some(dur) = self.pool_idle_timeout {
478 builder.pool_idle_timeout(dur);
479 }
480 if let Some(ref h2) = self.http2_settings {
481 apply_http2_settings(&mut builder, h2);
482 }
483 let inner = builder.build(https_connector);
484 let mut default_headers = self.default_headers;
485 if let Some(agent) = &self.user_agent {
486 if let Ok(val) = HeaderValue::from_str(agent) {
487 default_headers.insert(http::header::USER_AGENT, val);
488 }
489 }
490 Ok(Client {
491 inner,
492 redirect_policy: self.redirect_policy,
493 retry_policy: self.retry_policy,
494 default_headers,
495 connect_timeout: self.connect_timeout,
496 read_timeout: self.read_timeout,
497 decompression: self.decompression,
498 middleware: self.middleware,
499 cookie_jar: self.cookie_jar.clone(),
500 #[cfg(feature = "tls")]
501 tls_rebuild: None,
502 })
503 }
504
505 #[cfg(all(feature = "tls", feature = "socks"))]
511 pub fn build_socks5_proxy_https(
512 self,
513 ) -> Result<Client<OxiHttpsConnector<Socks5Connector>>, OxiHttpError> {
514 let proxy_uri = match self.proxy.as_ref() {
515 Some(ProxyKind::Socks5(u)) => u.clone(),
516 Some(ProxyKind::HttpConnect(_)) => {
517 return Err(OxiHttpError::ConnectionPool(
518 "HTTP CONNECT proxy configured; use build_proxy_https() instead".into(),
519 ))
520 }
521 None => {
522 return Err(OxiHttpError::ConnectionPool(
523 "no proxy configured; call with_socks5_proxy() first".into(),
524 ))
525 }
526 };
527
528 let tls_connector = self.build_tls_connector_inner()?;
529
530 let socks_connector = Socks5Connector::new(proxy_uri, self.connect_timeout);
531 let https_connector = OxiHttpsConnector::new(socks_connector, tls_connector);
532
533 let mut builder = HyperClient::builder(TokioExecutor::new());
534 if let Some(n) = self.pool_max_idle_per_host {
535 builder.pool_max_idle_per_host(n);
536 }
537 if let Some(dur) = self.pool_idle_timeout {
538 builder.pool_idle_timeout(dur);
539 }
540 if let Some(ref h2) = self.http2_settings {
541 apply_http2_settings(&mut builder, h2);
542 }
543 let inner = builder.build(https_connector);
544 let mut default_headers = self.default_headers;
545 if let Some(agent) = &self.user_agent {
546 if let Ok(val) = HeaderValue::from_str(agent) {
547 default_headers.insert(http::header::USER_AGENT, val);
548 }
549 }
550 Ok(Client {
551 inner,
552 redirect_policy: self.redirect_policy,
553 retry_policy: self.retry_policy,
554 default_headers,
555 connect_timeout: self.connect_timeout,
556 read_timeout: self.read_timeout,
557 decompression: self.decompression,
558 middleware: self.middleware,
559 cookie_jar: self.cookie_jar.clone(),
560 #[cfg(feature = "tls")]
561 tls_rebuild: None,
562 })
563 }
564
565 #[cfg(feature = "tls")]
572 pub fn with_tls(mut self) -> Self {
573 self.use_webpki_roots = true;
574 self
575 }
576
577 #[cfg(feature = "tls")]
579 pub fn with_webpki_roots(mut self) -> Self {
580 self.use_webpki_roots = true;
581 self
582 }
583
584 #[cfg(feature = "tls")]
588 pub fn with_trusted_cert_der(mut self, der: Vec<u8>) -> Self {
589 self.trusted_certs_der.push(der);
590 self
591 }
592
593 #[cfg(feature = "tls")]
595 pub fn with_alpn(mut self, protocols: &[&str]) -> Self {
596 self.alpn = protocols.iter().map(|s| s.to_string()).collect();
597 self
598 }
599
600 #[cfg(feature = "tls")]
604 pub fn with_danger_accept_invalid_certs(mut self) -> Self {
605 self.accept_invalid_certs = true;
606 self
607 }
608
609 #[cfg(feature = "tls")]
615 pub fn with_key_log_file(mut self, path: std::path::PathBuf) -> Self {
616 self.key_log_path = Some(path);
617 self
618 }
619
620 #[cfg(feature = "tls")]
630 pub fn with_early_data(mut self) -> Self {
631 self.early_data = true;
632 self
633 }
634
635 #[cfg(feature = "tls")]
661 pub fn danger_accept_invalid_certs(mut self, accept: bool) -> Self {
662 self.accept_invalid_certs = accept;
663 self
664 }
665
666 #[cfg(feature = "tls")]
702 pub fn with_custom_cert_verifier(
703 mut self,
704 verifier: std::sync::Arc<dyn rustls::client::danger::ServerCertVerifier>,
705 ) -> Self {
706 self.custom_cert_verifier = Some(verifier);
707 self
708 }
709
710 #[cfg(feature = "tls")]
716 fn build_tls_connector_inner(&self) -> Result<tokio_rustls::TlsConnector, OxiHttpError> {
717 if let Some(ref verifier) = self.custom_cert_verifier {
718 tls::build_tls_connector_with_verifier(
719 std::sync::Arc::clone(verifier),
720 &self.alpn,
721 self.early_data,
722 )
723 } else {
724 tls::build_tls_connector(
725 &self.trusted_certs_der,
726 &self.alpn,
727 self.accept_invalid_certs,
728 self.use_webpki_roots,
729 self.key_log_path.clone(),
730 self.early_data,
731 )
732 }
733 }
734
735 pub fn build(self) -> Result<Client, OxiHttpError> {
739 let mut builder = HyperClient::builder(TokioExecutor::new());
740
741 if let Some(n) = self.pool_max_idle_per_host {
742 builder.pool_max_idle_per_host(n);
743 }
744 if let Some(dur) = self.pool_idle_timeout {
745 builder.pool_idle_timeout(dur);
746 }
747 if let Some(ref h2) = self.http2_settings {
748 apply_http2_settings(&mut builder, h2);
749 }
750
751 let mut http = HttpConnector::new();
753 http.enforce_http(false);
754 if let Some(dur) = self.connect_timeout {
755 http.set_connect_timeout(Some(dur));
756 }
757 if let Some(nodelay) = self.tcp_nodelay {
758 http.set_nodelay(nodelay);
759 }
760 if let Some(ka) = self.tcp_keepalive {
761 http.set_keepalive(Some(ka));
762 }
763
764 let inner = builder.build(http);
765
766 let mut default_headers = self.default_headers;
767 if let Some(agent) = &self.user_agent {
768 if let Ok(val) = HeaderValue::from_str(agent) {
769 default_headers.insert(http::header::USER_AGENT, val);
770 }
771 }
772
773 Ok(Client {
774 inner,
775 redirect_policy: self.redirect_policy,
776 retry_policy: self.retry_policy,
777 default_headers,
778 connect_timeout: self.connect_timeout,
779 read_timeout: self.read_timeout,
780 decompression: self.decompression,
781 middleware: self.middleware,
782 cookie_jar: self.cookie_jar.clone(),
783 #[cfg(feature = "tls")]
784 tls_rebuild: None,
785 })
786 }
787
788 #[cfg(feature = "tls")]
794 pub fn build_https(self) -> Result<super::HttpsClient, OxiHttpError> {
795 let connector = self.build_tls_connector_inner()?;
796
797 let mut http = HttpConnector::new();
798 http.enforce_http(false);
803 if let Some(dur) = self.connect_timeout {
804 http.set_connect_timeout(Some(dur));
805 }
806 if let Some(nodelay) = self.tcp_nodelay {
807 http.set_nodelay(nodelay);
808 }
809 if let Some(ka) = self.tcp_keepalive {
810 http.set_keepalive(Some(ka));
811 }
812 let https_connector = OxiHttpsConnector::new(http, connector);
813
814 let mut builder = HyperClient::builder(TokioExecutor::new());
815
816 if let Some(n) = self.pool_max_idle_per_host {
817 builder.pool_max_idle_per_host(n);
818 }
819 if let Some(dur) = self.pool_idle_timeout {
820 builder.pool_idle_timeout(dur);
821 }
822 if let Some(ref h2) = self.http2_settings {
823 apply_http2_settings(&mut builder, h2);
824 }
825
826 let inner = builder.build(https_connector);
827
828 let mut default_headers = self.default_headers;
829 if let Some(agent) = &self.user_agent {
830 if let Ok(val) = HeaderValue::from_str(agent) {
831 default_headers.insert(http::header::USER_AGENT, val);
832 }
833 }
834
835 let tls_rebuild = Arc::new(TlsRebuildConfig {
836 trusted_certs_der: self.trusted_certs_der.clone(),
837 alpn: self.alpn.clone(),
838 accept_invalid_certs: self.accept_invalid_certs,
839 use_webpki_roots: self.use_webpki_roots,
840 key_log_path: self.key_log_path.clone(),
841 early_data: self.early_data,
842 connect_timeout: self.connect_timeout,
843 tcp_nodelay: self.tcp_nodelay,
844 tcp_keepalive: self.tcp_keepalive,
845 http2_settings: self.http2_settings.clone(),
846 pool_max_idle_per_host: self.pool_max_idle_per_host,
847 pool_idle_timeout: self.pool_idle_timeout,
848 custom_cert_verifier: self.custom_cert_verifier,
849 });
850
851 Ok(Client {
852 inner,
853 redirect_policy: self.redirect_policy,
854 retry_policy: self.retry_policy,
855 default_headers,
856 connect_timeout: self.connect_timeout,
857 read_timeout: self.read_timeout,
858 decompression: self.decompression,
859 middleware: self.middleware,
860 cookie_jar: self.cookie_jar.clone(),
861 tls_rebuild: Some(tls_rebuild),
862 })
863 }
864
865 pub fn build_with_resolver(self) -> Result<super::ResolverClient, OxiHttpError> {
871 let resolver = self.resolver.ok_or_else(|| {
872 OxiHttpError::Dns("with_resolver must be called before build_with_resolver".into())
873 })?;
874 let mut http = HttpConnector::new_with_resolver(BoxResolver(resolver));
875 http.enforce_http(false);
876 if let Some(dur) = self.connect_timeout {
877 http.set_connect_timeout(Some(dur));
878 }
879 if let Some(nodelay) = self.tcp_nodelay {
880 http.set_nodelay(nodelay);
881 }
882 if let Some(ka) = self.tcp_keepalive {
883 http.set_keepalive(Some(ka));
884 }
885
886 let mut builder = HyperClient::builder(TokioExecutor::new());
887 if let Some(n) = self.pool_max_idle_per_host {
888 builder.pool_max_idle_per_host(n);
889 }
890 if let Some(d) = self.pool_idle_timeout {
891 builder.pool_idle_timeout(d);
892 }
893 if let Some(ref h2) = self.http2_settings {
894 apply_http2_settings(&mut builder, h2);
895 }
896
897 let mut default_headers = self.default_headers;
898 if let Some(agent) = &self.user_agent {
899 if let Ok(val) = HeaderValue::from_str(agent) {
900 default_headers.insert(http::header::USER_AGENT, val);
901 }
902 }
903
904 Ok(Client {
905 inner: builder.build(http),
906 connect_timeout: self.connect_timeout,
907 read_timeout: self.read_timeout,
908 redirect_policy: self.redirect_policy,
909 retry_policy: self.retry_policy,
910 default_headers,
911 decompression: self.decompression,
912 middleware: self.middleware,
913 cookie_jar: self.cookie_jar,
914 #[cfg(feature = "tls")]
915 tls_rebuild: None,
916 })
917 }
918
919 #[cfg(feature = "tls")]
924 pub fn build_https_with_resolver(self) -> Result<super::ResolverHttpsClient, OxiHttpError> {
925 let tls_connector = self.build_tls_connector_inner()?;
929
930 let resolver = self.resolver.ok_or_else(|| {
931 OxiHttpError::Dns(
932 "with_resolver must be called before build_https_with_resolver".into(),
933 )
934 })?;
935
936 let mut http = HttpConnector::new_with_resolver(BoxResolver(resolver));
937 http.enforce_http(false);
938 if let Some(dur) = self.connect_timeout {
939 http.set_connect_timeout(Some(dur));
940 }
941 if let Some(nodelay) = self.tcp_nodelay {
942 http.set_nodelay(nodelay);
943 }
944 if let Some(ka) = self.tcp_keepalive {
945 http.set_keepalive(Some(ka));
946 }
947
948 let connector = crate::connector::OxiHttpsConnector::new(http, tls_connector);
949
950 let mut builder = HyperClient::builder(TokioExecutor::new());
951 if let Some(n) = self.pool_max_idle_per_host {
952 builder.pool_max_idle_per_host(n);
953 }
954 if let Some(d) = self.pool_idle_timeout {
955 builder.pool_idle_timeout(d);
956 }
957 if let Some(ref h2) = self.http2_settings {
958 apply_http2_settings(&mut builder, h2);
959 }
960
961 let mut default_headers = self.default_headers;
962 if let Some(agent) = &self.user_agent {
963 if let Ok(val) = HeaderValue::from_str(agent) {
964 default_headers.insert(http::header::USER_AGENT, val);
965 }
966 }
967
968 Ok(Client {
969 inner: builder.build(connector),
970 connect_timeout: self.connect_timeout,
971 read_timeout: self.read_timeout,
972 redirect_policy: self.redirect_policy,
973 retry_policy: self.retry_policy,
974 default_headers,
975 decompression: self.decompression,
976 middleware: self.middleware,
977 cookie_jar: self.cookie_jar,
978 #[cfg(feature = "tls")]
979 tls_rebuild: None,
980 })
981 }
982}
983
984impl Default for ClientBuilder {
985 fn default() -> Self {
986 Self::new()
987 }
988}
989
990impl std::fmt::Debug for ClientBuilder {
991 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
992 f.debug_struct("ClientBuilder")
993 .field("pool_max_idle_per_host", &self.pool_max_idle_per_host)
994 .field("pool_idle_timeout", &self.pool_idle_timeout)
995 .field("connect_timeout", &self.connect_timeout)
996 .field("read_timeout", &self.read_timeout)
997 .field("redirect_policy", &self.redirect_policy)
998 .field("retry_policy", &self.retry_policy)
999 .field("decompression", &self.decompression)
1000 .field("user_agent", &self.user_agent)
1001 .field("tcp_nodelay", &self.tcp_nodelay)
1002 .field("tcp_keepalive", &self.tcp_keepalive)
1003 .finish_non_exhaustive()
1004 }
1005}