isahc_http_client.rs

 1use std::{mem, sync::Arc, time::Duration};
 2
 3use futures::future::BoxFuture;
 4use isahc::config::RedirectPolicy;
 5use util::maybe;
 6
 7pub use isahc::config::Configurable;
 8pub struct IsahcHttpClient(isahc::HttpClient);
 9
10pub use http_client::*;
11
12impl IsahcHttpClient {
13    pub fn new(proxy: Option<Uri>, user_agent: Option<String>) -> Arc<IsahcHttpClient> {
14        let mut builder = isahc::HttpClient::builder()
15            .connect_timeout(Duration::from_secs(5))
16            .low_speed_timeout(100, Duration::from_secs(5))
17            .proxy(proxy.clone());
18        if let Some(agent) = user_agent {
19            builder = builder.default_header("User-Agent", agent);
20        }
21        Arc::new(IsahcHttpClient(builder.build().unwrap()))
22    }
23    pub fn builder() -> isahc::HttpClientBuilder {
24        isahc::HttpClientBuilder::new()
25    }
26}
27
28impl From<isahc::HttpClient> for IsahcHttpClient {
29    fn from(client: isahc::HttpClient) -> Self {
30        Self(client)
31    }
32}
33
34impl HttpClient for IsahcHttpClient {
35    fn proxy(&self) -> Option<&Uri> {
36        None
37    }
38
39    fn send_with_redirect_policy(
40        &self,
41        req: http_client::http::Request<http_client::AsyncBody>,
42        follow_redirects: bool,
43    ) -> BoxFuture<'static, Result<http_client::Response<http_client::AsyncBody>, anyhow::Error>>
44    {
45        let req = maybe!({
46            let (mut parts, body) = req.into_parts();
47            let mut builder = isahc::Request::builder()
48                .method(parts.method)
49                .uri(parts.uri)
50                .version(parts.version);
51
52            let headers = builder.headers_mut()?;
53            mem::swap(headers, &mut parts.headers);
54
55            let extensions = builder.extensions_mut()?;
56            mem::swap(extensions, &mut parts.extensions);
57
58            let isahc_body = match body.0 {
59                http_client::Inner::Empty => isahc::AsyncBody::empty(),
60                http_client::Inner::AsyncReader(reader) => isahc::AsyncBody::from_reader(reader),
61                http_client::Inner::SyncReader(reader) => {
62                    isahc::AsyncBody::from_bytes_static(reader.into_inner())
63                }
64            };
65
66            builder
67                .redirect_policy(if follow_redirects {
68                    RedirectPolicy::Follow
69                } else {
70                    RedirectPolicy::None
71                })
72                .body(isahc_body)
73                .ok()
74        });
75
76        let client = self.0.clone();
77
78        Box::pin(async move {
79            match req {
80                Some(req) => client
81                    .send_async(req)
82                    .await
83                    .map_err(Into::into)
84                    .map(|response| {
85                        let (parts, body) = response.into_parts();
86                        let body = http_client::AsyncBody::from_reader(body);
87                        http_client::Response::from_parts(parts, body)
88                    }),
89                None => Err(anyhow::anyhow!("Request was malformed")),
90            }
91        })
92    }
93}