isahc_http_client.rs

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