1use std::fmt;
2use std::ops::Deref;
3use std::sync::Arc;
4use std::time::Duration;
5use url::Url;
6
7use crate::middleware::Middleware;
8use crate::pool::ConnectionPool;
9use crate::proxy::Proxy;
10use crate::request::Request;
11use crate::resolve::{ArcResolver, StdResolver};
12use crate::stream::TlsConnector;
13
14#[cfg(feature = "cookies")]
15use {
16 crate::cookies::{CookieStoreGuard, CookieTin},
17 cookie_store::CookieStore,
18};
19
20/// Strategy for keeping `authorization` headers during redirects.
21///
22/// `Never` is the default strategy and never preserves `authorization` header in redirects.
23/// `SameHost` send the authorization header in redirects only if the host of the redirect is
24/// the same of the previous request, and both use the same scheme (or switch to a more secure one, i.e
25/// we can redirect from `http` to `https`, but not the reverse).
26#[derive(Debug, Clone, PartialEq, Eq)]
27#[non_exhaustive]
28pub enum RedirectAuthHeaders {
29 /// Never preserve the `authorization` header on redirect. This is the default.
30 Never,
31 /// Preserve the `authorization` header when the redirect is to the same host. Both hosts must use
32 /// the same scheme (or switch to a more secure one, i.e we can redirect from `http` to `https`,
33 /// but not the reverse).
34 SameHost,
35}
36
37/// Accumulates options towards building an [Agent].
38pub struct AgentBuilder {
39 config: AgentConfig,
40 try_proxy_from_env: bool,
41 max_idle_connections: usize,
42 max_idle_connections_per_host: usize,
43 /// Cookies saved between requests.
44 /// Invariant: All cookies must have a nonempty domain and path.
45 #[cfg(feature = "cookies")]
46 cookie_store: Option<CookieStore>,
47 resolver: ArcResolver,
48 middleware: Vec<Box<dyn Middleware>>,
49}
50
51#[derive(Clone)]
52pub(crate) struct TlsConfig(Arc<dyn TlsConnector>);
53
54impl fmt::Debug for TlsConfig {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.debug_struct(name:"TlsConfig").finish()
57 }
58}
59
60impl Deref for TlsConfig {
61 type Target = Arc<dyn TlsConnector>;
62
63 fn deref(&self) -> &Self::Target {
64 &self.0
65 }
66}
67
68/// Config as built by AgentBuilder and then static for the lifetime of the Agent.
69#[derive(Clone, Debug)]
70pub(crate) struct AgentConfig {
71 pub proxy: Option<Proxy>,
72 pub timeout_connect: Option<Duration>,
73 pub timeout_read: Option<Duration>,
74 pub timeout_write: Option<Duration>,
75 pub timeout: Option<Duration>,
76 pub https_only: bool,
77 pub no_delay: bool,
78 pub redirects: u32,
79 pub redirect_auth_headers: RedirectAuthHeaders,
80 pub user_agent: String,
81 pub tls_config: TlsConfig,
82}
83
84/// Agents keep state between requests.
85///
86/// By default, no state, such as cookies, is kept between requests.
87/// But by creating an agent as entry point for the request, we
88/// can keep a state.
89///
90/// ```
91/// # fn main() -> Result<(), ureq::Error> {
92/// # ureq::is_test(true);
93/// let mut agent = ureq::agent();
94///
95/// agent
96/// .post("http://example.com/post/login")
97/// .call()?;
98///
99/// let secret = agent
100/// .get("http://example.com/get/my-protected-page")
101/// .call()?
102/// .into_string()?;
103///
104/// println!("Secret is: {}", secret);
105/// # Ok(())
106/// # }
107/// ```
108///
109/// Agent uses an inner Arc, so cloning an Agent results in an instance
110/// that shares the same underlying connection pool and other state.
111#[derive(Debug, Clone)]
112pub struct Agent {
113 pub(crate) config: Arc<AgentConfig>,
114 /// Reused agent state for repeated requests from this agent.
115 pub(crate) state: Arc<AgentState>,
116}
117
118/// Container of the state
119///
120/// *Internal API*.
121pub(crate) struct AgentState {
122 /// Reused connections between requests.
123 pub(crate) pool: ConnectionPool,
124 /// Cookies saved between requests.
125 /// Invariant: All cookies must have a nonempty domain and path.
126 #[cfg(feature = "cookies")]
127 pub(crate) cookie_tin: CookieTin,
128 pub(crate) resolver: ArcResolver,
129 pub(crate) middleware: Vec<Box<dyn Middleware>>,
130}
131
132impl Agent {
133 /// Creates an Agent with default settings.
134 ///
135 /// Same as `AgentBuilder::new().build()`.
136 pub fn new() -> Self {
137 AgentBuilder::new().build()
138 }
139
140 /// Make a request with the HTTP verb as a parameter.
141 ///
142 /// This allows making requests with verbs that don't have a dedicated
143 /// method.
144 ///
145 /// If you've got an already-parsed [Url], try [request_url][Agent::request_url].
146 ///
147 /// ```
148 /// # fn main() -> Result<(), ureq::Error> {
149 /// # ureq::is_test(true);
150 /// use ureq::Response;
151 /// let agent = ureq::agent();
152 ///
153 /// let resp: Response = agent
154 /// .request("OPTIONS", "http://example.com/")
155 /// .call()?;
156 /// # Ok(())
157 /// # }
158 /// ```
159 pub fn request(&self, method: &str, path: &str) -> Request {
160 Request::new(self.clone(), method.into(), path.into())
161 }
162
163 /// Make a request using an already-parsed [Url].
164 ///
165 /// This is useful if you've got a parsed Url from some other source, or if
166 /// you want to parse the URL and then modify it before making the request.
167 /// If you'd just like to pass a String or a `&str`, try [request][Agent::request].
168 ///
169 /// ```
170 /// # fn main() -> Result<(), ureq::Error> {
171 /// # ureq::is_test(true);
172 /// use {url::Url, ureq::Response};
173 /// let agent = ureq::agent();
174 ///
175 /// let mut url: Url = "http://example.com/some-page".parse()?;
176 /// url.set_path("/get/robots.txt");
177 /// let resp: Response = agent
178 /// .request_url("GET", &url)
179 /// .call()?;
180 /// # Ok(())
181 /// # }
182 /// ```
183 pub fn request_url(&self, method: &str, url: &Url) -> Request {
184 Request::new(self.clone(), method.into(), url.to_string())
185 }
186
187 /// Make a GET request from this agent.
188 pub fn get(&self, path: &str) -> Request {
189 self.request("GET", path)
190 }
191
192 /// Make a HEAD request from this agent.
193 pub fn head(&self, path: &str) -> Request {
194 self.request("HEAD", path)
195 }
196
197 /// Make a PATCH request from this agent.
198 pub fn patch(&self, path: &str) -> Request {
199 self.request("PATCH", path)
200 }
201
202 /// Make a POST request from this agent.
203 pub fn post(&self, path: &str) -> Request {
204 self.request("POST", path)
205 }
206
207 /// Make a PUT request from this agent.
208 pub fn put(&self, path: &str) -> Request {
209 self.request("PUT", path)
210 }
211
212 /// Make a DELETE request from this agent.
213 pub fn delete(&self, path: &str) -> Request {
214 self.request("DELETE", path)
215 }
216
217 /// Read access to the cookie store.
218 ///
219 /// Used to persist the cookies to an external writer.
220 ///
221 /// ```no_run
222 /// use std::io::Write;
223 /// use std::fs::File;
224 ///
225 /// # fn main() -> Result<(), ureq::Error> {
226 /// # ureq::is_test(true);
227 /// let agent = ureq::agent();
228 ///
229 /// // Cookies set by www.google.com are stored in agent.
230 /// agent.get("https://www.google.com/").call()?;
231 ///
232 /// // Saves (persistent) cookies
233 /// let mut file = File::create("cookies.json")?;
234 /// agent.cookie_store().save_json(&mut file).unwrap();
235 /// # Ok(())
236 /// # }
237 /// ```
238 #[cfg(feature = "cookies")]
239 pub fn cookie_store(&self) -> CookieStoreGuard<'_> {
240 self.state.cookie_tin.read_lock()
241 }
242
243 pub(crate) fn weak_state(&self) -> std::sync::Weak<AgentState> {
244 Arc::downgrade(&self.state)
245 }
246}
247
248const DEFAULT_MAX_IDLE_CONNECTIONS: usize = 100;
249const DEFAULT_MAX_IDLE_CONNECTIONS_PER_HOST: usize = 1;
250
251impl AgentBuilder {
252 pub fn new() -> Self {
253 AgentBuilder {
254 config: AgentConfig {
255 proxy: None,
256 timeout_connect: Some(Duration::from_secs(30)),
257 timeout_read: None,
258 timeout_write: None,
259 timeout: None,
260 https_only: false,
261 no_delay: true,
262 redirects: 5,
263 redirect_auth_headers: RedirectAuthHeaders::Never,
264 user_agent: format!("ureq/{}", env!("CARGO_PKG_VERSION")),
265 tls_config: TlsConfig(crate::default_tls_config()),
266 },
267 #[cfg(feature = "proxy-from-env")]
268 try_proxy_from_env: true,
269 #[cfg(not(feature = "proxy-from-env"))]
270 try_proxy_from_env: false,
271 max_idle_connections: DEFAULT_MAX_IDLE_CONNECTIONS,
272 max_idle_connections_per_host: DEFAULT_MAX_IDLE_CONNECTIONS_PER_HOST,
273 resolver: StdResolver.into(),
274 #[cfg(feature = "cookies")]
275 cookie_store: None,
276 middleware: vec![],
277 }
278 }
279
280 /// Create a new agent.
281 // Note: This could take &self as the first argument, allowing one
282 // AgentBuilder to be used multiple times, except CookieStore does
283 // not implement clone, so we have to give ownership to the newly
284 // built Agent.
285 pub fn build(mut self) -> Agent {
286 if self.config.proxy.is_none() && self.try_proxy_from_env {
287 if let Some(proxy) = Proxy::try_from_system() {
288 self.config.proxy = Some(proxy);
289 }
290 }
291 Agent {
292 config: Arc::new(self.config),
293 state: Arc::new(AgentState {
294 pool: ConnectionPool::new_with_limits(
295 self.max_idle_connections,
296 self.max_idle_connections_per_host,
297 ),
298 #[cfg(feature = "cookies")]
299 cookie_tin: CookieTin::new(self.cookie_store.unwrap_or_else(CookieStore::default)),
300 resolver: self.resolver,
301 middleware: self.middleware,
302 }),
303 }
304 }
305
306 /// Set the proxy server to use for all connections from this Agent.
307 ///
308 /// Example:
309 /// ```
310 /// # fn main() -> Result<(), ureq::Error> {
311 /// # ureq::is_test(true);
312 /// let proxy = ureq::Proxy::new("user:password@cool.proxy:9090")?;
313 /// let agent = ureq::AgentBuilder::new()
314 /// .proxy(proxy)
315 /// .build();
316 /// # Ok(())
317 /// # }
318 /// ```
319 ///
320 /// Adding a proxy will disable `try_proxy_from_env`.
321 pub fn proxy(mut self, proxy: Proxy) -> Self {
322 self.config.proxy = Some(proxy);
323 self
324 }
325
326 /// Attempt to detect proxy settings from the environment, i.e. HTTP_PROXY
327 ///
328 /// The default is `false` without the `proxy-from-env` feature flag, i.e.
329 /// not detecting proxy from env, due to the potential security risk and to
330 /// maintain backward compatibility.
331 ///
332 /// If the `proxy` is set on the builder, this setting has no effect.
333 pub fn try_proxy_from_env(mut self, do_try: bool) -> Self {
334 self.try_proxy_from_env = do_try;
335 self
336 }
337
338 /// Enforce the client to only perform HTTPS requests.
339 /// This setting also makes the client refuse HTTPS to HTTP redirects.
340 /// Default is false
341 ///
342 /// Example:
343 /// ```
344 /// let agent = ureq::AgentBuilder::new()
345 /// .https_only(true)
346 /// .build();
347 /// ```
348 pub fn https_only(mut self, enforce: bool) -> Self {
349 self.config.https_only = enforce;
350 self
351 }
352
353 /// Sets the maximum number of connections allowed in the connection pool.
354 /// By default, this is set to 100. Setting this to zero would disable
355 /// connection pooling.
356 ///
357 /// ```
358 /// let agent = ureq::AgentBuilder::new()
359 /// .max_idle_connections(200)
360 /// .build();
361 /// ```
362 pub fn max_idle_connections(mut self, max: usize) -> Self {
363 self.max_idle_connections = max;
364 self
365 }
366
367 /// Sets the maximum number of connections per host to keep in the
368 /// connection pool. By default, this is set to 1. Setting this to zero
369 /// would disable connection pooling.
370 ///
371 /// ```
372 /// let agent = ureq::AgentBuilder::new()
373 /// .max_idle_connections_per_host(200)
374 /// .build();
375 /// ```
376 pub fn max_idle_connections_per_host(mut self, max: usize) -> Self {
377 self.max_idle_connections_per_host = max;
378 self
379 }
380
381 /// Configures a custom resolver to be used by this agent. By default,
382 /// address-resolution is done by std::net::ToSocketAddrs. This allows you
383 /// to override that resolution with your own alternative. Useful for
384 /// testing and special-cases like DNS-based load balancing.
385 ///
386 /// A `Fn(&str) -> io::Result<Vec<SocketAddr>>` is a valid resolver,
387 /// passing a closure is a simple way to override. Note that you might need
388 /// explicit type `&str` on the closure argument for type inference to
389 /// succeed.
390 /// ```
391 /// use std::net::ToSocketAddrs;
392 ///
393 /// let mut agent = ureq::AgentBuilder::new()
394 /// .resolver(|addr: &str| match addr {
395 /// "example.com" => Ok(vec![([127,0,0,1], 8096).into()]),
396 /// addr => addr.to_socket_addrs().map(Iterator::collect),
397 /// })
398 /// .build();
399 /// ```
400 pub fn resolver(mut self, resolver: impl crate::Resolver + 'static) -> Self {
401 self.resolver = resolver.into();
402 self
403 }
404
405 /// Timeout for the socket connection to be successful.
406 /// If both this and `.timeout()` are both set, `.timeout_connect()`
407 /// takes precedence.
408 ///
409 /// The default is 30 seconds.
410 ///
411 /// ```
412 /// use std::time::Duration;
413 /// # fn main() -> Result<(), ureq::Error> {
414 /// # ureq::is_test(true);
415 /// let agent = ureq::builder()
416 /// .timeout_connect(Duration::from_secs(1))
417 /// .build();
418 /// let result = agent.get("http://httpbin.org/delay/20").call();
419 /// # Ok(())
420 /// # }
421 /// ```
422 pub fn timeout_connect(mut self, timeout: Duration) -> Self {
423 self.config.timeout_connect = Some(timeout);
424 self
425 }
426
427 /// Timeout for the individual reads of the socket.
428 /// If both this and `.timeout()` are both set, `.timeout()`
429 /// takes precedence.
430 ///
431 /// The default is no timeout. In other words, requests may block forever on reads by default.
432 ///
433 /// ```
434 /// use std::time::Duration;
435 /// # fn main() -> Result<(), ureq::Error> {
436 /// # ureq::is_test(true);
437 /// let agent = ureq::builder()
438 /// .timeout_read(Duration::from_secs(1))
439 /// .build();
440 /// let result = agent.get("http://httpbin.org/delay/20").call();
441 /// # Ok(())
442 /// # }
443 /// ```
444 pub fn timeout_read(mut self, timeout: Duration) -> Self {
445 self.config.timeout_read = Some(timeout);
446 self
447 }
448
449 /// Timeout for the individual writes to the socket.
450 /// If both this and `.timeout()` are both set, `.timeout()`
451 /// takes precedence.
452 ///
453 /// The default is no timeout. In other words, requests may block forever on writes by default.
454 ///
455 /// ```
456 /// use std::time::Duration;
457 /// # fn main() -> Result<(), ureq::Error> {
458 /// # ureq::is_test(true);
459 /// let agent = ureq::builder()
460 /// .timeout_read(Duration::from_secs(1))
461 /// .build();
462 /// let result = agent.get("http://httpbin.org/delay/20").call();
463 /// # Ok(())
464 /// # }
465 /// ```
466 pub fn timeout_write(mut self, timeout: Duration) -> Self {
467 self.config.timeout_write = Some(timeout);
468 self
469 }
470
471 /// Timeout for the overall request, including DNS resolution, connection
472 /// time, redirects, and reading the response body. Slow DNS resolution
473 /// may cause a request to exceed the timeout, because the DNS request
474 /// cannot be interrupted with the available APIs.
475 ///
476 /// This takes precedence over `.timeout_read()` and `.timeout_write()`, but
477 /// not `.timeout_connect()`.
478 ///
479 /// ```
480 /// # fn main() -> Result<(), ureq::Error> {
481 /// # ureq::is_test(true);
482 /// // wait max 1 second for whole request to complete.
483 /// let agent = ureq::builder()
484 /// .timeout(std::time::Duration::from_secs(1))
485 /// .build();
486 /// let result = agent.get("http://httpbin.org/delay/20").call();
487 /// # Ok(())
488 /// # }
489 /// ```
490 pub fn timeout(mut self, timeout: Duration) -> Self {
491 self.config.timeout = Some(timeout);
492 self
493 }
494
495 /// Whether no_delay will be set on the tcp socket.
496 /// Setting this to true disables Nagle's algorithm.
497 ///
498 /// Defaults to true.
499 ///
500 /// ```
501 /// # fn main() -> Result<(), ureq::Error> {
502 /// # ureq::is_test(true);
503 /// let agent = ureq::builder()
504 /// .no_delay(false)
505 /// .build();
506 /// let result = agent.get("http://httpbin.org/get").call();
507 /// # Ok(())
508 /// # }
509 /// ```
510 pub fn no_delay(mut self, no_delay: bool) -> Self {
511 self.config.no_delay = no_delay;
512 self
513 }
514
515 /// How many redirects to follow.
516 ///
517 /// Defaults to `5`. Set to `0` to avoid redirects and instead
518 /// get a response object with the 3xx status code.
519 ///
520 /// If the redirect count hits this limit (and it's > 0), TooManyRedirects is returned.
521 ///
522 /// WARNING: for 307 and 308 redirects, this value is ignored for methods that have a body.
523 /// You must handle 307 redirects yourself when sending a PUT, POST, PATCH, or DELETE request.
524 ///
525 /// ```no_run
526 /// # fn main() -> Result<(), ureq::Error> {
527 /// # ureq::is_test(true);
528 /// let result = ureq::builder()
529 /// .redirects(1)
530 /// .build()
531 /// # ;
532 /// # let result = ureq::agent()
533 /// .get("http://httpbin.org/status/301")
534 /// .call()?;
535 /// assert_ne!(result.status(), 301);
536 ///
537 /// let result = ureq::post("http://httpbin.org/status/307")
538 /// .send_bytes(b"some data")?;
539 /// assert_eq!(result.status(), 307);
540 /// # Ok(())
541 /// # }
542 /// ```
543 pub fn redirects(mut self, n: u32) -> Self {
544 self.config.redirects = n;
545 self
546 }
547
548 /// Set the strategy for propagation of authorization headers in redirects.
549 ///
550 /// Defaults to [`RedirectAuthHeaders::Never`].
551 ///
552 pub fn redirect_auth_headers(mut self, v: RedirectAuthHeaders) -> Self {
553 self.config.redirect_auth_headers = v;
554 self
555 }
556
557 /// The user-agent header to associate with all requests from this agent by default.
558 ///
559 /// Defaults to `ureq/[VERSION]`. You can override the user-agent on an individual request by
560 /// setting the `User-Agent` header when constructing the request.
561 ///
562 /// ```no_run
563 /// # #[cfg(feature = "json")]
564 /// # fn main() -> Result<(), ureq::Error> {
565 /// # ureq::is_test(true);
566 /// let agent = ureq::builder()
567 /// .user_agent("ferris/1.0")
568 /// .build();
569 ///
570 /// // Uses agent's header
571 /// let result: serde_json::Value =
572 /// agent.get("http://httpbin.org/headers").call()?.into_json()?;
573 /// assert_eq!(&result["headers"]["User-Agent"], "ferris/1.0");
574 ///
575 /// // Overrides user-agent set on the agent
576 /// let result: serde_json::Value = agent.get("http://httpbin.org/headers")
577 /// .set("User-Agent", "super-ferris/2.0")
578 /// .call()?.into_json()?;
579 /// assert_eq!(&result["headers"]["User-Agent"], "super-ferris/2.0");
580 /// # Ok(())
581 /// # }
582 /// # #[cfg(not(feature = "json"))]
583 /// # fn main() {}
584 /// ```
585 pub fn user_agent(mut self, user_agent: &str) -> Self {
586 self.config.user_agent = user_agent.into();
587 self
588 }
589
590 /// Configure TLS options for rustls to use when making HTTPS connections from this Agent.
591 ///
592 /// This overrides any previous call to tls_config or tls_connector.
593 ///
594 /// ```
595 /// # fn main() -> Result<(), ureq::Error> {
596 /// # ureq::is_test(true);
597 /// use std::sync::Arc;
598 /// let mut root_store = rustls::RootCertStore {
599 /// roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(),
600 /// };
601 ///
602 /// let tls_config = rustls::ClientConfig::builder()
603 /// .with_root_certificates(root_store)
604 /// .with_no_client_auth();
605 /// let agent = ureq::builder()
606 /// .tls_config(Arc::new(tls_config))
607 /// .build();
608 /// # Ok(())
609 /// # }
610 #[cfg(feature = "tls")]
611 pub fn tls_config(mut self, tls_config: Arc<rustls::ClientConfig>) -> Self {
612 self.config.tls_config = TlsConfig(Arc::new(tls_config));
613 self
614 }
615
616 /// Configure TLS options for a backend other than rustls. The parameter can be a
617 /// any type which implements the [`TlsConnector`] trait. If you enable the native-tls
618 /// feature, we provide `impl TlsConnector for native_tls::TlsConnector` so you can pass
619 /// [`Arc<native_tls::TlsConnector>`](https://docs.rs/native-tls/0.2.7/native_tls/struct.TlsConnector.html).
620 ///
621 /// This overrides any previous call to tls_config or tls_connector.
622 ///
623 /// ```
624 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
625 /// # ureq::is_test(true);
626 /// use std::sync::Arc;
627 /// # #[cfg(feature = "native-tls")]
628 /// let tls_connector = Arc::new(native_tls::TlsConnector::new()?);
629 /// # #[cfg(feature = "native-tls")]
630 /// let agent = ureq::builder()
631 /// .tls_connector(tls_connector.clone())
632 /// .build();
633 /// # Ok(())
634 /// # }
635 /// ```
636 pub fn tls_connector<T: TlsConnector + 'static>(mut self, tls_config: Arc<T>) -> Self {
637 self.config.tls_config = TlsConfig(tls_config);
638 self
639 }
640
641 /// Provide the cookie store to be used for all requests using this agent.
642 ///
643 /// This is useful in two cases. First when there is a need to persist cookies
644 /// to some backing store, and second when there's a need to prepare the agent
645 /// with some pre-existing cookies.
646 ///
647 /// Example
648 /// ```no_run
649 /// # fn main() -> Result<(), ureq::Error> {
650 /// # ureq::is_test(true);
651 /// use cookie_store::CookieStore;
652 /// use std::fs::File;
653 /// use std::io::BufReader;
654 /// let file = File::open("cookies.json")?;
655 /// let read = BufReader::new(file);
656 ///
657 /// // Read persisted cookies from cookies.json
658 /// let my_store = CookieStore::load_json(read).unwrap();
659 ///
660 /// // Cookies will be used for requests done through agent.
661 /// let agent = ureq::builder()
662 /// .cookie_store(my_store)
663 /// .build();
664 /// # Ok(())
665 /// # }
666 /// ```
667 #[cfg(feature = "cookies")]
668 pub fn cookie_store(mut self, cookie_store: CookieStore) -> Self {
669 self.cookie_store = Some(cookie_store);
670 self
671 }
672
673 /// Add middleware handler to this agent.
674 ///
675 /// All requests made by the agent will use this middleware. Middleware is invoked
676 /// in the order they are added to the builder.
677 pub fn middleware(mut self, m: impl Middleware) -> Self {
678 self.middleware.push(Box::new(m));
679 self
680 }
681}
682
683#[cfg(feature = "tls")]
684#[derive(Clone)]
685pub(crate) struct TLSClientConfig(pub(crate) Arc<rustls::ClientConfig>);
686
687#[cfg(feature = "tls")]
688impl fmt::Debug for TLSClientConfig {
689 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690 f.debug_struct(name:"TLSClientConfig").finish()
691 }
692}
693
694impl fmt::Debug for AgentBuilder {
695 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
696 f&mut DebugStruct<'_, '_>.debug_struct("AgentBuilder")
697 .field("config", &self.config)
698 .field("max_idle_connections", &self.max_idle_connections)
699 .field(
700 "max_idle_connections_per_host",
701 &self.max_idle_connections_per_host,
702 )
703 .field(name:"resolver", &self.resolver)
704 // self.cookies missing because it's feature flagged.
705 // self.middleware missing because we don't want to force Debug on Middleware trait.
706 .finish_non_exhaustive()
707 }
708}
709
710impl fmt::Debug for AgentState {
711 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712 f&mut DebugStruct<'_, '_>.debug_struct("AgentState")
713 .field("pool", &self.pool)
714 .field(name:"resolver", &self.resolver)
715 // self.cookie_tin missing because it's feature flagged.
716 // self.middleware missing because we don't want to force Debug on Middleware trait.
717 .finish_non_exhaustive()
718 }
719}
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724
725 ///////////////////// AGENT TESTS //////////////////////////////
726
727 #[test]
728 fn agent_implements_send_and_sync() {
729 let _agent: Box<dyn Send> = Box::new(AgentBuilder::new().build());
730 let _agent: Box<dyn Sync> = Box::new(AgentBuilder::new().build());
731 }
732
733 #[test]
734 fn agent_config_debug() {
735 let agent = AgentBuilder::new().build();
736 let debug_format = format!("{:?}", agent);
737 assert!(debug_format.contains("Agent"));
738 assert!(debug_format.contains("config:"));
739 assert!(debug_format.contains("proxy:"));
740 assert!(debug_format.contains("timeout_connect:"));
741 assert!(debug_format.contains("timeout_read:"));
742 assert!(debug_format.contains("timeout_write:"));
743 assert!(debug_format.contains("timeout:"));
744 assert!(debug_format.contains("https_only:"));
745 assert!(debug_format.contains("no_delay:"));
746 assert!(debug_format.contains("redirects:"));
747 assert!(debug_format.contains("redirect_auth_headers:"));
748 assert!(debug_format.contains("user_agent:"));
749 assert!(debug_format.contains("tls_config:"));
750 assert!(debug_format.contains("state:"));
751 assert!(debug_format.contains("pool:"));
752 }
753}
754