1pub(crate) mod backoff;
23
24#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
25mod dns;
26
27#[cfg(not(target_arch = "wasm32"))]
28#[cfg(test)]
29pub(crate) mod mock_server;
30
31pub(crate) mod retry;
32
33#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
34pub(crate) mod pagination;
35
36pub(crate) mod get;
37
38#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
39pub(crate) mod list;
40
41#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
42pub(crate) mod token;
43
44pub(crate) mod header;
45
46#[cfg(any(feature = "aws-base", feature = "gcp-base"))]
47pub(crate) mod s3;
48
49pub(crate) mod builder;
50mod http;
51
52#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
53pub(crate) mod parts;
54pub use http::*;
55
56#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
57mod crypto;
58
59#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
60pub use crypto::*;
61
62use ::http::header::{HeaderMap, HeaderValue};
63use async_trait::async_trait;
64use serde::{Deserialize, Serialize};
65use std::collections::HashMap;
66use std::str::FromStr;
67use std::sync::Arc;
68use std::time::Duration;
69
70#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
71use reqwest::{NoProxy, Proxy};
72
73use crate::config::{ConfigValue, fmt_duration};
74use crate::path::Path;
75use crate::{GetOptions, Result};
76
77#[cfg(feature = "reqwest")]
78fn map_client_error(e: reqwest::Error) -> super::Error {
79 super::Error::Generic {
80 store: "HTTP client",
81 source: Box::new(e),
82 }
83}
84
85#[cfg(feature = "reqwest")]
86static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
87
88#[derive(PartialEq, Eq, Hash, Clone, Debug, Copy, Deserialize, Serialize)]
90#[non_exhaustive]
91pub enum ClientConfigKey {
92 AllowHttp,
97 AllowInvalidCertificates,
114 NoSystemCertificates,
122 ConnectTimeout,
127 DefaultContentType,
132 Http1Only,
137 Http2KeepAliveInterval,
142 Http2KeepAliveTimeout,
147 Http2KeepAliveWhileIdle,
152 Http2MaxFrameSize,
157 Http2Only,
162 PoolIdleTimeout,
169 PoolMaxIdlePerHost,
174 ProxyUrl,
179 ProxyCaCertificate,
184 ProxyExcludes,
189 RandomizeAddresses,
204 ReadTimeout,
213 Timeout,
221 UserAgent,
226}
227
228impl AsRef<str> for ClientConfigKey {
229 fn as_ref(&self) -> &str {
230 match self {
231 Self::AllowHttp => "allow_http",
232 Self::AllowInvalidCertificates => "allow_invalid_certificates",
233 Self::NoSystemCertificates => "disable_system_certificates",
234 Self::ConnectTimeout => "connect_timeout",
235 Self::DefaultContentType => "default_content_type",
236 Self::Http1Only => "http1_only",
237 Self::Http2Only => "http2_only",
238 Self::Http2KeepAliveInterval => "http2_keep_alive_interval",
239 Self::Http2KeepAliveTimeout => "http2_keep_alive_timeout",
240 Self::Http2KeepAliveWhileIdle => "http2_keep_alive_while_idle",
241 Self::Http2MaxFrameSize => "http2_max_frame_size",
242 Self::PoolIdleTimeout => "pool_idle_timeout",
243 Self::PoolMaxIdlePerHost => "pool_max_idle_per_host",
244 Self::ProxyUrl => "proxy_url",
245 Self::ProxyCaCertificate => "proxy_ca_certificate",
246 Self::ProxyExcludes => "proxy_excludes",
247 Self::RandomizeAddresses => "randomize_addresses",
248 Self::ReadTimeout => "read_timeout",
249 Self::Timeout => "timeout",
250 Self::UserAgent => "user_agent",
251 }
252 }
253}
254
255impl FromStr for ClientConfigKey {
256 type Err = super::Error;
257
258 fn from_str(s: &str) -> Result<Self, Self::Err> {
259 match s {
260 "allow_http" => Ok(Self::AllowHttp),
261 "allow_invalid_certificates" => Ok(Self::AllowInvalidCertificates),
262 "disable_system_certificates" => Ok(Self::NoSystemCertificates),
263 "connect_timeout" => Ok(Self::ConnectTimeout),
264 "default_content_type" => Ok(Self::DefaultContentType),
265 "http1_only" => Ok(Self::Http1Only),
266 "http2_only" => Ok(Self::Http2Only),
267 "http2_keep_alive_interval" => Ok(Self::Http2KeepAliveInterval),
268 "http2_keep_alive_timeout" => Ok(Self::Http2KeepAliveTimeout),
269 "http2_keep_alive_while_idle" => Ok(Self::Http2KeepAliveWhileIdle),
270 "http2_max_frame_size" => Ok(Self::Http2MaxFrameSize),
271 "pool_idle_timeout" => Ok(Self::PoolIdleTimeout),
272 "pool_max_idle_per_host" => Ok(Self::PoolMaxIdlePerHost),
273 "proxy_url" => Ok(Self::ProxyUrl),
274 "proxy_ca_certificate" => Ok(Self::ProxyCaCertificate),
275 "proxy_excludes" => Ok(Self::ProxyExcludes),
276 "randomize_addresses" => Ok(Self::RandomizeAddresses),
277 "read_timeout" => Ok(Self::ReadTimeout),
278 "timeout" => Ok(Self::Timeout),
279 "user_agent" => Ok(Self::UserAgent),
280 _ => Err(super::Error::UnknownConfigurationKey {
281 store: "HTTP",
282 key: s.into(),
283 }),
284 }
285 }
286}
287
288#[derive(Debug, Clone)]
293#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
294pub struct Certificate(reqwest::tls::Certificate);
295
296#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
297impl Certificate {
298 pub fn from_pem(pem: &[u8]) -> Result<Self> {
313 Ok(Self(
314 reqwest::tls::Certificate::from_pem(pem).map_err(map_client_error)?,
315 ))
316 }
317
318 pub fn from_pem_bundle(pem_bundle: &[u8]) -> Result<Vec<Self>> {
324 Ok(reqwest::tls::Certificate::from_pem_bundle(pem_bundle)
325 .map_err(map_client_error)?
326 .into_iter()
327 .map(Self)
328 .collect())
329 }
330
331 pub fn from_der(der: &[u8]) -> Result<Self> {
333 Ok(Self(
334 reqwest::tls::Certificate::from_der(der).map_err(map_client_error)?,
335 ))
336 }
337}
338
339#[derive(Debug, Clone)]
341pub struct ClientOptions {
342 user_agent: Option<ConfigValue<HeaderValue>>,
343 #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
344 root_certificates: Vec<Certificate>,
345 no_system_certificates: ConfigValue<bool>,
346 content_type_map: HashMap<String, String>,
347 default_content_type: Option<String>,
348 default_headers: Option<HeaderMap>,
349 proxy_url: Option<String>,
350 proxy_ca_certificate: Option<String>,
351 proxy_excludes: Option<String>,
352 allow_http: ConfigValue<bool>,
353 allow_invalid_certificates: ConfigValue<bool>,
354 timeout: Option<ConfigValue<Duration>>,
355 connect_timeout: Option<ConfigValue<Duration>>,
356 read_timeout: Option<ConfigValue<Duration>>,
357 pool_idle_timeout: Option<ConfigValue<Duration>>,
358 pool_max_idle_per_host: Option<ConfigValue<usize>>,
359 http2_keep_alive_interval: Option<ConfigValue<Duration>>,
360 http2_keep_alive_timeout: Option<ConfigValue<Duration>>,
361 http2_keep_alive_while_idle: ConfigValue<bool>,
362 http2_max_frame_size: Option<ConfigValue<u32>>,
363 http1_only: ConfigValue<bool>,
364 http2_only: ConfigValue<bool>,
365 randomize_addresses: ConfigValue<bool>,
366}
367
368impl Default for ClientOptions {
369 fn default() -> Self {
370 Self {
378 user_agent: None,
379 #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
380 root_certificates: Default::default(),
381 no_system_certificates: false.into(),
382 content_type_map: Default::default(),
383 default_content_type: None,
384 default_headers: None,
385 proxy_url: None,
386 proxy_ca_certificate: None,
387 proxy_excludes: None,
388 allow_http: Default::default(),
389 allow_invalid_certificates: Default::default(),
390 timeout: Some(Duration::from_secs(30).into()),
391 connect_timeout: Some(Duration::from_secs(5).into()),
392 read_timeout: None,
393 pool_idle_timeout: None,
394 pool_max_idle_per_host: None,
395 http2_keep_alive_interval: None,
396 http2_keep_alive_timeout: None,
397 http2_keep_alive_while_idle: Default::default(),
398 http2_max_frame_size: None,
399 http1_only: true.into(),
403 http2_only: Default::default(),
404 randomize_addresses: true.into(),
405 }
406 }
407}
408
409impl ClientOptions {
410 pub fn new() -> Self {
412 Default::default()
413 }
414
415 pub fn with_config(mut self, key: ClientConfigKey, value: impl Into<String>) -> Self {
417 match key {
418 ClientConfigKey::AllowHttp => self.allow_http.parse(value),
419 ClientConfigKey::AllowInvalidCertificates => {
420 self.allow_invalid_certificates.parse(value)
421 }
422 ClientConfigKey::NoSystemCertificates => self.no_system_certificates.parse(value),
423 ClientConfigKey::ConnectTimeout => {
424 self.connect_timeout = Some(ConfigValue::Deferred(value.into()))
425 }
426 ClientConfigKey::ReadTimeout => {
427 self.read_timeout = Some(ConfigValue::Deferred(value.into()))
428 }
429 ClientConfigKey::DefaultContentType => self.default_content_type = Some(value.into()),
430 ClientConfigKey::Http1Only => self.http1_only.parse(value),
431 ClientConfigKey::Http2Only => self.http2_only.parse(value),
432 ClientConfigKey::Http2KeepAliveInterval => {
433 self.http2_keep_alive_interval = Some(ConfigValue::Deferred(value.into()))
434 }
435 ClientConfigKey::Http2KeepAliveTimeout => {
436 self.http2_keep_alive_timeout = Some(ConfigValue::Deferred(value.into()))
437 }
438 ClientConfigKey::Http2KeepAliveWhileIdle => {
439 self.http2_keep_alive_while_idle.parse(value)
440 }
441 ClientConfigKey::Http2MaxFrameSize => {
442 self.http2_max_frame_size = Some(ConfigValue::Deferred(value.into()))
443 }
444 ClientConfigKey::PoolIdleTimeout => {
445 self.pool_idle_timeout = Some(ConfigValue::Deferred(value.into()))
446 }
447 ClientConfigKey::PoolMaxIdlePerHost => {
448 self.pool_max_idle_per_host = Some(ConfigValue::Deferred(value.into()))
449 }
450 ClientConfigKey::ProxyUrl => self.proxy_url = Some(value.into()),
451 ClientConfigKey::ProxyCaCertificate => self.proxy_ca_certificate = Some(value.into()),
452 ClientConfigKey::ProxyExcludes => self.proxy_excludes = Some(value.into()),
453 ClientConfigKey::RandomizeAddresses => {
454 self.randomize_addresses.parse(value);
455 }
456 ClientConfigKey::Timeout => self.timeout = Some(ConfigValue::Deferred(value.into())),
457 ClientConfigKey::UserAgent => {
458 self.user_agent = Some(ConfigValue::Deferred(value.into()))
459 }
460 }
461 self
462 }
463
464 pub fn get_config_value(&self, key: &ClientConfigKey) -> Option<String> {
466 match key {
467 ClientConfigKey::AllowHttp => Some(self.allow_http.to_string()),
468 ClientConfigKey::AllowInvalidCertificates => {
469 Some(self.allow_invalid_certificates.to_string())
470 }
471 ClientConfigKey::NoSystemCertificates => Some(self.no_system_certificates.to_string()),
472 ClientConfigKey::ConnectTimeout => self.connect_timeout.as_ref().map(fmt_duration),
473 ClientConfigKey::ReadTimeout => self.read_timeout.as_ref().map(fmt_duration),
474 ClientConfigKey::DefaultContentType => self.default_content_type.clone(),
475 ClientConfigKey::Http1Only => Some(self.http1_only.to_string()),
476 ClientConfigKey::Http2KeepAliveInterval => {
477 self.http2_keep_alive_interval.as_ref().map(fmt_duration)
478 }
479 ClientConfigKey::Http2KeepAliveTimeout => {
480 self.http2_keep_alive_timeout.as_ref().map(fmt_duration)
481 }
482 ClientConfigKey::Http2KeepAliveWhileIdle => {
483 Some(self.http2_keep_alive_while_idle.to_string())
484 }
485 ClientConfigKey::Http2MaxFrameSize => {
486 self.http2_max_frame_size.as_ref().map(|v| v.to_string())
487 }
488 ClientConfigKey::Http2Only => Some(self.http2_only.to_string()),
489 ClientConfigKey::PoolIdleTimeout => self.pool_idle_timeout.as_ref().map(fmt_duration),
490 ClientConfigKey::PoolMaxIdlePerHost => {
491 self.pool_max_idle_per_host.as_ref().map(|v| v.to_string())
492 }
493 ClientConfigKey::ProxyUrl => self.proxy_url.clone(),
494 ClientConfigKey::ProxyCaCertificate => self.proxy_ca_certificate.clone(),
495 ClientConfigKey::ProxyExcludes => self.proxy_excludes.clone(),
496 ClientConfigKey::RandomizeAddresses => Some(self.randomize_addresses.to_string()),
497 ClientConfigKey::Timeout => self.timeout.as_ref().map(fmt_duration),
498 ClientConfigKey::UserAgent => self
499 .user_agent
500 .as_ref()
501 .and_then(|v| v.get().ok())
502 .and_then(|v| v.to_str().ok().map(|s| s.to_string())),
503 }
504 }
505
506 pub fn with_user_agent(mut self, agent: HeaderValue) -> Self {
510 self.user_agent = Some(agent.into());
511 self
512 }
513
514 #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
519 pub fn with_root_certificate(mut self, certificate: Certificate) -> Self {
520 self.root_certificates.push(certificate);
521 self
522 }
523
524 pub fn with_default_content_type(mut self, mime: impl Into<String>) -> Self {
526 self.default_content_type = Some(mime.into());
527 self
528 }
529
530 pub fn with_content_type_for_suffix(
532 mut self,
533 extension: impl Into<String>,
534 mime: impl Into<String>,
535 ) -> Self {
536 self.content_type_map.insert(extension.into(), mime.into());
537 self
538 }
539
540 pub fn with_default_headers(mut self, headers: HeaderMap) -> Self {
542 self.default_headers = Some(headers);
543 self
544 }
545
546 pub fn with_allow_http(mut self, allow_http: bool) -> Self {
552 self.allow_http = allow_http.into();
553 self
554 }
555
556 pub fn with_allow_invalid_certificates(mut self, allow_invalid_certificates: bool) -> Self {
574 self.allow_invalid_certificates = allow_invalid_certificates.into();
575 self
576 }
577
578 pub fn with_no_system_certificates(mut self, no_certs: bool) -> Self {
588 self.no_system_certificates = no_certs.into();
589 self
590 }
591
592 pub fn with_http1_only(mut self) -> Self {
602 self.http2_only = false.into();
603 self.http1_only = true.into();
604 self
605 }
606
607 pub fn with_http2_only(mut self) -> Self {
617 self.http1_only = false.into();
618 self.http2_only = true.into();
619 self
620 }
621
622 pub fn with_allow_http2(mut self) -> Self {
632 self.http1_only = false.into();
633 self.http2_only = false.into();
634 self
635 }
636
637 pub fn with_proxy_url(mut self, proxy_url: impl Into<String>) -> Self {
639 self.proxy_url = Some(proxy_url.into());
640 self
641 }
642
643 pub fn with_proxy_ca_certificate(mut self, proxy_ca_certificate: impl Into<String>) -> Self {
645 self.proxy_ca_certificate = Some(proxy_ca_certificate.into());
646 self
647 }
648
649 pub fn with_proxy_excludes(mut self, proxy_excludes: impl Into<String>) -> Self {
651 self.proxy_excludes = Some(proxy_excludes.into());
652 self
653 }
654
655 pub fn with_timeout(mut self, timeout: Duration) -> Self {
671 self.timeout = Some(ConfigValue::Parsed(timeout));
672 self
673 }
674
675 pub fn with_timeout_disabled(mut self) -> Self {
680 self.timeout = None;
681 self
682 }
683
684 pub fn with_connect_timeout(mut self, timeout: Duration) -> Self {
700 self.connect_timeout = Some(ConfigValue::Parsed(timeout));
701 self
702 }
703
704 pub fn with_connect_timeout_disabled(mut self) -> Self {
709 self.connect_timeout = None;
710 self
711 }
712
713 pub fn with_read_timeout(mut self, timeout: Duration) -> Self {
730 self.read_timeout = Some(ConfigValue::Parsed(timeout));
731 self
732 }
733
734 pub fn with_read_timeout_disabled(mut self) -> Self {
739 self.read_timeout = None;
740 self
741 }
742
743 pub fn with_pool_idle_timeout(mut self, timeout: Duration) -> Self {
749 self.pool_idle_timeout = Some(ConfigValue::Parsed(timeout));
750 self
751 }
752
753 pub fn with_pool_max_idle_per_host(mut self, max: usize) -> Self {
757 self.pool_max_idle_per_host = Some(max.into());
758 self
759 }
760
761 pub fn with_http2_keep_alive_interval(mut self, interval: Duration) -> Self {
765 self.http2_keep_alive_interval = Some(ConfigValue::Parsed(interval));
766 self
767 }
768
769 pub fn with_http2_keep_alive_timeout(mut self, interval: Duration) -> Self {
776 self.http2_keep_alive_timeout = Some(ConfigValue::Parsed(interval));
777 self
778 }
779
780 pub fn with_http2_keep_alive_while_idle(mut self) -> Self {
787 self.http2_keep_alive_while_idle = true.into();
788 self
789 }
790
791 pub fn with_http2_max_frame_size(mut self, sz: u32) -> Self {
795 self.http2_max_frame_size = Some(ConfigValue::Parsed(sz));
796 self
797 }
798
799 pub fn get_default_headers(&self) -> Option<&HeaderMap> {
801 self.default_headers.as_ref()
802 }
803
804 pub fn get_content_type(&self, path: &Path) -> Option<&str> {
813 match path.extension() {
814 Some(extension) => match self.content_type_map.get(extension) {
815 Some(ct) => Some(ct.as_str()),
816 None => self.default_content_type.as_deref(),
817 },
818 None => self.default_content_type.as_deref(),
819 }
820 }
821
822 #[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
828 pub(crate) fn metadata_options(&self) -> Self {
829 self.clone()
830 .with_allow_http(true)
831 .with_connect_timeout(Duration::from_secs(1))
832 }
833
834 #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
835 pub(crate) fn client(&self) -> Result<reqwest::Client> {
836 let mut builder = reqwest::ClientBuilder::new();
837
838 match &self.user_agent {
839 Some(user_agent) => builder = builder.user_agent(user_agent.get()?),
840 None => builder = builder.user_agent(DEFAULT_USER_AGENT),
841 }
842
843 if let Some(headers) = &self.default_headers {
844 builder = builder.default_headers(headers.clone())
845 }
846
847 if let Some(proxy) = &self.proxy_url {
848 let mut proxy = Proxy::all(proxy).map_err(map_client_error)?;
849
850 if let Some(certificate) = &self.proxy_ca_certificate {
851 let certificate = reqwest::tls::Certificate::from_pem(certificate.as_bytes())
852 .map_err(map_client_error)?;
853
854 builder = builder.tls_certs_merge(std::iter::once(certificate));
855 }
856
857 if let Some(proxy_excludes) = &self.proxy_excludes {
858 let no_proxy = NoProxy::from_string(proxy_excludes);
859
860 proxy = proxy.no_proxy(no_proxy);
861 }
862
863 builder = builder.proxy(proxy);
864 }
865
866 let certs = self
867 .root_certificates
868 .iter()
869 .map(|certificate| certificate.0.clone());
870
871 if self.no_system_certificates.get()? {
872 builder = builder.tls_certs_only(certs);
873 } else {
874 builder = builder.tls_certs_merge(certs);
875 }
876
877 if let Some(timeout) = &self.timeout {
878 builder = builder.timeout(timeout.get()?)
879 }
880
881 if let Some(timeout) = &self.connect_timeout {
882 builder = builder.connect_timeout(timeout.get()?)
883 }
884
885 if let Some(timeout) = &self.read_timeout {
886 builder = builder.read_timeout(timeout.get()?)
887 }
888
889 if let Some(timeout) = &self.pool_idle_timeout {
890 builder = builder.pool_idle_timeout(timeout.get()?)
891 }
892
893 if let Some(max) = &self.pool_max_idle_per_host {
894 builder = builder.pool_max_idle_per_host(max.get()?)
895 }
896
897 if let Some(interval) = &self.http2_keep_alive_interval {
898 builder = builder.http2_keep_alive_interval(interval.get()?)
899 }
900
901 if let Some(interval) = &self.http2_keep_alive_timeout {
902 builder = builder.http2_keep_alive_timeout(interval.get()?)
903 }
904
905 if self.http2_keep_alive_while_idle.get()? {
906 builder = builder.http2_keep_alive_while_idle(true)
907 }
908
909 if let Some(sz) = &self.http2_max_frame_size {
910 builder = builder.http2_max_frame_size(Some(sz.get()?))
911 }
912
913 if self.http1_only.get()? {
914 builder = builder.http1_only()
915 }
916
917 if self.http2_only.get()? {
918 builder = builder.http2_prior_knowledge()
919 }
920
921 if self.allow_invalid_certificates.get()? {
922 builder = builder.danger_accept_invalid_certs(true)
923 }
924
925 builder = builder.no_gzip().no_brotli().no_zstd().no_deflate();
930
931 if self.randomize_addresses.get()? {
932 builder = builder.dns_resolver(Arc::new(dns::ShuffleResolver));
933 }
934
935 builder
936 .https_only(!self.allow_http.get()?)
937 .build()
938 .map_err(map_client_error)
939 }
940
941 #[cfg(all(feature = "reqwest", target_arch = "wasm32", target_os = "unknown"))]
942 pub(crate) fn client(&self) -> Result<reqwest::Client> {
943 let mut builder = reqwest::ClientBuilder::new();
944
945 match &self.user_agent {
946 Some(user_agent) => builder = builder.user_agent(user_agent.get()?),
947 None => builder = builder.user_agent(DEFAULT_USER_AGENT),
948 }
949
950 if let Some(headers) = &self.default_headers {
951 builder = builder.default_headers(headers.clone())
952 }
953
954 builder.build().map_err(map_client_error)
955 }
956}
957
958pub(crate) trait GetOptionsExt {
959 fn with_get_options(self, options: GetOptions) -> Self;
960}
961
962impl GetOptionsExt for HttpRequestBuilder {
963 fn with_get_options(mut self, options: GetOptions) -> Self {
964 use hyper::header::*;
965
966 let GetOptions {
967 if_match,
968 if_none_match,
969 if_modified_since,
970 if_unmodified_since,
971 range,
972 version: _,
973 head: _,
974 extensions,
975 } = options;
976
977 if let Some(range) = range {
978 self = self.header(RANGE, range.to_string());
979 }
980
981 if let Some(tag) = if_match {
982 self = self.header(IF_MATCH, tag);
983 }
984
985 if let Some(tag) = if_none_match {
986 self = self.header(IF_NONE_MATCH, tag);
987 }
988
989 const DATE_FORMAT: &str = "%a, %d %b %Y %H:%M:%S GMT";
990 if let Some(date) = if_unmodified_since {
991 self = self.header(IF_UNMODIFIED_SINCE, date.format(DATE_FORMAT).to_string());
992 }
993
994 if let Some(date) = if_modified_since {
995 self = self.header(IF_MODIFIED_SINCE, date.format(DATE_FORMAT).to_string());
996 }
997
998 self = self.extensions(extensions);
999
1000 self
1001 }
1002}
1003
1004#[async_trait]
1006pub trait CredentialProvider: std::fmt::Debug + Send + Sync {
1007 type Credential;
1009
1010 async fn get_credential(&self) -> Result<Arc<Self::Credential>>;
1012}
1013
1014#[derive(Debug)]
1016pub struct StaticCredentialProvider<T> {
1017 credential: Arc<T>,
1018}
1019
1020impl<T> StaticCredentialProvider<T> {
1021 pub fn new(credential: T) -> Self {
1023 Self {
1024 credential: Arc::new(credential),
1025 }
1026 }
1027}
1028
1029#[async_trait]
1030impl<T> CredentialProvider for StaticCredentialProvider<T>
1031where
1032 T: std::fmt::Debug + Send + Sync,
1033{
1034 type Credential = T;
1035
1036 async fn get_credential(&self) -> Result<Arc<T>> {
1037 Ok(Arc::clone(&self.credential))
1038 }
1039}
1040
1041#[cfg(any(feature = "aws-base", feature = "azure-base", feature = "gcp-base"))]
1042mod cloud {
1043 use super::*;
1044 use crate::RetryConfig;
1045 use crate::client::token::{TemporaryToken, TokenCache};
1046
1047 #[derive(Debug)]
1049 pub(crate) struct TokenCredentialProvider<T: TokenProvider> {
1050 inner: T,
1051 client: HttpClient,
1052 retry: RetryConfig,
1053 cache: TokenCache<Arc<T::Credential>>,
1054 }
1055
1056 impl<T: TokenProvider> TokenCredentialProvider<T> {
1057 pub(crate) fn new(inner: T, client: HttpClient, retry: RetryConfig) -> Self {
1058 Self {
1059 inner,
1060 client,
1061 retry,
1062 cache: Default::default(),
1063 }
1064 }
1065
1066 #[cfg(any(feature = "aws-base", feature = "gcp-base"))]
1068 pub(crate) fn with_min_ttl(mut self, min_ttl: Duration) -> Self {
1069 self.cache = self.cache.with_min_ttl(min_ttl);
1070 self
1071 }
1072 }
1073
1074 #[async_trait]
1075 impl<T: TokenProvider> CredentialProvider for TokenCredentialProvider<T> {
1076 type Credential = T::Credential;
1077
1078 async fn get_credential(&self) -> Result<Arc<Self::Credential>> {
1079 self.cache
1080 .get_or_insert_with(|| self.inner.fetch_token(&self.client, &self.retry))
1081 .await
1082 }
1083 }
1084
1085 #[async_trait]
1086 pub(crate) trait TokenProvider: std::fmt::Debug + Send + Sync {
1087 type Credential: std::fmt::Debug + Send + Sync;
1088
1089 async fn fetch_token(
1090 &self,
1091 client: &HttpClient,
1092 retry: &RetryConfig,
1093 ) -> Result<TemporaryToken<Arc<Self::Credential>>>;
1094 }
1095}
1096
1097use crate::client::builder::HttpRequestBuilder;
1098#[cfg(any(feature = "aws-base", feature = "azure-base", feature = "gcp-base"))]
1099pub(crate) use cloud::*;
1100
1101#[cfg(test)]
1102mod tests {
1103 use super::*;
1104 use std::collections::HashMap;
1105
1106 #[test]
1107 fn client_test_config_from_map() {
1108 let allow_http = "true".to_string();
1109 let allow_invalid_certificates = "false".to_string();
1110 let connect_timeout = "90 seconds".to_string();
1111 let default_content_type = "object_store:fake_default_content_type".to_string();
1112 let http1_only = "true".to_string();
1113 let http2_only = "false".to_string();
1114 let http2_keep_alive_interval = "90 seconds".to_string();
1115 let http2_keep_alive_timeout = "91 seconds".to_string();
1116 let http2_keep_alive_while_idle = "92 seconds".to_string();
1117 let http2_max_frame_size = "1337".to_string();
1118 let no_system_certificates = "true".to_string();
1119 let pool_idle_timeout = "93 seconds".to_string();
1120 let pool_max_idle_per_host = "94".to_string();
1121 let proxy_url = "https://fake_proxy_url".to_string();
1122 let read_timeout = "45 seconds".to_string();
1123 let timeout = "95 seconds".to_string();
1124 let user_agent = "object_store:fake_user_agent".to_string();
1125
1126 let options = HashMap::from([
1127 ("allow_http", allow_http.clone()),
1128 (
1129 "allow_invalid_certificates",
1130 allow_invalid_certificates.clone(),
1131 ),
1132 ("connect_timeout", connect_timeout.clone()),
1133 ("default_content_type", default_content_type.clone()),
1134 ("http1_only", http1_only.clone()),
1135 ("http2_only", http2_only.clone()),
1136 (
1137 "http2_keep_alive_interval",
1138 http2_keep_alive_interval.clone(),
1139 ),
1140 ("http2_keep_alive_timeout", http2_keep_alive_timeout.clone()),
1141 (
1142 "http2_keep_alive_while_idle",
1143 http2_keep_alive_while_idle.clone(),
1144 ),
1145 ("http2_max_frame_size", http2_max_frame_size.clone()),
1146 (
1147 "disable_system_certificates",
1148 no_system_certificates.clone(),
1149 ),
1150 ("pool_idle_timeout", pool_idle_timeout.clone()),
1151 ("pool_max_idle_per_host", pool_max_idle_per_host.clone()),
1152 ("proxy_url", proxy_url.clone()),
1153 ("read_timeout", read_timeout.clone()),
1154 ("timeout", timeout.clone()),
1155 ("user_agent", user_agent.clone()),
1156 ]);
1157
1158 let builder = options
1159 .into_iter()
1160 .fold(ClientOptions::new(), |builder, (key, value)| {
1161 builder.with_config(key.parse().unwrap(), value)
1162 });
1163
1164 assert_eq!(
1165 builder
1166 .get_config_value(&ClientConfigKey::AllowHttp)
1167 .unwrap(),
1168 allow_http
1169 );
1170 assert_eq!(
1171 builder
1172 .get_config_value(&ClientConfigKey::AllowInvalidCertificates)
1173 .unwrap(),
1174 allow_invalid_certificates
1175 );
1176 assert_eq!(
1177 builder
1178 .get_config_value(&ClientConfigKey::ConnectTimeout)
1179 .unwrap(),
1180 connect_timeout
1181 );
1182 assert_eq!(
1183 builder
1184 .get_config_value(&ClientConfigKey::DefaultContentType)
1185 .unwrap(),
1186 default_content_type
1187 );
1188 assert_eq!(
1189 builder
1190 .get_config_value(&ClientConfigKey::Http1Only)
1191 .unwrap(),
1192 http1_only
1193 );
1194 assert_eq!(
1195 builder
1196 .get_config_value(&ClientConfigKey::Http2Only)
1197 .unwrap(),
1198 http2_only
1199 );
1200 assert_eq!(
1201 builder
1202 .get_config_value(&ClientConfigKey::Http2KeepAliveInterval)
1203 .unwrap(),
1204 http2_keep_alive_interval
1205 );
1206 assert_eq!(
1207 builder
1208 .get_config_value(&ClientConfigKey::Http2KeepAliveTimeout)
1209 .unwrap(),
1210 http2_keep_alive_timeout
1211 );
1212 assert_eq!(
1213 builder
1214 .get_config_value(&ClientConfigKey::Http2KeepAliveWhileIdle)
1215 .unwrap(),
1216 http2_keep_alive_while_idle
1217 );
1218 assert_eq!(
1219 builder
1220 .get_config_value(&ClientConfigKey::Http2MaxFrameSize)
1221 .unwrap(),
1222 http2_max_frame_size
1223 );
1224 assert_eq!(
1225 builder
1226 .get_config_value(&ClientConfigKey::NoSystemCertificates)
1227 .unwrap(),
1228 no_system_certificates
1229 );
1230
1231 assert_eq!(
1232 builder
1233 .get_config_value(&ClientConfigKey::PoolIdleTimeout)
1234 .unwrap(),
1235 pool_idle_timeout
1236 );
1237 assert_eq!(
1238 builder
1239 .get_config_value(&ClientConfigKey::PoolMaxIdlePerHost)
1240 .unwrap(),
1241 pool_max_idle_per_host
1242 );
1243 assert_eq!(
1244 builder
1245 .get_config_value(&ClientConfigKey::ProxyUrl)
1246 .unwrap(),
1247 proxy_url
1248 );
1249 assert_eq!(
1250 builder
1251 .get_config_value(&ClientConfigKey::ReadTimeout)
1252 .unwrap(),
1253 read_timeout
1254 );
1255 assert_eq!(
1256 builder.get_config_value(&ClientConfigKey::Timeout).unwrap(),
1257 timeout
1258 );
1259 assert_eq!(
1260 builder
1261 .get_config_value(&ClientConfigKey::UserAgent)
1262 .unwrap(),
1263 user_agent
1264 );
1265 }
1266}