[go: up one dir, main page]

ureq/
config.rs

1//! Agent configuration
2
3use std::fmt;
4use std::net::SocketAddr;
5use std::sync::Arc;
6use std::time::Duration;
7
8use http::Uri;
9
10use crate::middleware::{Middleware, MiddlewareChain};
11use crate::{http, Body, Error};
12use crate::{Agent, AsSendBody, Proxy, RequestBuilder};
13
14#[cfg(feature = "_tls")]
15use crate::tls::TlsConfig;
16
17pub use ureq_proto::client::RedirectAuthHeaders;
18
19mod private {
20    use super::Config;
21
22    pub trait ConfigScope {
23        fn config(&mut self) -> &mut Config;
24    }
25}
26
27pub(crate) mod typestate {
28    use super::*;
29    use crate::request_ext::WithAgent;
30
31    /// Typestate for [`Config`] when configured for an [`Agent`].
32    pub struct AgentScope(pub(crate) Config);
33    /// Typestate for [`Config`] when configured on the [`RequestBuilder`] level.
34    pub struct RequestScope<Any>(pub(crate) RequestBuilder<Any>);
35    /// Typestate for for [`Config`] when configured via [`Agent::configure_request`].
36    pub struct HttpCrateScope<S: AsSendBody>(pub(crate) http::Request<S>);
37    /// Typestate for for [`Config`] when configured via [`crate::RequestExt::with_agent`].
38    pub struct RequestExtScope<'a, S: AsSendBody>(pub(crate) WithAgent<'a, S>);
39
40    impl private::ConfigScope for AgentScope {
41        fn config(&mut self) -> &mut Config {
42            &mut self.0
43        }
44    }
45
46    impl<Any> private::ConfigScope for RequestScope<Any> {
47        fn config(&mut self) -> &mut Config {
48            self.0.request_level_config()
49        }
50    }
51
52    impl<S: AsSendBody> private::ConfigScope for HttpCrateScope<S> {
53        fn config(&mut self) -> &mut Config {
54            // This unwrap is OK, because we should not construct an
55            // HttpCrateScope without first ensure it is there.
56            let req_level: &mut RequestLevelConfig = self.0.extensions_mut().get_mut().unwrap();
57            &mut req_level.0
58        }
59    }
60
61    impl<S: AsSendBody> private::ConfigScope for RequestExtScope<'_, S> {
62        fn config(&mut self) -> &mut Config {
63            self.0.request_level_config()
64        }
65    }
66}
67
68use crate::config::typestate::RequestExtScope;
69use crate::http::Response;
70use crate::request_ext::WithAgent;
71use typestate::AgentScope;
72use typestate::HttpCrateScope;
73use typestate::RequestScope;
74
75/// Config primarily for the [`Agent`], but also per-request.
76///
77/// Config objects are cheap to clone and should not incur any heap allocations.
78///
79/// # Agent level config
80///
81/// ## Example
82///
83/// ```
84/// use ureq::Agent;
85/// use std::time::Duration;
86///
87/// let config = Agent::config_builder()
88///     .timeout_global(Some(Duration::from_secs(10)))
89///     .https_only(true)
90///     .build();
91///
92/// let agent = Agent::new_with_config(config);
93/// ```
94///
95///
96/// # Request level config
97///
98/// The config can also be change per-request. Since every request ultimately executes
99/// using an [`Agent`] (also the root-level `ureq::get(...)` have an implicit agent),
100/// a request level config clones the agent level config.
101///
102/// There are two ways of getting a request level config.
103///
104/// ## Request builder example
105///
106/// The first way is via [`RequestBuilder::config()`][crate::RequestBuilder::config].
107///
108/// ```
109/// use ureq::Agent;
110///
111/// let agent: Agent = Agent::config_builder()
112///     .https_only(false)
113///     .build()
114///     .into();
115///
116/// let response = agent.get("http://httpbin.org/get")
117///     .config()
118///     // override agent level setting for this request
119///     .https_only(true)
120///     .build()
121///     .call();
122/// ```
123///
124/// ## HTTP request example
125///
126/// The second way is via [`Agent::configure_request()`][crate::Agent::configure_request].
127/// This is used when working with the http crate [`http::Request`] type directly.
128///
129/// ```
130/// use ureq::{http, Agent};
131///
132/// let agent: Agent = Agent::config_builder()
133///     .https_only(false)
134///     .build()
135///     .into();
136///
137/// let request = http::Request::get("http://httpbin.org/get")
138///     .body(()).unwrap();
139///
140/// let request = agent.configure_request(request)
141///     // override agent level setting for this request
142///     .https_only(true)
143///     .build();
144///
145/// let response = agent.run(request);
146/// ```
147///
148#[derive(Clone)]
149pub struct Config {
150    http_status_as_error: bool,
151    https_only: bool,
152    ip_family: IpFamily,
153    #[cfg(feature = "_tls")]
154    tls_config: TlsConfig,
155    proxy: Option<Proxy>,
156    no_delay: bool,
157    max_redirects: u32,
158    max_redirects_will_error: bool,
159    redirect_auth_headers: RedirectAuthHeaders,
160    save_redirect_history: bool,
161    user_agent: AutoHeaderValue,
162    accept: AutoHeaderValue,
163    accept_encoding: AutoHeaderValue,
164    timeouts: Timeouts,
165    max_response_header_size: usize,
166    input_buffer_size: usize,
167    output_buffer_size: usize,
168    max_idle_connections: usize,
169    max_idle_connections_per_host: usize,
170    max_idle_age: Duration,
171    allow_non_standard_methods: bool,
172
173    // Chain built for middleware.
174    pub(crate) middleware: MiddlewareChain,
175}
176
177impl Config {
178    /// A builder to make a bespoke configuration.
179    ///
180    /// The default values are already set.
181    pub fn builder() -> ConfigBuilder<AgentScope> {
182        ConfigBuilder(AgentScope(Config::default()))
183    }
184
185    /// Creates a new agent by cloning this config.
186    ///
187    /// Cloning the config does not incur heap allocations.
188    pub fn new_agent(&self) -> Agent {
189        self.clone().into()
190    }
191
192    pub(crate) fn connect_proxy_uri(&self) -> Option<&Uri> {
193        let proxy = self.proxy.as_ref()?;
194
195        if !proxy.protocol().is_connect() {
196            return None;
197        }
198
199        Some(proxy.uri())
200    }
201
202    pub(crate) fn max_redirects_do_error(&self) -> bool {
203        self.max_redirects > 0 && self.max_redirects_will_error
204    }
205
206    pub(crate) fn clone_without_proxy(&self) -> Self {
207        let mut c = self.clone();
208        c.proxy = None;
209        c
210    }
211}
212
213impl Config {
214    /// Whether to treat 4xx and 5xx HTTP status codes as
215    /// [`Err(Error::StatusCode))`](crate::Error::StatusCode).
216    ///
217    /// Defaults to `true`.
218    pub fn http_status_as_error(&self) -> bool {
219        self.http_status_as_error
220    }
221
222    /// Whether to limit requests (including redirects) to https only
223    ///
224    /// Defaults to `false`.
225    pub fn https_only(&self) -> bool {
226        self.https_only
227    }
228
229    /// Configuration of IPv4/IPv6.
230    ///
231    /// This affects the resolver.
232    ///
233    /// Defaults to `IpFamily::Any`.
234    pub fn ip_family(&self) -> IpFamily {
235        self.ip_family
236    }
237
238    /// Config for TLS.
239    ///
240    /// This config is generic for all TLS connectors.
241    #[cfg(feature = "_tls")]
242    pub fn tls_config(&self) -> &TlsConfig {
243        &self.tls_config
244    }
245
246    /// Proxy configuration.
247    ///
248    /// Picked up from environment when using [`Config::default()`] or
249    pub fn proxy(&self) -> Option<&Proxy> {
250        self.proxy.as_ref()
251    }
252
253    /// Disable Nagle's algorithm
254    ///
255    /// Set TCP_NODELAY. It's up to the transport whether this flag is honored.
256    ///
257    /// Defaults to `true`.
258    pub fn no_delay(&self) -> bool {
259        self.no_delay
260    }
261
262    /// The max number of redirects to follow before giving up.
263    ///
264    /// Whe max redirects are reached, the behavior is controlled by the
265    /// `max_redirects_will_error` setting. Set to `true` (which
266    /// is the default) the result is a `TooManyRedirects` error. Set
267    /// to `false`, the response is returned as is.
268    ///
269    /// If `max_redirects` is 0, no redirects are followed and the response
270    /// is always returned (never a `TooManyRedirects` error).
271    ///
272    /// Defaults to 10
273    pub fn max_redirects(&self) -> u32 {
274        self.max_redirects
275    }
276
277    /// If we should error when max redirects are reached.
278    ///
279    /// This has no meaning if `max_redirects` is 0.
280    ///
281    /// Defaults to true
282    pub fn max_redirects_will_error(&self) -> bool {
283        self.max_redirects_will_error
284    }
285
286    /// How to handle `Authorization` headers when following redirects
287    ///
288    /// * `Never` (the default) means the authorization header is never attached to a redirected call.
289    /// * `SameHost` will keep the header when the redirect is to the same host and under https.
290    ///
291    /// Defaults to `None`.
292    pub fn redirect_auth_headers(&self) -> RedirectAuthHeaders {
293        self.redirect_auth_headers
294    }
295
296    /// If we should record a history of every redirect location,
297    /// including the request and final locations.
298    ///
299    /// Comes at the cost of allocating/retaining the `Uri` for
300    /// every redirect loop.
301    ///
302    /// See [`ResponseExt::get_redirect_history()`][crate::ResponseExt::get_redirect_history].
303    ///
304    /// Defaults to `false`.
305    pub fn save_redirect_history(&self) -> bool {
306        self.save_redirect_history
307    }
308
309    /// Value to use for the `User-Agent` header.
310    ///
311    /// This can be overridden by setting a `user-agent` header on the request
312    /// object. The one difference is that a connection to a HTTP proxy server
313    /// will receive this value, not the request-level one.
314    ///
315    /// Setting a value of `""` on the request or agent level will also not send a header.
316    ///
317    /// Defaults to `Default`, which results in `ureq/<version>`
318    pub fn user_agent(&self) -> &AutoHeaderValue {
319        &self.user_agent
320    }
321
322    /// Value to use for the `Accept` header.
323    ///
324    /// This agent configured value can be overriden per request by setting the header.
325    //
326    /// Setting a value of `""` on the request or agent level will also not send a header.
327    ///
328    /// Defaults to `Default`, which results in `*/*`
329    pub fn accept(&self) -> &AutoHeaderValue {
330        &self.accept
331    }
332
333    /// Value to use for the `Accept-Encoding` header.
334    ///
335    /// Defaults to `Default`, which will add `gz` and `brotli` depending on
336    /// the feature flags **gzip** and **brotli** respectively. If neither
337    /// feature is enabled, the header is not added.
338    ///
339    /// This agent configured value can be overriden per request by setting the header.
340    ///
341    /// Setting a value of `""` on the request or agent level will also not send a header.
342    ///
343    /// This communicates capability to the server, however the triggering the
344    /// automatic decompression behavior is not affected since that only looks
345    /// at the `Content-Encoding` response header.
346    pub fn accept_encoding(&self) -> &AutoHeaderValue {
347        &self.accept_encoding
348    }
349
350    /// All configured timeouts.
351    pub fn timeouts(&self) -> Timeouts {
352        self.timeouts
353    }
354
355    /// Max size of the HTTP response header.
356    ///
357    /// From the status, including all headers up until the body.
358    ///
359    /// Defaults to 64kb.
360    pub fn max_response_header_size(&self) -> usize {
361        self.max_response_header_size
362    }
363
364    /// Default size of the input buffer
365    ///
366    /// The default connectors use this setting.
367    ///
368    /// Defaults to 128kb.
369    pub fn input_buffer_size(&self) -> usize {
370        self.input_buffer_size
371    }
372
373    /// Default size of the output buffer.
374    ///
375    /// The default connectors use this setting.
376    ///
377    /// Defaults to 128kb.
378    pub fn output_buffer_size(&self) -> usize {
379        self.output_buffer_size
380    }
381
382    /// Max number of idle pooled connections overall.
383    ///
384    /// This setting has no effect when used per-request.
385    ///
386    /// Defaults to 10
387    pub fn max_idle_connections(&self) -> usize {
388        self.max_idle_connections
389    }
390
391    /// Max number of idle pooled connections per host/port combo.
392    ///
393    /// This setting has no effect when used per-request.
394    ///
395    /// Defaults to 3
396    pub fn max_idle_connections_per_host(&self) -> usize {
397        self.max_idle_connections_per_host
398    }
399
400    /// Max duration to keep an idle connection in the pool
401    ///
402    /// This can also be configured per-request to be shorter than the pool.
403    /// For example: if the pool is configured to 15 seconds and we have a
404    /// connection with an age of 10 seconds, a request setting this config
405    /// property to 3 seconds, would ignore the pooled connection (but still
406    /// leave it in the pool).
407    ///
408    /// Defaults to 15 seconds
409    pub fn max_idle_age(&self) -> Duration {
410        self.max_idle_age
411    }
412
413    /// Whether to allow non-standard HTTP methods.
414    ///
415    /// By default the methods are limited by the HTTP version.
416    ///
417    /// Defaults to false
418    pub fn allow_non_standard_methods(&self) -> bool {
419        self.allow_non_standard_methods
420    }
421}
422
423/// Builder of [`Config`]
424pub struct ConfigBuilder<Scope: private::ConfigScope>(pub(crate) Scope);
425
426impl<Scope: private::ConfigScope> ConfigBuilder<Scope> {
427    fn config(&mut self) -> &mut Config {
428        self.0.config()
429    }
430
431    /// Whether to treat 4xx and 5xx HTTP status codes as
432    /// [`Err(Error::StatusCode))`](crate::Error::StatusCode).
433    ///
434    /// Defaults to `true`.
435    pub fn http_status_as_error(mut self, v: bool) -> Self {
436        self.config().http_status_as_error = v;
437        self
438    }
439
440    /// Whether to limit requests (including redirects) to https only
441    ///
442    /// Defaults to `false`.
443    pub fn https_only(mut self, v: bool) -> Self {
444        self.config().https_only = v;
445        self
446    }
447
448    /// Configuration of IPv4/IPv6.
449    ///
450    /// This affects the resolver.
451    ///
452    /// Defaults to `IpFamily::Any`.
453    pub fn ip_family(mut self, v: IpFamily) -> Self {
454        self.config().ip_family = v;
455        self
456    }
457
458    /// Config for TLS.
459    ///
460    /// This config is generic for all TLS connectors.
461    #[cfg(feature = "_tls")]
462    pub fn tls_config(mut self, v: TlsConfig) -> Self {
463        self.config().tls_config = v;
464        self
465    }
466
467    /// Proxy configuration.
468    ///
469    /// Picked up from environment when using [`Config::default()`] or
470    /// [`Agent::new_with_defaults()`][crate::Agent::new_with_defaults].
471    pub fn proxy(mut self, v: Option<Proxy>) -> Self {
472        self.config().proxy = v;
473        self
474    }
475
476    /// Disable Nagle's algorithm
477    ///
478    /// Set TCP_NODELAY. It's up to the transport whether this flag is honored.
479    ///
480    /// Defaults to `true`.
481    pub fn no_delay(mut self, v: bool) -> Self {
482        self.config().no_delay = v;
483        self
484    }
485
486    /// The max number of redirects to follow before giving up.
487    ///
488    /// Whe max redirects are reached, the behavior is controlled by the
489    /// `max_redirects_will_error` setting. Set to `true` (which
490    /// is the default) the result is a `TooManyRedirects` error. Set
491    /// to `false`, the response is returned as is.
492    ///
493    /// If `max_redirects` is 0, no redirects are followed and the response
494    /// is always returned (never a `TooManyRedirects` error).
495    ///
496    /// Defaults to 10
497    pub fn max_redirects(mut self, v: u32) -> Self {
498        self.config().max_redirects = v;
499        self
500    }
501
502    /// If we should error when max redirects are reached.
503    ///
504    /// This has no meaning if `max_redirects` is 0.
505    ///
506    /// Defaults to true
507    pub fn max_redirects_will_error(mut self, v: bool) -> Self {
508        self.config().max_redirects_will_error = v;
509        self
510    }
511
512    /// How to handle `Authorization` headers when following redirects
513    ///
514    /// * `Never` (the default) means the authorization header is never attached to a redirected call.
515    /// * `SameHost` will keep the header when the redirect is to the same host and under https.
516    ///
517    /// Defaults to `None`.
518    pub fn redirect_auth_headers(mut self, v: RedirectAuthHeaders) -> Self {
519        self.config().redirect_auth_headers = v;
520        self
521    }
522
523    /// If we should record a history of every redirect location,
524    /// including the request and final locations.
525    ///
526    /// Comes at the cost of allocating/retaining the `Uri` for
527    /// every redirect loop.
528    ///
529    /// See [`ResponseExt::get_redirect_history()`][crate::ResponseExt::get_redirect_history].
530    ///
531    /// Defaults to `false`.
532    pub fn save_redirect_history(mut self, v: bool) -> Self {
533        self.config().save_redirect_history = v;
534        self
535    }
536
537    /// Value to use for the `User-Agent` header.
538    ///
539    /// This can be overridden by setting a `user-agent` header on the request
540    /// object. The one difference is that a connection to a HTTP proxy server
541    /// will receive this value, not the request-level one.
542    ///
543    /// Setting a value of `""` on the request or agent level will also not send a header.
544    ///
545    /// Defaults to `Default`, which results in `ureq/<version>`
546    pub fn user_agent(mut self, v: impl Into<AutoHeaderValue>) -> Self {
547        self.config().user_agent = v.into();
548        self
549    }
550
551    /// Value to use for the `Accept` header.
552    ///
553    /// This agent configured value can be overriden per request by setting the header.
554    //
555    /// Setting a value of `""` on the request or agent level will also not send a header.
556    ///
557    /// Defaults to `Default`, which results in `*/*`
558    pub fn accept(mut self, v: impl Into<AutoHeaderValue>) -> Self {
559        self.config().accept = v.into();
560        self
561    }
562
563    /// Value to use for the `Accept-Encoding` header.
564    ///
565    /// Defaults to `Default`, which will add `gz` and `brotli` depending on
566    /// the feature flags **gzip** and **brotli** respectively. If neither
567    /// feature is enabled, the header is not added.
568    ///
569    /// This agent configured value can be overriden per request by setting the header.
570    ///
571    /// Setting a value of `""` on the request or agent level will also not send a header.
572    ///
573    /// This communicates capability to the server, however the triggering the
574    /// automatic decompression behavior is not affected since that only looks
575    /// at the `Content-Encoding` response header.
576    pub fn accept_encoding(mut self, v: impl Into<AutoHeaderValue>) -> Self {
577        self.config().accept_encoding = v.into();
578        self
579    }
580
581    /// Max size of the HTTP response header.
582    ///
583    /// From the status, including all headers up until the body.
584    ///
585    /// Defaults to 64kb.
586    pub fn max_response_header_size(mut self, v: usize) -> Self {
587        self.config().max_response_header_size = v;
588        self
589    }
590
591    /// Default size of the input buffer
592    ///
593    /// The default connectors use this setting.
594    ///
595    /// Defaults to 128kb.
596    pub fn input_buffer_size(mut self, v: usize) -> Self {
597        self.config().input_buffer_size = v;
598        self
599    }
600
601    /// Default size of the output buffer.
602    ///
603    /// The default connectors use this setting.
604    ///
605    /// Defaults to 128kb.
606    pub fn output_buffer_size(mut self, v: usize) -> Self {
607        self.config().output_buffer_size = v;
608        self
609    }
610
611    /// Max number of idle pooled connections overall.
612    ///
613    /// This setting has no effect when used per-request.
614    ///
615    /// Defaults to 10
616    pub fn max_idle_connections(mut self, v: usize) -> Self {
617        self.config().max_idle_connections = v;
618        self
619    }
620
621    /// Max number of idle pooled connections per host/port combo.
622    ///
623    /// This setting has no effect when used per-request.
624    ///
625    /// Defaults to 3
626    pub fn max_idle_connections_per_host(mut self, v: usize) -> Self {
627        self.config().max_idle_connections_per_host = v;
628        self
629    }
630
631    /// Max duration to keep an idle connection in the pool
632    ///
633    /// This can also be configured per-request to be shorter than the pool.
634    /// For example: if the pool is configured to 15 seconds and we have a
635    /// connection with an age of 10 seconds, a request setting this config
636    /// property to 3 seconds, would ignore the pooled connection (but still
637    /// leave it in the pool).
638    ///
639    /// Defaults to 15 seconds
640    pub fn max_idle_age(mut self, v: Duration) -> Self {
641        self.config().max_idle_age = v;
642        self
643    }
644
645    /// Whether to allow non-standard HTTP methods.
646    ///
647    /// By default the methods are limited by the HTTP version.
648    ///
649    /// Defaults to false
650    pub fn allow_non_standard_methods(mut self, v: bool) -> Self {
651        self.config().allow_non_standard_methods = v;
652        self
653    }
654
655    /// Add middleware to use for each request in this agent.
656    ///
657    /// Defaults to no middleware.
658    pub fn middleware(mut self, v: impl Middleware) -> Self {
659        self.config().middleware.add(v);
660        self
661    }
662
663    /// Timeout for the entire call
664    ///
665    /// This is end-to-end, from DNS lookup to finishing reading the response body.
666    /// Thus it covers all other timeouts.
667    ///
668    /// Defaults to `None`.
669    pub fn timeout_global(mut self, v: Option<Duration>) -> Self {
670        self.config().timeouts.global = v;
671        self
672    }
673
674    /// Timeout for call-by-call when following redirects
675    ///
676    /// This covers a single call and the timeout is reset when
677    /// ureq follows a redirections.
678    ///
679    /// Defaults to `None`..
680    pub fn timeout_per_call(mut self, v: Option<Duration>) -> Self {
681        self.config().timeouts.per_call = v;
682        self
683    }
684
685    /// Max duration for doing the DNS lookup when establishing the connection
686    ///
687    /// Because most platforms do not have an async syscall for looking up
688    /// a host name, setting this might force str0m to spawn a thread to handle
689    /// the timeout.
690    ///
691    /// Defaults to `None`.
692    pub fn timeout_resolve(mut self, v: Option<Duration>) -> Self {
693        self.config().timeouts.resolve = v;
694        self
695    }
696
697    /// Max duration for establishing the connection
698    ///
699    /// For a TLS connection this includes opening the socket and doing the TLS handshake.
700    ///
701    /// Defaults to `None`.
702    pub fn timeout_connect(mut self, v: Option<Duration>) -> Self {
703        self.config().timeouts.connect = v;
704        self
705    }
706
707    /// Max duration for sending the request, but not the request body.
708    ///
709    /// Defaults to `None`.
710    pub fn timeout_send_request(mut self, v: Option<Duration>) -> Self {
711        self.config().timeouts.send_request = v;
712        self
713    }
714
715    /// Max duration for awaiting a 100-continue response.
716    ///
717    /// Only used if there is a request body and we sent the `Expect: 100-continue`
718    /// header to indicate we want the server to respond with 100.
719    ///
720    /// This defaults to 1 second.
721    pub fn timeout_await_100(mut self, v: Option<Duration>) -> Self {
722        self.config().timeouts.await_100 = v;
723        self
724    }
725
726    /// Max duration for sending a request body (if there is one)
727    ///
728    /// Defaults to `None`.
729    pub fn timeout_send_body(mut self, v: Option<Duration>) -> Self {
730        self.config().timeouts.send_body = v;
731        self
732    }
733
734    /// Max duration for receiving the response headers, but not the body
735    ///
736    /// Defaults to `None`.
737    pub fn timeout_recv_response(mut self, v: Option<Duration>) -> Self {
738        self.config().timeouts.recv_response = v;
739        self
740    }
741
742    /// Max duration for receving the response body.
743    ///
744    /// Defaults to `None`.
745    pub fn timeout_recv_body(mut self, v: Option<Duration>) -> Self {
746        self.config().timeouts.recv_body = v;
747        self
748    }
749}
750
751/// Possible config values for headers.
752///
753/// * `None` no automatic header
754/// * `Default` default behavior. I.e. for user-agent something like `ureq/3.1.2`
755/// * `Provided` is a user provided header
756#[derive(Debug, Clone)]
757pub enum AutoHeaderValue {
758    /// No automatic header.
759    None,
760
761    /// Default behavior.
762    ///
763    /// I.e. for user-agent something like `ureq/3.1.2`.
764    Default,
765
766    /// User provided header value.
767    Provided(Arc<String>),
768}
769
770impl Default for AutoHeaderValue {
771    fn default() -> Self {
772        Self::Default
773    }
774}
775
776impl AutoHeaderValue {
777    pub(crate) fn as_str(&self, default: &'static str) -> Option<&str> {
778        let x = match self {
779            AutoHeaderValue::None => "",
780            AutoHeaderValue::Default => default,
781            AutoHeaderValue::Provided(v) => v.as_str(),
782        };
783
784        if x.is_empty() {
785            None
786        } else {
787            Some(x)
788        }
789    }
790}
791
792impl<S: AsRef<str>> From<S> for AutoHeaderValue {
793    fn from(value: S) -> Self {
794        match value.as_ref() {
795            "" => Self::None,
796            _ => Self::Provided(Arc::new(value.as_ref().to_owned())),
797        }
798    }
799}
800
801impl ConfigBuilder<AgentScope> {
802    /// Finalize the config
803    pub fn build(self) -> Config {
804        self.0 .0
805    }
806}
807
808impl<Any> ConfigBuilder<RequestScope<Any>> {
809    /// Finalize the config
810    pub fn build(self) -> RequestBuilder<Any> {
811        self.0 .0
812    }
813}
814
815impl<S: AsSendBody> ConfigBuilder<HttpCrateScope<S>> {
816    /// Finalize the config
817    pub fn build(self) -> http::Request<S> {
818        self.0 .0
819    }
820}
821
822impl<'a, S: AsSendBody> ConfigBuilder<RequestExtScope<'a, S>> {
823    /// Finalize the config
824    pub fn build(self) -> WithAgent<'a, S> {
825        self.0 .0
826    }
827
828    /// Run the request with the agent in the ConfigBuilder
829    pub fn run(self) -> Result<Response<Body>, Error> {
830        self.0 .0.run()
831    }
832}
833
834/// Request timeout configuration.
835///
836/// This can be configured both on Agent level as well as per request.
837#[derive(Clone, Copy)]
838pub struct Timeouts {
839    /// Timeout for the entire call
840    pub global: Option<Duration>,
841
842    /// Timeout for call-by-call when following redirects
843    pub per_call: Option<Duration>,
844
845    /// Max duration for doing the DNS lookup when establishing the connection
846    pub resolve: Option<Duration>,
847
848    /// Max duration for establishing the connection
849    pub connect: Option<Duration>,
850
851    /// Max duration for sending the request, but not the request body.
852    pub send_request: Option<Duration>,
853
854    /// Max duration for awaiting a 100-continue response.
855    pub await_100: Option<Duration>,
856
857    /// Max duration for sending a request body (if there is one)
858    pub send_body: Option<Duration>,
859
860    /// Max duration for receiving the response headers, but not the body
861    pub recv_response: Option<Duration>,
862
863    /// Max duration for receving the response body.
864    pub recv_body: Option<Duration>,
865}
866
867#[derive(Debug, Clone)]
868pub(crate) struct RequestLevelConfig(pub Config);
869
870pub(crate) static DEFAULT_USER_AGENT: &str =
871    concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
872
873impl Default for Config {
874    fn default() -> Self {
875        Self {
876            http_status_as_error: true,
877            https_only: false,
878            ip_family: IpFamily::Any,
879            #[cfg(feature = "_tls")]
880            tls_config: TlsConfig::default(),
881            proxy: Proxy::try_from_env(),
882            no_delay: true,
883            max_redirects: 10,
884            max_redirects_will_error: true,
885            redirect_auth_headers: RedirectAuthHeaders::Never,
886            save_redirect_history: false,
887            user_agent: AutoHeaderValue::default(),
888            accept: AutoHeaderValue::default(),
889            accept_encoding: AutoHeaderValue::default(),
890            timeouts: Timeouts::default(),
891            max_response_header_size: 64 * 1024,
892            input_buffer_size: 128 * 1024,
893            output_buffer_size: 128 * 1024,
894            max_idle_connections: 10,
895            max_idle_connections_per_host: 3,
896            max_idle_age: Duration::from_secs(15),
897            allow_non_standard_methods: false,
898            middleware: MiddlewareChain::default(),
899        }
900    }
901}
902
903impl Default for Timeouts {
904    fn default() -> Self {
905        Self {
906            global: None,
907            per_call: None,
908            resolve: None,
909            connect: None,
910            send_request: None,
911            await_100: Some(Duration::from_secs(1)),
912            send_body: None,
913            recv_response: None,
914            recv_body: None,
915        }
916    }
917}
918
919/// Configuration of IP family to use.
920///
921/// Used to limit the IP to either IPv4, IPv6 or any.
922// TODO(martin): make this configurable
923#[derive(Debug, Clone, Copy, PartialEq, Eq)]
924pub enum IpFamily {
925    /// Both Ipv4 and Ipv6
926    Any,
927    /// Just Ipv4
928    Ipv4Only,
929    /// Just Ipv6
930    Ipv6Only,
931}
932
933impl IpFamily {
934    /// Filter the socket addresses to the family of IP.
935    pub fn keep_wanted<'a>(
936        &'a self,
937        iter: impl Iterator<Item = SocketAddr> + 'a,
938    ) -> impl Iterator<Item = SocketAddr> + 'a {
939        iter.filter(move |a| self.is_wanted(a))
940    }
941
942    fn is_wanted(&self, addr: &SocketAddr) -> bool {
943        match self {
944            IpFamily::Any => true,
945            IpFamily::Ipv4Only => addr.is_ipv4(),
946            IpFamily::Ipv6Only => addr.is_ipv6(),
947        }
948    }
949}
950
951impl fmt::Debug for Config {
952    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
953        let mut dbg = f.debug_struct("Config");
954
955        dbg.field("http_status_as_error", &self.http_status_as_error)
956            .field("https_only", &self.https_only)
957            .field("ip_family", &self.ip_family)
958            .field("proxy", &self.proxy)
959            .field("no_delay", &self.no_delay)
960            .field("max_redirects", &self.max_redirects)
961            .field("redirect_auth_headers", &self.redirect_auth_headers)
962            .field("save_redirect_history", &self.save_redirect_history)
963            .field("user_agent", &self.user_agent)
964            .field("timeouts", &self.timeouts)
965            .field("max_response_header_size", &self.max_response_header_size)
966            .field("input_buffer_size", &self.input_buffer_size)
967            .field("output_buffer_size", &self.output_buffer_size)
968            .field("max_idle_connections", &self.max_idle_connections)
969            .field(
970                "max_idle_connections_per_host",
971                &self.max_idle_connections_per_host,
972            )
973            .field("max_idle_age", &self.max_idle_age)
974            .field("middleware", &self.middleware);
975
976        #[cfg(feature = "_tls")]
977        {
978            dbg.field("tls_config", &self.tls_config);
979        }
980
981        dbg.finish()
982    }
983}
984
985impl fmt::Debug for Timeouts {
986    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
987        f.debug_struct("Timeouts")
988            .field("global", &self.global)
989            .field("per_call", &self.per_call)
990            .field("resolve", &self.resolve)
991            .field("connect", &self.connect)
992            .field("send_request", &self.send_request)
993            .field("await_100", &self.await_100)
994            .field("send_body", &self.send_body)
995            .field("recv_response", &self.recv_response)
996            .field("recv_body", &self.recv_body)
997            .finish()
998    }
999}
1000
1001#[cfg(test)]
1002mod test {
1003    use super::*;
1004    use assert_no_alloc::*;
1005
1006    #[test]
1007    fn default_config_clone_does_not_allocate() {
1008        let c = Config::default();
1009        assert_no_alloc(|| c.clone());
1010    }
1011}