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}