1 | #[cfg (any(feature = "native-tls" , feature = "__rustls" ,))] |
2 | use std::any::Any; |
3 | use std::convert::TryInto; |
4 | use std::fmt; |
5 | use std::future::Future; |
6 | use std::net::IpAddr; |
7 | use std::net::SocketAddr; |
8 | use std::sync::Arc; |
9 | use std::thread; |
10 | use std::time::Duration; |
11 | |
12 | use http::header::HeaderValue; |
13 | use log::{error, trace}; |
14 | use tokio::sync::{mpsc, oneshot}; |
15 | |
16 | use super::request::{Request, RequestBuilder}; |
17 | use super::response::Response; |
18 | use super::wait; |
19 | #[cfg (feature = "__tls" )] |
20 | use crate::tls; |
21 | #[cfg (feature = "__tls" )] |
22 | use crate::Certificate; |
23 | #[cfg (any(feature = "native-tls" , feature = "__rustls" ))] |
24 | use crate::Identity; |
25 | use crate::{async_impl, header, redirect, IntoUrl, Method, Proxy}; |
26 | |
27 | /// A `Client` to make Requests with. |
28 | /// |
29 | /// The Client has various configuration values to tweak, but the defaults |
30 | /// are set to what is usually the most commonly desired value. To configure a |
31 | /// `Client`, use `Client::builder()`. |
32 | /// |
33 | /// The `Client` holds a connection pool internally, so it is advised that |
34 | /// you create one and **reuse** it. |
35 | /// |
36 | /// # Examples |
37 | /// |
38 | /// ```rust |
39 | /// use reqwest::blocking::Client; |
40 | /// # |
41 | /// # fn run() -> Result<(), reqwest::Error> { |
42 | /// let client = Client::new(); |
43 | /// let resp = client.get("http://httpbin.org/" ).send()?; |
44 | /// # drop(resp); |
45 | /// # Ok(()) |
46 | /// # } |
47 | /// |
48 | /// ``` |
49 | #[derive (Clone)] |
50 | pub struct Client { |
51 | inner: ClientHandle, |
52 | } |
53 | |
54 | /// A `ClientBuilder` can be used to create a `Client` with custom configuration. |
55 | /// |
56 | /// # Example |
57 | /// |
58 | /// ``` |
59 | /// # fn run() -> Result<(), reqwest::Error> { |
60 | /// use std::time::Duration; |
61 | /// |
62 | /// let client = reqwest::blocking::Client::builder() |
63 | /// .timeout(Duration::from_secs(10)) |
64 | /// .build()?; |
65 | /// # Ok(()) |
66 | /// # } |
67 | /// ``` |
68 | #[must_use ] |
69 | pub struct ClientBuilder { |
70 | inner: async_impl::ClientBuilder, |
71 | timeout: Timeout, |
72 | } |
73 | |
74 | impl Default for ClientBuilder { |
75 | fn default() -> Self { |
76 | Self::new() |
77 | } |
78 | } |
79 | |
80 | impl ClientBuilder { |
81 | /// Constructs a new `ClientBuilder`. |
82 | /// |
83 | /// This is the same as `Client::builder()`. |
84 | pub fn new() -> ClientBuilder { |
85 | ClientBuilder { |
86 | inner: async_impl::ClientBuilder::new(), |
87 | timeout: Timeout::default(), |
88 | } |
89 | } |
90 | |
91 | /// Returns a `Client` that uses this `ClientBuilder` configuration. |
92 | /// |
93 | /// # Errors |
94 | /// |
95 | /// This method fails if TLS backend cannot be initialized, or the resolver |
96 | /// cannot load the system configuration. |
97 | /// |
98 | /// # Panics |
99 | /// |
100 | /// This method panics if called from within an async runtime. See docs on |
101 | /// [`reqwest::blocking`][crate::blocking] for details. |
102 | pub fn build(self) -> crate::Result<Client> { |
103 | ClientHandle::new(self).map(|handle| Client { inner: handle }) |
104 | } |
105 | |
106 | // Higher-level options |
107 | |
108 | /// Sets the `User-Agent` header to be used by this client. |
109 | /// |
110 | /// # Example |
111 | /// |
112 | /// ```rust |
113 | /// # fn doc() -> Result<(), reqwest::Error> { |
114 | /// // Name your user agent after your app? |
115 | /// static APP_USER_AGENT: &str = concat!( |
116 | /// env!("CARGO_PKG_NAME" ), |
117 | /// "/" , |
118 | /// env!("CARGO_PKG_VERSION" ), |
119 | /// ); |
120 | /// |
121 | /// let client = reqwest::blocking::Client::builder() |
122 | /// .user_agent(APP_USER_AGENT) |
123 | /// .build()?; |
124 | /// let res = client.get("https://www.rust-lang.org" ).send()?; |
125 | /// # Ok(()) |
126 | /// # } |
127 | /// ``` |
128 | pub fn user_agent<V>(self, value: V) -> ClientBuilder |
129 | where |
130 | V: TryInto<HeaderValue>, |
131 | V::Error: Into<http::Error>, |
132 | { |
133 | self.with_inner(move |inner| inner.user_agent(value)) |
134 | } |
135 | |
136 | /// Sets the default headers for every request. |
137 | /// |
138 | /// # Example |
139 | /// |
140 | /// ```rust |
141 | /// use reqwest::header; |
142 | /// # fn build_client() -> Result<(), reqwest::Error> { |
143 | /// let mut headers = header::HeaderMap::new(); |
144 | /// headers.insert("X-MY-HEADER" , header::HeaderValue::from_static("value" )); |
145 | /// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret" )); |
146 | /// |
147 | /// // Consider marking security-sensitive headers with `set_sensitive`. |
148 | /// let mut auth_value = header::HeaderValue::from_static("secret" ); |
149 | /// auth_value.set_sensitive(true); |
150 | /// headers.insert(header::AUTHORIZATION, auth_value); |
151 | /// |
152 | /// // get a client builder |
153 | /// let client = reqwest::blocking::Client::builder() |
154 | /// .default_headers(headers) |
155 | /// .build()?; |
156 | /// let res = client.get("https://www.rust-lang.org" ).send()?; |
157 | /// # Ok(()) |
158 | /// # } |
159 | /// ``` |
160 | /// |
161 | /// Override the default headers: |
162 | /// |
163 | /// ```rust |
164 | /// use reqwest::header; |
165 | /// # fn build_client() -> Result<(), reqwest::Error> { |
166 | /// let mut headers = header::HeaderMap::new(); |
167 | /// headers.insert("X-MY-HEADER" , header::HeaderValue::from_static("value" )); |
168 | /// |
169 | /// // get a client builder |
170 | /// let client = reqwest::blocking::Client::builder() |
171 | /// .default_headers(headers) |
172 | /// .build()?; |
173 | /// let res = client |
174 | /// .get("https://www.rust-lang.org" ) |
175 | /// .header("X-MY-HEADER" , "new_value" ) |
176 | /// .send()?; |
177 | /// # Ok(()) |
178 | /// # } |
179 | /// ``` |
180 | pub fn default_headers(self, headers: header::HeaderMap) -> ClientBuilder { |
181 | self.with_inner(move |inner| inner.default_headers(headers)) |
182 | } |
183 | |
184 | /// Enable a persistent cookie store for the client. |
185 | /// |
186 | /// Cookies received in responses will be preserved and included in |
187 | /// additional requests. |
188 | /// |
189 | /// By default, no cookie store is used. |
190 | /// |
191 | /// # Optional |
192 | /// |
193 | /// This requires the optional `cookies` feature to be enabled. |
194 | #[cfg (feature = "cookies" )] |
195 | #[cfg_attr (docsrs, doc(cfg(feature = "cookies" )))] |
196 | pub fn cookie_store(self, enable: bool) -> ClientBuilder { |
197 | self.with_inner(|inner| inner.cookie_store(enable)) |
198 | } |
199 | |
200 | /// Set the persistent cookie store for the client. |
201 | /// |
202 | /// Cookies received in responses will be passed to this store, and |
203 | /// additional requests will query this store for cookies. |
204 | /// |
205 | /// By default, no cookie store is used. |
206 | /// |
207 | /// # Optional |
208 | /// |
209 | /// This requires the optional `cookies` feature to be enabled. |
210 | #[cfg (feature = "cookies" )] |
211 | #[cfg_attr (docsrs, doc(cfg(feature = "cookies" )))] |
212 | pub fn cookie_provider<C: crate::cookie::CookieStore + 'static>( |
213 | self, |
214 | cookie_store: Arc<C>, |
215 | ) -> ClientBuilder { |
216 | self.with_inner(|inner| inner.cookie_provider(cookie_store)) |
217 | } |
218 | |
219 | /// Enable auto gzip decompression by checking the `Content-Encoding` response header. |
220 | /// |
221 | /// If auto gzip decompresson is turned on: |
222 | /// |
223 | /// - When sending a request and if the request's headers do not already contain |
224 | /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `gzip`. |
225 | /// The request body is **not** automatically compressed. |
226 | /// - When receiving a response, if it's headers contain a `Content-Encoding` value that |
227 | /// equals to `gzip`, both values `Content-Encoding` and `Content-Length` are removed from the |
228 | /// headers' set. The response body is automatically decompressed. |
229 | /// |
230 | /// If the `gzip` feature is turned on, the default option is enabled. |
231 | /// |
232 | /// # Optional |
233 | /// |
234 | /// This requires the optional `gzip` feature to be enabled |
235 | #[cfg (feature = "gzip" )] |
236 | #[cfg_attr (docsrs, doc(cfg(feature = "gzip" )))] |
237 | pub fn gzip(self, enable: bool) -> ClientBuilder { |
238 | self.with_inner(|inner| inner.gzip(enable)) |
239 | } |
240 | |
241 | /// Enable auto brotli decompression by checking the `Content-Encoding` response header. |
242 | /// |
243 | /// If auto brotli decompression is turned on: |
244 | /// |
245 | /// - When sending a request and if the request's headers do not already contain |
246 | /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `br`. |
247 | /// The request body is **not** automatically compressed. |
248 | /// - When receiving a response, if it's headers contain a `Content-Encoding` value that |
249 | /// equals to `br`, both values `Content-Encoding` and `Content-Length` are removed from the |
250 | /// headers' set. The response body is automatically decompressed. |
251 | /// |
252 | /// If the `brotli` feature is turned on, the default option is enabled. |
253 | /// |
254 | /// # Optional |
255 | /// |
256 | /// This requires the optional `brotli` feature to be enabled |
257 | #[cfg (feature = "brotli" )] |
258 | #[cfg_attr (docsrs, doc(cfg(feature = "brotli" )))] |
259 | pub fn brotli(self, enable: bool) -> ClientBuilder { |
260 | self.with_inner(|inner| inner.brotli(enable)) |
261 | } |
262 | |
263 | /// Enable auto deflate decompression by checking the `Content-Encoding` response header. |
264 | /// |
265 | /// If auto deflate decompresson is turned on: |
266 | /// |
267 | /// - When sending a request and if the request's headers do not already contain |
268 | /// an `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `deflate`. |
269 | /// The request body is **not** automatically compressed. |
270 | /// - When receiving a response, if it's headers contain a `Content-Encoding` value that |
271 | /// equals to `deflate`, both values `Content-Encoding` and `Content-Length` are removed from the |
272 | /// headers' set. The response body is automatically decompressed. |
273 | /// |
274 | /// If the `deflate` feature is turned on, the default option is enabled. |
275 | /// |
276 | /// # Optional |
277 | /// |
278 | /// This requires the optional `deflate` feature to be enabled |
279 | #[cfg (feature = "deflate" )] |
280 | #[cfg_attr (docsrs, doc(cfg(feature = "deflate" )))] |
281 | pub fn deflate(self, enable: bool) -> ClientBuilder { |
282 | self.with_inner(|inner| inner.deflate(enable)) |
283 | } |
284 | |
285 | /// Disable auto response body gzip decompression. |
286 | /// |
287 | /// This method exists even if the optional `gzip` feature is not enabled. |
288 | /// This can be used to ensure a `Client` doesn't use gzip decompression |
289 | /// even if another dependency were to enable the optional `gzip` feature. |
290 | pub fn no_gzip(self) -> ClientBuilder { |
291 | self.with_inner(|inner| inner.no_gzip()) |
292 | } |
293 | |
294 | /// Disable auto response body brotli decompression. |
295 | /// |
296 | /// This method exists even if the optional `brotli` feature is not enabled. |
297 | /// This can be used to ensure a `Client` doesn't use brotli decompression |
298 | /// even if another dependency were to enable the optional `brotli` feature. |
299 | pub fn no_brotli(self) -> ClientBuilder { |
300 | self.with_inner(|inner| inner.no_brotli()) |
301 | } |
302 | |
303 | /// Disable auto response body deflate decompression. |
304 | /// |
305 | /// This method exists even if the optional `deflate` feature is not enabled. |
306 | /// This can be used to ensure a `Client` doesn't use deflate decompression |
307 | /// even if another dependency were to enable the optional `deflate` feature. |
308 | pub fn no_deflate(self) -> ClientBuilder { |
309 | self.with_inner(|inner| inner.no_deflate()) |
310 | } |
311 | |
312 | // Redirect options |
313 | |
314 | /// Set a `redirect::Policy` for this client. |
315 | /// |
316 | /// Default will follow redirects up to a maximum of 10. |
317 | pub fn redirect(self, policy: redirect::Policy) -> ClientBuilder { |
318 | self.with_inner(move |inner| inner.redirect(policy)) |
319 | } |
320 | |
321 | /// Enable or disable automatic setting of the `Referer` header. |
322 | /// |
323 | /// Default is `true`. |
324 | pub fn referer(self, enable: bool) -> ClientBuilder { |
325 | self.with_inner(|inner| inner.referer(enable)) |
326 | } |
327 | |
328 | // Proxy options |
329 | |
330 | /// Add a `Proxy` to the list of proxies the `Client` will use. |
331 | /// |
332 | /// # Note |
333 | /// |
334 | /// Adding a proxy will disable the automatic usage of the "system" proxy. |
335 | pub fn proxy(self, proxy: Proxy) -> ClientBuilder { |
336 | self.with_inner(move |inner| inner.proxy(proxy)) |
337 | } |
338 | |
339 | /// Clear all `Proxies`, so `Client` will use no proxy anymore. |
340 | /// |
341 | /// # Note |
342 | /// To add a proxy exclusion list, use [crate::proxy::Proxy::no_proxy()] |
343 | /// on all desired proxies instead. |
344 | /// |
345 | /// This also disables the automatic usage of the "system" proxy. |
346 | pub fn no_proxy(self) -> ClientBuilder { |
347 | self.with_inner(move |inner| inner.no_proxy()) |
348 | } |
349 | |
350 | // Timeout options |
351 | |
352 | /// Set a timeout for connect, read and write operations of a `Client`. |
353 | /// |
354 | /// Default is 30 seconds. |
355 | /// |
356 | /// Pass `None` to disable timeout. |
357 | pub fn timeout<T>(mut self, timeout: T) -> ClientBuilder |
358 | where |
359 | T: Into<Option<Duration>>, |
360 | { |
361 | self.timeout = Timeout(timeout.into()); |
362 | self |
363 | } |
364 | |
365 | /// Set a timeout for only the connect phase of a `Client`. |
366 | /// |
367 | /// Default is `None`. |
368 | pub fn connect_timeout<T>(self, timeout: T) -> ClientBuilder |
369 | where |
370 | T: Into<Option<Duration>>, |
371 | { |
372 | let timeout = timeout.into(); |
373 | if let Some(dur) = timeout { |
374 | self.with_inner(|inner| inner.connect_timeout(dur)) |
375 | } else { |
376 | self |
377 | } |
378 | } |
379 | |
380 | /// Set whether connections should emit verbose logs. |
381 | /// |
382 | /// Enabling this option will emit [log][] messages at the `TRACE` level |
383 | /// for read and write operations on connections. |
384 | /// |
385 | /// [log]: https://crates.io/crates/log |
386 | pub fn connection_verbose(self, verbose: bool) -> ClientBuilder { |
387 | self.with_inner(move |inner| inner.connection_verbose(verbose)) |
388 | } |
389 | |
390 | // HTTP options |
391 | |
392 | /// Set an optional timeout for idle sockets being kept-alive. |
393 | /// |
394 | /// Pass `None` to disable timeout. |
395 | /// |
396 | /// Default is 90 seconds. |
397 | pub fn pool_idle_timeout<D>(self, val: D) -> ClientBuilder |
398 | where |
399 | D: Into<Option<Duration>>, |
400 | { |
401 | self.with_inner(|inner| inner.pool_idle_timeout(val)) |
402 | } |
403 | |
404 | /// Sets the maximum idle connection per host allowed in the pool. |
405 | pub fn pool_max_idle_per_host(self, max: usize) -> ClientBuilder { |
406 | self.with_inner(move |inner| inner.pool_max_idle_per_host(max)) |
407 | } |
408 | |
409 | /// Send headers as title case instead of lowercase. |
410 | pub fn http1_title_case_headers(self) -> ClientBuilder { |
411 | self.with_inner(|inner| inner.http1_title_case_headers()) |
412 | } |
413 | |
414 | /// Set whether HTTP/1 connections will accept obsolete line folding for |
415 | /// header values. |
416 | /// |
417 | /// Newline codepoints (`\r` and `\n`) will be transformed to spaces when |
418 | /// parsing. |
419 | pub fn http1_allow_obsolete_multiline_headers_in_responses(self, value: bool) -> ClientBuilder { |
420 | self.with_inner(|inner| inner.http1_allow_obsolete_multiline_headers_in_responses(value)) |
421 | } |
422 | |
423 | /// Only use HTTP/1. |
424 | pub fn http1_only(self) -> ClientBuilder { |
425 | self.with_inner(|inner| inner.http1_only()) |
426 | } |
427 | |
428 | /// Allow HTTP/0.9 responses |
429 | pub fn http09_responses(self) -> ClientBuilder { |
430 | self.with_inner(|inner| inner.http09_responses()) |
431 | } |
432 | |
433 | /// Only use HTTP/2. |
434 | pub fn http2_prior_knowledge(self) -> ClientBuilder { |
435 | self.with_inner(|inner| inner.http2_prior_knowledge()) |
436 | } |
437 | |
438 | /// Sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP2 stream-level flow control. |
439 | /// |
440 | /// Default is currently 65,535 but may change internally to optimize for common uses. |
441 | pub fn http2_initial_stream_window_size(self, sz: impl Into<Option<u32>>) -> ClientBuilder { |
442 | self.with_inner(|inner| inner.http2_initial_stream_window_size(sz)) |
443 | } |
444 | |
445 | /// Sets the max connection-level flow control for HTTP2 |
446 | /// |
447 | /// Default is currently 65,535 but may change internally to optimize for common uses. |
448 | pub fn http2_initial_connection_window_size(self, sz: impl Into<Option<u32>>) -> ClientBuilder { |
449 | self.with_inner(|inner| inner.http2_initial_connection_window_size(sz)) |
450 | } |
451 | |
452 | /// Sets whether to use an adaptive flow control. |
453 | /// |
454 | /// Enabling this will override the limits set in `http2_initial_stream_window_size` and |
455 | /// `http2_initial_connection_window_size`. |
456 | pub fn http2_adaptive_window(self, enabled: bool) -> ClientBuilder { |
457 | self.with_inner(|inner| inner.http2_adaptive_window(enabled)) |
458 | } |
459 | |
460 | /// Sets the maximum frame size to use for HTTP2. |
461 | /// |
462 | /// Default is currently 16,384 but may change internally to optimize for common uses. |
463 | pub fn http2_max_frame_size(self, sz: impl Into<Option<u32>>) -> ClientBuilder { |
464 | self.with_inner(|inner| inner.http2_max_frame_size(sz)) |
465 | } |
466 | |
467 | // TCP options |
468 | |
469 | /// Set whether sockets have `TCP_NODELAY` enabled. |
470 | /// |
471 | /// Default is `true`. |
472 | pub fn tcp_nodelay(self, enabled: bool) -> ClientBuilder { |
473 | self.with_inner(move |inner| inner.tcp_nodelay(enabled)) |
474 | } |
475 | |
476 | /// Bind to a local IP Address. |
477 | /// |
478 | /// # Example |
479 | /// |
480 | /// ``` |
481 | /// use std::net::IpAddr; |
482 | /// let local_addr = IpAddr::from([12, 4, 1, 8]); |
483 | /// let client = reqwest::blocking::Client::builder() |
484 | /// .local_address(local_addr) |
485 | /// .build().unwrap(); |
486 | /// ``` |
487 | pub fn local_address<T>(self, addr: T) -> ClientBuilder |
488 | where |
489 | T: Into<Option<IpAddr>>, |
490 | { |
491 | self.with_inner(move |inner| inner.local_address(addr)) |
492 | } |
493 | |
494 | /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration. |
495 | /// |
496 | /// If `None`, the option will not be set. |
497 | pub fn tcp_keepalive<D>(self, val: D) -> ClientBuilder |
498 | where |
499 | D: Into<Option<Duration>>, |
500 | { |
501 | self.with_inner(move |inner| inner.tcp_keepalive(val)) |
502 | } |
503 | |
504 | // TLS options |
505 | |
506 | /// Add a custom root certificate. |
507 | /// |
508 | /// This allows connecting to a server that has a self-signed |
509 | /// certificate for example. This **does not** replace the existing |
510 | /// trusted store. |
511 | /// |
512 | /// # Example |
513 | /// |
514 | /// ``` |
515 | /// # use std::fs::File; |
516 | /// # use std::io::Read; |
517 | /// # fn build_client() -> Result<(), Box<dyn std::error::Error>> { |
518 | /// // read a local binary DER encoded certificate |
519 | /// let der = std::fs::read("my-cert.der" )?; |
520 | /// |
521 | /// // create a certificate |
522 | /// let cert = reqwest::Certificate::from_der(&der)?; |
523 | /// |
524 | /// // get a client builder |
525 | /// let client = reqwest::blocking::Client::builder() |
526 | /// .add_root_certificate(cert) |
527 | /// .build()?; |
528 | /// # drop(client); |
529 | /// # Ok(()) |
530 | /// # } |
531 | /// ``` |
532 | /// |
533 | /// # Optional |
534 | /// |
535 | /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)` |
536 | /// feature to be enabled. |
537 | #[cfg (feature = "__tls" )] |
538 | #[cfg_attr ( |
539 | docsrs, |
540 | doc(cfg(any( |
541 | feature = "default-tls" , |
542 | feature = "native-tls" , |
543 | feature = "rustls-tls" |
544 | ))) |
545 | )] |
546 | pub fn add_root_certificate(self, cert: Certificate) -> ClientBuilder { |
547 | self.with_inner(move |inner| inner.add_root_certificate(cert)) |
548 | } |
549 | |
550 | /// Controls the use of built-in system certificates during certificate validation. |
551 | /// |
552 | /// Defaults to `true` -- built-in system certs will be used. |
553 | /// |
554 | /// # Optional |
555 | /// |
556 | /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)` |
557 | /// feature to be enabled. |
558 | #[cfg (feature = "__tls" )] |
559 | #[cfg_attr ( |
560 | docsrs, |
561 | doc(cfg(any( |
562 | feature = "default-tls" , |
563 | feature = "native-tls" , |
564 | feature = "rustls-tls" |
565 | ))) |
566 | )] |
567 | pub fn tls_built_in_root_certs(self, tls_built_in_root_certs: bool) -> ClientBuilder { |
568 | self.with_inner(move |inner| inner.tls_built_in_root_certs(tls_built_in_root_certs)) |
569 | } |
570 | |
571 | /// Sets the identity to be used for client certificate authentication. |
572 | /// |
573 | /// # Optional |
574 | /// |
575 | /// This requires the optional `native-tls` or `rustls-tls(-...)` feature to be |
576 | /// enabled. |
577 | #[cfg (any(feature = "native-tls" , feature = "__rustls" ))] |
578 | #[cfg_attr (docsrs, doc(cfg(any(feature = "native-tls" , feature = "rustls-tls" ))))] |
579 | pub fn identity(self, identity: Identity) -> ClientBuilder { |
580 | self.with_inner(move |inner| inner.identity(identity)) |
581 | } |
582 | |
583 | /// Controls the use of hostname verification. |
584 | /// |
585 | /// Defaults to `false`. |
586 | /// |
587 | /// # Warning |
588 | /// |
589 | /// You should think very carefully before you use this method. If |
590 | /// hostname verification is not used, any valid certificate for any |
591 | /// site will be trusted for use from any other. This introduces a |
592 | /// significant vulnerability to man-in-the-middle attacks. |
593 | /// |
594 | /// # Optional |
595 | /// |
596 | /// This requires the optional `native-tls` feature to be enabled. |
597 | #[cfg (feature = "native-tls" )] |
598 | #[cfg_attr (docsrs, doc(cfg(feature = "native-tls" )))] |
599 | pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder { |
600 | self.with_inner(|inner| inner.danger_accept_invalid_hostnames(accept_invalid_hostname)) |
601 | } |
602 | |
603 | /// Controls the use of certificate validation. |
604 | /// |
605 | /// Defaults to `false`. |
606 | /// |
607 | /// # Warning |
608 | /// |
609 | /// You should think very carefully before using this method. If |
610 | /// invalid certificates are trusted, *any* certificate for *any* site |
611 | /// will be trusted for use. This includes expired certificates. This |
612 | /// introduces significant vulnerabilities, and should only be used |
613 | /// as a last resort. |
614 | #[cfg (feature = "__tls" )] |
615 | #[cfg_attr ( |
616 | docsrs, |
617 | doc(cfg(any( |
618 | feature = "default-tls" , |
619 | feature = "native-tls" , |
620 | feature = "rustls-tls" |
621 | ))) |
622 | )] |
623 | pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder { |
624 | self.with_inner(|inner| inner.danger_accept_invalid_certs(accept_invalid_certs)) |
625 | } |
626 | |
627 | /// Controls the use of TLS server name indication. |
628 | /// |
629 | /// Defaults to `true`. |
630 | #[cfg (feature = "__tls" )] |
631 | #[cfg_attr ( |
632 | docsrs, |
633 | doc(cfg(any( |
634 | feature = "default-tls" , |
635 | feature = "native-tls" , |
636 | feature = "rustls-tls" |
637 | ))) |
638 | )] |
639 | pub fn tls_sni(self, tls_sni: bool) -> ClientBuilder { |
640 | self.with_inner(|inner| inner.tls_sni(tls_sni)) |
641 | } |
642 | |
643 | /// Set the minimum required TLS version for connections. |
644 | /// |
645 | /// By default the TLS backend's own default is used. |
646 | /// |
647 | /// # Errors |
648 | /// |
649 | /// A value of `tls::Version::TLS_1_3` will cause an error with the |
650 | /// `native-tls`/`default-tls` backend. This does not mean the version |
651 | /// isn't supported, just that it can't be set as a minimum due to |
652 | /// technical limitations. |
653 | /// |
654 | /// # Optional |
655 | /// |
656 | /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)` |
657 | /// feature to be enabled. |
658 | #[cfg (feature = "__tls" )] |
659 | #[cfg_attr ( |
660 | docsrs, |
661 | doc(cfg(any( |
662 | feature = "default-tls" , |
663 | feature = "native-tls" , |
664 | feature = "rustls-tls" |
665 | ))) |
666 | )] |
667 | pub fn min_tls_version(self, version: tls::Version) -> ClientBuilder { |
668 | self.with_inner(|inner| inner.min_tls_version(version)) |
669 | } |
670 | |
671 | /// Set the maximum allowed TLS version for connections. |
672 | /// |
673 | /// By default there's no maximum. |
674 | /// |
675 | /// # Errors |
676 | /// |
677 | /// A value of `tls::Version::TLS_1_3` will cause an error with the |
678 | /// `native-tls`/`default-tls` backend. This does not mean the version |
679 | /// isn't supported, just that it can't be set as a maximum due to |
680 | /// technical limitations. |
681 | /// |
682 | /// # Optional |
683 | /// |
684 | /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)` |
685 | /// feature to be enabled. |
686 | #[cfg (feature = "__tls" )] |
687 | #[cfg_attr ( |
688 | docsrs, |
689 | doc(cfg(any( |
690 | feature = "default-tls" , |
691 | feature = "native-tls" , |
692 | feature = "rustls-tls" |
693 | ))) |
694 | )] |
695 | pub fn max_tls_version(self, version: tls::Version) -> ClientBuilder { |
696 | self.with_inner(|inner| inner.max_tls_version(version)) |
697 | } |
698 | |
699 | /// Force using the native TLS backend. |
700 | /// |
701 | /// Since multiple TLS backends can be optionally enabled, this option will |
702 | /// force the `native-tls` backend to be used for this `Client`. |
703 | /// |
704 | /// # Optional |
705 | /// |
706 | /// This requires the optional `native-tls` feature to be enabled. |
707 | #[cfg (feature = "native-tls" )] |
708 | #[cfg_attr (docsrs, doc(cfg(feature = "native-tls" )))] |
709 | pub fn use_native_tls(self) -> ClientBuilder { |
710 | self.with_inner(move |inner| inner.use_native_tls()) |
711 | } |
712 | |
713 | /// Force using the Rustls TLS backend. |
714 | /// |
715 | /// Since multiple TLS backends can be optionally enabled, this option will |
716 | /// force the `rustls` backend to be used for this `Client`. |
717 | /// |
718 | /// # Optional |
719 | /// |
720 | /// This requires the optional `rustls-tls(-...)` feature to be enabled. |
721 | #[cfg (feature = "__rustls" )] |
722 | #[cfg_attr (docsrs, doc(cfg(feature = "rustls-tls" )))] |
723 | pub fn use_rustls_tls(self) -> ClientBuilder { |
724 | self.with_inner(move |inner| inner.use_rustls_tls()) |
725 | } |
726 | |
727 | /// Use a preconfigured TLS backend. |
728 | /// |
729 | /// If the passed `Any` argument is not a TLS backend that reqwest |
730 | /// understands, the `ClientBuilder` will error when calling `build`. |
731 | /// |
732 | /// # Advanced |
733 | /// |
734 | /// This is an advanced option, and can be somewhat brittle. Usage requires |
735 | /// keeping the preconfigured TLS argument version in sync with reqwest, |
736 | /// since version mismatches will result in an "unknown" TLS backend. |
737 | /// |
738 | /// If possible, it's preferable to use the methods on `ClientBuilder` |
739 | /// to configure reqwest's TLS. |
740 | /// |
741 | /// # Optional |
742 | /// |
743 | /// This requires one of the optional features `native-tls` or |
744 | /// `rustls-tls(-...)` to be enabled. |
745 | #[cfg (any(feature = "native-tls" , feature = "__rustls" ,))] |
746 | #[cfg_attr (docsrs, doc(cfg(any(feature = "native-tls" , feature = "rustls-tls" ))))] |
747 | pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder { |
748 | self.with_inner(move |inner| inner.use_preconfigured_tls(tls)) |
749 | } |
750 | |
751 | /// Enables the [trust-dns](trust_dns_resolver) async resolver instead of a default threadpool using `getaddrinfo`. |
752 | /// |
753 | /// If the `trust-dns` feature is turned on, the default option is enabled. |
754 | /// |
755 | /// # Optional |
756 | /// |
757 | /// This requires the optional `trust-dns` feature to be enabled |
758 | #[cfg (feature = "trust-dns" )] |
759 | #[cfg_attr (docsrs, doc(cfg(feature = "trust-dns" )))] |
760 | pub fn trust_dns(self, enable: bool) -> ClientBuilder { |
761 | self.with_inner(|inner| inner.trust_dns(enable)) |
762 | } |
763 | |
764 | /// Disables the trust-dns async resolver. |
765 | /// |
766 | /// This method exists even if the optional `trust-dns` feature is not enabled. |
767 | /// This can be used to ensure a `Client` doesn't use the trust-dns async resolver |
768 | /// even if another dependency were to enable the optional `trust-dns` feature. |
769 | pub fn no_trust_dns(self) -> ClientBuilder { |
770 | self.with_inner(|inner| inner.no_trust_dns()) |
771 | } |
772 | |
773 | /// Restrict the Client to be used with HTTPS only requests. |
774 | /// |
775 | /// Defaults to false. |
776 | pub fn https_only(self, enabled: bool) -> ClientBuilder { |
777 | self.with_inner(|inner| inner.https_only(enabled)) |
778 | } |
779 | |
780 | /// Override DNS resolution for specific domains to a particular IP address. |
781 | /// |
782 | /// Warning |
783 | /// |
784 | /// Since the DNS protocol has no notion of ports, if you wish to send |
785 | /// traffic to a particular port you must include this port in the URL |
786 | /// itself, any port in the overridden addr will be ignored and traffic sent |
787 | /// to the conventional port for the given scheme (e.g. 80 for http). |
788 | pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder { |
789 | self.resolve_to_addrs(domain, &[addr]) |
790 | } |
791 | |
792 | /// Override DNS resolution for specific domains to particular IP addresses. |
793 | /// |
794 | /// Warning |
795 | /// |
796 | /// Since the DNS protocol has no notion of ports, if you wish to send |
797 | /// traffic to a particular port you must include this port in the URL |
798 | /// itself, any port in the overridden addresses will be ignored and traffic sent |
799 | /// to the conventional port for the given scheme (e.g. 80 for http). |
800 | pub fn resolve_to_addrs(self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder { |
801 | self.with_inner(|inner| inner.resolve_to_addrs(domain, addrs)) |
802 | } |
803 | |
804 | // private |
805 | |
806 | fn with_inner<F>(mut self, func: F) -> ClientBuilder |
807 | where |
808 | F: FnOnce(async_impl::ClientBuilder) -> async_impl::ClientBuilder, |
809 | { |
810 | self.inner = func(self.inner); |
811 | self |
812 | } |
813 | } |
814 | |
815 | impl From<async_impl::ClientBuilder> for ClientBuilder { |
816 | fn from(builder: async_impl::ClientBuilder) -> Self { |
817 | Self { |
818 | inner: builder, |
819 | timeout: Timeout::default(), |
820 | } |
821 | } |
822 | } |
823 | |
824 | impl Default for Client { |
825 | fn default() -> Self { |
826 | Self::new() |
827 | } |
828 | } |
829 | |
830 | impl Client { |
831 | /// Constructs a new `Client`. |
832 | /// |
833 | /// # Panic |
834 | /// |
835 | /// This method panics if TLS backend cannot be initialized, or the resolver |
836 | /// cannot load the system configuration. |
837 | /// |
838 | /// Use `Client::builder()` if you wish to handle the failure as an `Error` |
839 | /// instead of panicking. |
840 | /// |
841 | /// This method also panics if called from within an async runtime. See docs |
842 | /// on [`reqwest::blocking`][crate::blocking] for details. |
843 | pub fn new() -> Client { |
844 | ClientBuilder::new().build().expect("Client::new()" ) |
845 | } |
846 | |
847 | /// Creates a `ClientBuilder` to configure a `Client`. |
848 | /// |
849 | /// This is the same as `ClientBuilder::new()`. |
850 | pub fn builder() -> ClientBuilder { |
851 | ClientBuilder::new() |
852 | } |
853 | |
854 | /// Convenience method to make a `GET` request to a URL. |
855 | /// |
856 | /// # Errors |
857 | /// |
858 | /// This method fails whenever supplied `Url` cannot be parsed. |
859 | pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder { |
860 | self.request(Method::GET, url) |
861 | } |
862 | |
863 | /// Convenience method to make a `POST` request to a URL. |
864 | /// |
865 | /// # Errors |
866 | /// |
867 | /// This method fails whenever supplied `Url` cannot be parsed. |
868 | pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder { |
869 | self.request(Method::POST, url) |
870 | } |
871 | |
872 | /// Convenience method to make a `PUT` request to a URL. |
873 | /// |
874 | /// # Errors |
875 | /// |
876 | /// This method fails whenever supplied `Url` cannot be parsed. |
877 | pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder { |
878 | self.request(Method::PUT, url) |
879 | } |
880 | |
881 | /// Convenience method to make a `PATCH` request to a URL. |
882 | /// |
883 | /// # Errors |
884 | /// |
885 | /// This method fails whenever supplied `Url` cannot be parsed. |
886 | pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder { |
887 | self.request(Method::PATCH, url) |
888 | } |
889 | |
890 | /// Convenience method to make a `DELETE` request to a URL. |
891 | /// |
892 | /// # Errors |
893 | /// |
894 | /// This method fails whenever supplied `Url` cannot be parsed. |
895 | pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder { |
896 | self.request(Method::DELETE, url) |
897 | } |
898 | |
899 | /// Convenience method to make a `HEAD` request to a URL. |
900 | /// |
901 | /// # Errors |
902 | /// |
903 | /// This method fails whenever supplied `Url` cannot be parsed. |
904 | pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder { |
905 | self.request(Method::HEAD, url) |
906 | } |
907 | |
908 | /// Start building a `Request` with the `Method` and `Url`. |
909 | /// |
910 | /// Returns a `RequestBuilder`, which will allow setting headers and |
911 | /// request body before sending. |
912 | /// |
913 | /// # Errors |
914 | /// |
915 | /// This method fails whenever supplied `Url` cannot be parsed. |
916 | pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder { |
917 | let req = url.into_url().map(move |url| Request::new(method, url)); |
918 | RequestBuilder::new(self.clone(), req) |
919 | } |
920 | |
921 | /// Executes a `Request`. |
922 | /// |
923 | /// A `Request` can be built manually with `Request::new()` or obtained |
924 | /// from a RequestBuilder with `RequestBuilder::build()`. |
925 | /// |
926 | /// You should prefer to use the `RequestBuilder` and |
927 | /// `RequestBuilder::send()`. |
928 | /// |
929 | /// # Errors |
930 | /// |
931 | /// This method fails if there was an error while sending request, |
932 | /// or redirect limit was exhausted. |
933 | pub fn execute(&self, request: Request) -> crate::Result<Response> { |
934 | self.inner.execute_request(request) |
935 | } |
936 | } |
937 | |
938 | impl fmt::Debug for Client { |
939 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
940 | fDebugStruct<'_, '_>.debug_struct(name:"Client" ) |
941 | //.field("gzip", &self.inner.gzip) |
942 | //.field("redirect_policy", &self.inner.redirect_policy) |
943 | //.field("referer", &self.inner.referer) |
944 | .finish() |
945 | } |
946 | } |
947 | |
948 | impl fmt::Debug for ClientBuilder { |
949 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
950 | self.inner.fmt(f) |
951 | } |
952 | } |
953 | |
954 | #[derive (Clone)] |
955 | struct ClientHandle { |
956 | timeout: Timeout, |
957 | inner: Arc<InnerClientHandle>, |
958 | } |
959 | |
960 | type OneshotResponse = oneshot::Sender<crate::Result<async_impl::Response>>; |
961 | type ThreadSender = mpsc::UnboundedSender<(async_impl::Request, OneshotResponse)>; |
962 | |
963 | struct InnerClientHandle { |
964 | tx: Option<ThreadSender>, |
965 | thread: Option<thread::JoinHandle<()>>, |
966 | } |
967 | |
968 | impl Drop for InnerClientHandle { |
969 | fn drop(&mut self) { |
970 | let id: ThreadId = self |
971 | .thread |
972 | .as_ref() |
973 | .map(|h| h.thread().id()) |
974 | .expect(msg:"thread not dropped yet" ); |
975 | |
976 | trace!("closing runtime thread ( {:?})" , id); |
977 | self.tx.take(); |
978 | trace!("signaled close for runtime thread ( {:?})" , id); |
979 | self.thread.take().map(|h: JoinHandle<()>| h.join()); |
980 | trace!("closed runtime thread ( {:?})" , id); |
981 | } |
982 | } |
983 | |
984 | impl ClientHandle { |
985 | fn new(builder: ClientBuilder) -> crate::Result<ClientHandle> { |
986 | let timeout = builder.timeout; |
987 | let builder = builder.inner; |
988 | let (tx, rx) = mpsc::unbounded_channel::<(async_impl::Request, OneshotResponse)>(); |
989 | let (spawn_tx, spawn_rx) = oneshot::channel::<crate::Result<()>>(); |
990 | let handle = thread::Builder::new() |
991 | .name("reqwest-internal-sync-runtime" .into()) |
992 | .spawn(move || { |
993 | use tokio::runtime; |
994 | let rt = match runtime::Builder::new_current_thread() |
995 | .enable_all() |
996 | .build() |
997 | .map_err(crate::error::builder) |
998 | { |
999 | Err(e) => { |
1000 | if let Err(e) = spawn_tx.send(Err(e)) { |
1001 | error!("Failed to communicate runtime creation failure: {:?}" , e); |
1002 | } |
1003 | return; |
1004 | } |
1005 | Ok(v) => v, |
1006 | }; |
1007 | |
1008 | let f = async move { |
1009 | let client = match builder.build() { |
1010 | Err(e) => { |
1011 | if let Err(e) = spawn_tx.send(Err(e)) { |
1012 | error!("Failed to communicate client creation failure: {:?}" , e); |
1013 | } |
1014 | return; |
1015 | } |
1016 | Ok(v) => v, |
1017 | }; |
1018 | if let Err(e) = spawn_tx.send(Ok(())) { |
1019 | error!("Failed to communicate successful startup: {:?}" , e); |
1020 | return; |
1021 | } |
1022 | |
1023 | let mut rx = rx; |
1024 | |
1025 | while let Some((req, req_tx)) = rx.recv().await { |
1026 | let req_fut = client.execute(req); |
1027 | tokio::spawn(forward(req_fut, req_tx)); |
1028 | } |
1029 | |
1030 | trace!("( {:?}) Receiver is shutdown" , thread::current().id()); |
1031 | }; |
1032 | |
1033 | trace!("( {:?}) start runtime::block_on" , thread::current().id()); |
1034 | rt.block_on(f); |
1035 | trace!("( {:?}) end runtime::block_on" , thread::current().id()); |
1036 | drop(rt); |
1037 | trace!("( {:?}) finished" , thread::current().id()); |
1038 | }) |
1039 | .map_err(crate::error::builder)?; |
1040 | |
1041 | // Wait for the runtime thread to start up... |
1042 | match wait::timeout(spawn_rx, None) { |
1043 | Ok(Ok(())) => (), |
1044 | Ok(Err(err)) => return Err(err), |
1045 | Err(_canceled) => event_loop_panicked(), |
1046 | } |
1047 | |
1048 | let inner_handle = Arc::new(InnerClientHandle { |
1049 | tx: Some(tx), |
1050 | thread: Some(handle), |
1051 | }); |
1052 | |
1053 | Ok(ClientHandle { |
1054 | timeout, |
1055 | inner: inner_handle, |
1056 | }) |
1057 | } |
1058 | |
1059 | fn execute_request(&self, req: Request) -> crate::Result<Response> { |
1060 | let (tx, rx) = oneshot::channel(); |
1061 | let (req, body) = req.into_async(); |
1062 | let url = req.url().clone(); |
1063 | let timeout = req.timeout().copied().or(self.timeout.0); |
1064 | |
1065 | self.inner |
1066 | .tx |
1067 | .as_ref() |
1068 | .expect("core thread exited early" ) |
1069 | .send((req, tx)) |
1070 | .expect("core thread panicked" ); |
1071 | |
1072 | let result: Result<crate::Result<async_impl::Response>, wait::Waited<crate::Error>> = |
1073 | if let Some(body) = body { |
1074 | let f = async move { |
1075 | body.send().await?; |
1076 | rx.await.map_err(|_canceled| event_loop_panicked()) |
1077 | }; |
1078 | wait::timeout(f, timeout) |
1079 | } else { |
1080 | let f = async move { rx.await.map_err(|_canceled| event_loop_panicked()) }; |
1081 | wait::timeout(f, timeout) |
1082 | }; |
1083 | |
1084 | match result { |
1085 | Ok(Err(err)) => Err(err.with_url(url)), |
1086 | Ok(Ok(res)) => Ok(Response::new( |
1087 | res, |
1088 | timeout, |
1089 | KeepCoreThreadAlive(Some(self.inner.clone())), |
1090 | )), |
1091 | Err(wait::Waited::TimedOut(e)) => Err(crate::error::request(e).with_url(url)), |
1092 | Err(wait::Waited::Inner(err)) => Err(err.with_url(url)), |
1093 | } |
1094 | } |
1095 | } |
1096 | |
1097 | async fn forward<F>(fut: F, mut tx: OneshotResponse) |
1098 | where |
1099 | F: Future<Output = crate::Result<async_impl::Response>>, |
1100 | { |
1101 | use std::task::Poll; |
1102 | |
1103 | futures_util::pin_mut!(fut); |
1104 | |
1105 | // "select" on the sender being canceled, and the future completing |
1106 | let res: Option> = futures_utilPollFn) -> …>::future::poll_fn(|cx: &mut Context<'_>| { |
1107 | match fut.as_mut().poll(cx) { |
1108 | Poll::Ready(val: Result) => Poll::Ready(Some(val)), |
1109 | Poll::Pending => { |
1110 | // check if the callback is canceled |
1111 | futures_core::ready!(tx.poll_closed(cx)); |
1112 | Poll::Ready(None) |
1113 | } |
1114 | } |
1115 | }) |
1116 | .await; |
1117 | |
1118 | if let Some(res: Result) = res { |
1119 | let _ = tx.send(res); |
1120 | } |
1121 | // else request is canceled |
1122 | } |
1123 | |
1124 | #[derive (Clone, Copy)] |
1125 | struct Timeout(Option<Duration>); |
1126 | |
1127 | impl Default for Timeout { |
1128 | fn default() -> Timeout { |
1129 | // default mentioned in ClientBuilder::timeout() doc comment |
1130 | Timeout(Some(Duration::from_secs(30))) |
1131 | } |
1132 | } |
1133 | |
1134 | pub(crate) struct KeepCoreThreadAlive(Option<Arc<InnerClientHandle>>); |
1135 | |
1136 | impl KeepCoreThreadAlive { |
1137 | pub(crate) fn empty() -> KeepCoreThreadAlive { |
1138 | KeepCoreThreadAlive(None) |
1139 | } |
1140 | } |
1141 | |
1142 | #[cold ] |
1143 | #[inline (never)] |
1144 | fn event_loop_panicked() -> ! { |
1145 | // The only possible reason there would be a Canceled error |
1146 | // is if the thread running the event loop panicked. We could return |
1147 | // an Err here, like a BrokenPipe, but the Client is not |
1148 | // recoverable. Additionally, the panic in the other thread |
1149 | // is not normal, and should likely be propagated. |
1150 | panic!("event loop thread panicked" ); |
1151 | } |
1152 | |