|
1 | | -//! HTTP client |
2 | | -
|
3 | 1 | use core::fmt; |
4 | 2 |
|
5 | 3 | use reqwest::{Request, Response}; |
6 | 4 |
|
7 | | -use crate::{ |
8 | | - credentials::{AuthorizationError, Credentials}, |
9 | | - execute::ExecuteRequest, |
10 | | -}; |
| 5 | +use crate::{authorize::Authorize, credentials::Credentials, execute::ExecuteRequest}; |
11 | 6 |
|
12 | 7 | // CLIENT ERROR //////////////////////////////////////////////////////////////// |
13 | 8 |
|
14 | 9 | #[derive(Debug, thiserror::Error)] |
15 | | -pub enum ClientError<E = reqwest::Error> { |
| 10 | +pub enum ClientError<E, A> { |
16 | 11 | #[error("failed to authorize request, {0}")] |
17 | | - Authorize(#[from] AuthorizationError), |
| 12 | + Authorize(A), |
18 | 13 | #[error("failed to execute request, {0}")] |
19 | 14 | Execute(E), |
20 | 15 | } |
21 | 16 |
|
22 | 17 | // CLIENT ////////////////////////////////////////////////////////////////////// |
23 | 18 |
|
24 | | -/// HTTP client with optional [`Credentials`]. |
25 | | -/// |
26 | | -/// When credentials are provided, the client, will ensure requests are authorized |
27 | | -/// before they are executed. |
28 | | -#[derive(Debug, Default, Clone)] |
| 19 | +/// HTTP client that authorize requests before execution. |
| 20 | +#[derive(Debug, Clone)] |
29 | 21 | #[must_use] |
30 | | -pub struct Client<T = reqwest::Client> { |
31 | | - inner: T, |
32 | | - credentials: Option<Credentials>, |
| 22 | +pub struct Client<T = reqwest::Client, A = Option<Credentials>> { |
| 23 | + executer: T, |
| 24 | + authorizer: A, |
33 | 25 | } |
34 | 26 |
|
35 | | -impl<T> From<T> for Client<T> { |
| 27 | +impl<T, A: Default> From<T> for Client<T, A> { |
36 | 28 | fn from(value: T) -> Self { |
37 | 29 | Self { |
38 | | - inner: value, |
39 | | - credentials: None, |
| 30 | + executer: value, |
| 31 | + authorizer: A::default(), |
40 | 32 | } |
41 | 33 | } |
42 | 34 | } |
43 | 35 |
|
44 | | -impl From<&Credentials> for Client { |
45 | | - fn from(value: &Credentials) -> Self { |
46 | | - Self::from(value.clone()) |
| 36 | +impl Default for Client { |
| 37 | + fn default() -> Self { |
| 38 | + Self::from(reqwest::Client::default()) |
47 | 39 | } |
48 | 40 | } |
49 | 41 |
|
50 | | -impl<T: Into<Box<str>>> From<Credentials<T>> for Client { |
51 | | - fn from(value: Credentials<T>) -> Self { |
| 42 | +impl<T: fmt::Debug, A: fmt::Debug> Client<T, A> { |
| 43 | + #[cfg_attr(feature = "tracing", tracing::instrument)] |
| 44 | + pub fn new(executer: T, authorizer: A) -> Self { |
52 | 45 | Self { |
53 | | - inner: reqwest::Client::new(), |
54 | | - credentials: Some(value.into()), |
| 46 | + executer, |
| 47 | + authorizer, |
55 | 48 | } |
56 | 49 | } |
57 | | -} |
58 | 50 |
|
59 | | -impl Client { |
60 | | - pub fn new() -> Self { |
61 | | - Self::default() |
| 51 | + pub fn executer(&self) -> &T { |
| 52 | + &self.executer |
62 | 53 | } |
63 | | -} |
64 | 54 |
|
65 | | -impl<T: fmt::Debug> Client<T> { |
66 | | - /// Sets the `credentials` to be used by the client to authorize HTTP request, |
67 | | - /// discarding the current value, if any. |
68 | | - #[cfg_attr(feature = "tracing", tracing::instrument)] |
69 | | - pub fn set_credentials(&mut self, credentials: Option<Credentials>) { |
70 | | - self.credentials = credentials; |
| 55 | + pub fn executer_mut(&mut self) -> &mut T { |
| 56 | + &mut self.executer |
71 | 57 | } |
72 | 58 |
|
73 | | - #[cfg_attr(feature = "tracing", tracing::instrument)] |
74 | | - fn set_credentials_from<U: Into<Box<str>> + fmt::Debug>( |
75 | | - &mut self, |
76 | | - credentials: Option<Credentials<U>>, |
77 | | - ) { |
78 | | - self.credentials = credentials.map(Credentials::into); |
| 59 | + pub fn authorizer(&self) -> &A { |
| 60 | + &self.authorizer |
79 | 61 | } |
80 | 62 |
|
81 | | - /// Fills the `credentials` to be used by the client to authorize HTTP request, |
82 | | - /// discarding the current value, if any. |
83 | | - pub fn with_credentials<U: Into<Box<str>> + fmt::Debug>( |
84 | | - mut self, |
85 | | - credentials: impl Into<Option<Credentials<U>>>, |
86 | | - ) -> Self { |
87 | | - self.set_credentials_from(credentials.into()); |
88 | | - self |
89 | | - } |
90 | | - |
91 | | - /// Returns the credentials that will be used by this client to authorized |
92 | | - /// subsequent HTTP requests. |
93 | | - #[cfg_attr(feature = "tracing", tracing::instrument)] |
94 | | - pub fn credentials(&self) -> Option<Credentials<&str>> { |
95 | | - self.credentials.as_ref().map(Credentials::as_ref) |
96 | | - } |
97 | | - |
98 | | - /// Returns a shared reference to the inner HTTP client. |
99 | | - #[cfg_attr(feature = "tracing", tracing::instrument)] |
100 | | - pub fn inner(&self) -> &T { |
101 | | - &self.inner |
102 | | - } |
103 | | - |
104 | | - /// Appends an `Authorization` header to the `request`, if this client has credentials and unless it is already set. |
105 | | - /// |
106 | | - /// Returns `true` if the `Authorization` header was inserted. |
107 | | - /// |
108 | | - /// # Errors |
109 | | - /// |
110 | | - /// Upon failure to produce the header value. |
111 | | - /// |
112 | | - /// If the client doesn't have credentials, this method is infallible. |
113 | | - #[cfg_attr(feature = "tracing", tracing::instrument)] |
114 | | - pub fn authorize(&self, request: &mut Request) -> Result<bool, AuthorizationError> { |
115 | | - match self.credentials() { |
116 | | - None => Ok(false), |
117 | | - Some(credentials) => credentials.authorize(request), |
118 | | - } |
| 63 | + pub fn authorizer_mut(&mut self) -> &mut A { |
| 64 | + &mut self.authorizer |
119 | 65 | } |
120 | 66 | } |
121 | 67 |
|
122 | | -impl<T: ExecuteRequest> ExecuteRequest for Client<T> { |
123 | | - type Error = ClientError<T::Error>; |
| 68 | +impl<T: ExecuteRequest, A: Authorize> ExecuteRequest for Client<T, A> { |
| 69 | + type Error = ClientError<T::Error, A::Error>; |
124 | 70 |
|
125 | 71 | fn execute_request( |
126 | 72 | &self, |
127 | 73 | mut request: Request, |
128 | 74 | ) -> impl Future<Output = Result<Response, Self::Error>> + Send + 'static { |
129 | 75 | let result = self |
| 76 | + .authorizer |
130 | 77 | .authorize(&mut request) |
131 | | - .map(|_| self.inner.execute_request(request)); |
| 78 | + .map_err(ClientError::Authorize) |
| 79 | + .map(|_| self.executer.execute_request(request)); |
132 | 80 |
|
133 | 81 | async move { result?.await.map_err(ClientError::Execute) } |
134 | 82 | } |
135 | 83 | } |
136 | | - |
137 | | -#[cfg(feature = "zeroize")] |
138 | | -impl<T> Drop for Client<T> { |
139 | | - fn drop(&mut self) { |
140 | | - use zeroize::Zeroize; |
141 | | - |
142 | | - if let Some(mut credentials) = self.credentials.take() { |
143 | | - credentials.zeroize(); |
144 | | - } |
145 | | - } |
146 | | -} |
0 commit comments