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}