reqwest/blocking/
client.rs

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