async_jsonrpc_client/http_client/
builder.rs

1use std::{
2    fmt,
3    sync::{atomic::AtomicU64, Arc},
4    time::Duration,
5};
6
7use http::header::{self, HeaderMap, HeaderName, HeaderValue};
8
9use crate::{error::HttpClientError, http_client::HttpClient};
10
11/// A `HttpClientBuilder` can be used to create a `HttpClient` with  custom configuration.
12#[derive(Debug)]
13pub struct HttpClientBuilder {
14    pub(crate) headers: HeaderMap,
15    timeout: Option<Duration>,
16    connect_timeout: Option<Duration>,
17}
18
19impl Default for HttpClientBuilder {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl HttpClientBuilder {
26    /// Creates a new `HttpClientBuilder`.
27    ///
28    /// This is the same as `HttpClient::builder()`.
29    pub fn new() -> Self {
30        Self {
31            headers: HeaderMap::new(),
32            timeout: None,
33            connect_timeout: None,
34        }
35    }
36
37    // ========================================================================
38    // HTTP header options
39    // ========================================================================
40
41    /// Enable basic authentication.
42    pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> Self
43    where
44        U: fmt::Display,
45        P: fmt::Display,
46    {
47        let mut basic_auth = "Basic ".to_string();
48        let auth = if let Some(password) = password {
49            base64::encode(format!("{}:{}", username, password))
50        } else {
51            base64::encode(format!("{}:", username))
52        };
53        basic_auth.push_str(&auth);
54        let value = HeaderValue::from_str(&basic_auth).expect("basic auth header value");
55        self.header(header::AUTHORIZATION, value)
56    }
57
58    /// Enable bearer authentication.
59    pub fn bearer_auth<T>(self, token: T) -> Self
60    where
61        T: fmt::Display,
62    {
63        let bearer_auth = format!("Bearer {}", token);
64        let value = HeaderValue::from_str(&bearer_auth).expect("bearer auth header value");
65        self.header(header::AUTHORIZATION, value)
66    }
67
68    /// Adds a `Header` for every request.
69    pub fn header(mut self, name: HeaderName, value: HeaderValue) -> Self {
70        self.headers.insert(name, value);
71        self
72    }
73
74    /// Adds `Header`s for every request.
75    pub fn headers(mut self, headers: HeaderMap) -> Self {
76        self.headers.extend(headers);
77        self
78    }
79
80    // ========================================================================
81    // Timeout options
82    // ========================================================================
83
84    /// Enables a request timeout.
85    ///
86    /// The timeout is applied from when the request starts connecting until the
87    /// response body has finished.
88    ///
89    /// Default is no timeout.
90    pub fn timeout(mut self, timeout: Duration) -> Self {
91        self.timeout = Some(timeout);
92        self
93    }
94
95    /// Set a timeout for only the connect phase of a `Client`.
96    ///
97    /// Default is `None`.
98    #[cfg(feature = "http-tokio")]
99    pub fn connect_timeout(mut self, timeout: Duration) -> Self {
100        self.connect_timeout = Some(timeout);
101        self
102    }
103
104    // ========================================================================
105
106    /// Returns a `HttpClient` that uses this `HttpClientBuilder` configuration.
107    #[cfg(feature = "http-async-std")]
108    pub fn build<U: Into<String>>(self, url: U) -> Result<HttpClient, HttpClientError> {
109        Ok(HttpClient {
110            url: url.into(),
111            id: Arc::new(AtomicU64::new(1)),
112            client: surf::Client::new(),
113            headers: self.headers,
114            timeout: self.timeout,
115        })
116    }
117
118    /// Returns a `HttpClient` that uses this `HttpClientBuilder` configuration.
119    #[cfg(feature = "http-tokio")]
120    pub fn build<U: Into<String>>(self, url: U) -> Result<HttpClient, HttpClientError> {
121        let builder = reqwest::Client::builder().default_headers(self.headers);
122        let builder = if let Some(timeout) = self.timeout {
123            builder.timeout(timeout)
124        } else {
125            builder
126        };
127        let builder = if let Some(timeout) = self.connect_timeout {
128            builder.connect_timeout(timeout)
129        } else {
130            builder
131        };
132        let client = builder.build()?;
133        Ok(HttpClient {
134            url: url.into(),
135            id: Arc::new(AtomicU64::new(1)),
136            client,
137        })
138    }
139}