1use std::future::{Future, ready};
14use std::str::FromStr;
15use std::sync::Arc;
16use bytes::Bytes;
17use futures::{FutureExt, TryFutureExt, TryStreamExt};
18use crate::async_client_trait::{AppAuthClient, HttpClient, HttpRequest, HttpRequestResultRaw,
19 NoauthClient, TeamAuthClient, TeamSelect, UserAuthClient};
20use crate::default_client_common::impl_set_path_root;
21use crate::Error;
22use crate::oauth2::{Authorization, TokenCache};
23
24macro_rules! impl_update_token {
25 ($self:ident) => {
26 fn update_token(&$self, old_token: Arc<String>)
27 -> impl Future<Output = Result<bool, Error>> + Send
28 {
29 info!("refreshing auth token");
30 $self.tokens
31 .update_token(
32 TokenUpdateClient { inner: &$self.inner },
33 old_token,
34 )
35 .map(|r| match r {
36 Ok(_) => Ok(true),
37 Err(e) => {
38 error!("failed to update auth token: {e}");
39 Err(e.into())
40 }
41 })
42 }
43 };
44}
45
46pub struct UserAuthDefaultClient {
48 inner: ReqwestClient,
49 tokens: Arc<TokenCache>,
50 path_root: Option<String>, }
52
53impl UserAuthDefaultClient {
54 pub fn new(auth: Authorization) -> Self {
56 Self::from_token_cache(Arc::new(TokenCache::new(auth)))
57 }
58
59 pub fn from_token_cache(tokens: Arc<TokenCache>) -> Self {
62 Self {
63 inner: Default::default(),
64 tokens,
65 path_root: None,
66 }
67 }
68
69 impl_set_path_root!(self);
70}
71
72impl HttpClient for UserAuthDefaultClient {
73 type Request = ReqwestRequest;
74
75 fn execute(
76 &self,
77 request: Self::Request,
78 body: Bytes,
79 ) -> impl Future<Output=Result<HttpRequestResultRaw, Error>> + Send {
80 self.inner.execute(request, body)
81 }
82
83 fn new_request(&self, url: &str) -> Self::Request {
84 self.inner.new_request(url)
85 }
86
87 impl_update_token!(self);
88
89 fn token(&self) -> Option<Arc<String>> {
90 self.tokens.get_token()
91 }
92
93 fn path_root(&self) -> Option<&str> {
94 self.path_root.as_deref()
95 }
96}
97
98impl UserAuthClient for UserAuthDefaultClient {}
99
100pub struct TeamAuthDefaultClient {
102 inner: ReqwestClient,
103 tokens: Arc<TokenCache>,
104 path_root: Option<String>, team_select: Option<TeamSelect>,
106}
107
108impl TeamAuthDefaultClient {
109 pub fn new(tokens: impl Into<Arc<TokenCache>>) -> Self {
111 Self {
112 inner: Default::default(),
113 tokens: tokens.into(),
114 path_root: None,
115 team_select: None,
116 }
117 }
118
119 pub fn select(&mut self, team_select: Option<TeamSelect>) {
121 self.team_select = team_select;
122 }
123
124 impl_set_path_root!(self);
125}
126
127impl HttpClient for TeamAuthDefaultClient {
128 type Request = ReqwestRequest;
129
130 fn execute(
131 &self,
132 request: Self::Request,
133 body: Bytes,
134 ) -> impl Future<Output=Result<HttpRequestResultRaw, Error>> + Send {
135 self.inner.execute(request, body)
136 }
137
138 fn new_request(&self, url: &str) -> Self::Request {
139 self.inner.new_request(url)
140 }
141
142 fn token(&self) -> Option<Arc<String>> {
143 self.tokens.get_token()
144 }
145
146 impl_update_token!(self);
147
148 fn path_root(&self) -> Option<&str> {
149 self.path_root.as_deref()
150 }
151
152 fn team_select(&self) -> Option<&TeamSelect> {
153 self.team_select.as_ref()
154 }
155}
156
157impl TeamAuthClient for TeamAuthDefaultClient {}
158
159#[derive(Debug)]
161pub struct AppAuthDefaultClient {
162 inner: ReqwestClient,
163 path_root: Option<String>,
164 auth: String,
165}
166
167impl AppAuthDefaultClient {
168 pub fn new(app_key: &str, app_secret: &str) -> Self {
170 use base64::prelude::*;
171 let encoded = BASE64_STANDARD.encode(format!("{app_key}:{app_secret}"));
172 Self {
173 inner: ReqwestClient::default(),
174 path_root: None,
175 auth: format!("Basic {encoded}"),
176 }
177 }
178
179 impl_set_path_root!(self);
180}
181
182impl HttpClient for AppAuthDefaultClient {
183 type Request = ReqwestRequest;
184
185 fn execute(&self, request: Self::Request, body: Bytes) -> impl Future<Output=Result<HttpRequestResultRaw, Error>> + Send {
186 self.inner.execute(request, body)
187 }
188
189 fn new_request(&self, url: &str) -> Self::Request {
190 self.inner.new_request(url)
191 .set_header("Authorization", &self.auth)
192 }
193}
194
195impl AppAuthClient for AppAuthDefaultClient {}
196
197#[derive(Debug, Default)]
199pub struct NoauthDefaultClient {
200 inner: ReqwestClient,
201 path_root: Option<String>,
202}
203
204impl NoauthDefaultClient {
205 impl_set_path_root!(self);
206}
207
208impl HttpClient for NoauthDefaultClient {
209 type Request = ReqwestRequest;
210
211 fn execute(
212 &self,
213 request: Self::Request,
214 body: Bytes,
215 ) -> impl Future<Output=Result<HttpRequestResultRaw, Error>> + Send {
216 self.inner.execute(request, body)
217 }
218
219 fn new_request(&self, url: &str) -> Self::Request {
220 self.inner.new_request(url)
221 }
222
223 fn path_root(&self) -> Option<&str> {
224 self.path_root.as_deref()
225 }
226}
227
228impl NoauthClient for NoauthDefaultClient {}
229
230struct TokenUpdateClient<'a> {
233 inner: &'a ReqwestClient,
234}
235
236impl HttpClient for TokenUpdateClient<'_> {
237 type Request = ReqwestRequest;
238
239 fn execute(
240 &self,
241 request: Self::Request,
242 body: Bytes,
243 ) -> impl Future<Output=Result<HttpRequestResultRaw, Error>> + Send {
244 self.inner.execute(request, body)
245 }
246
247 fn new_request(&self, url: &str) -> Self::Request {
248 self.inner.new_request(url)
249 }
250}
251
252impl NoauthClient for TokenUpdateClient<'_> {}
253
254#[derive(Debug)]
255struct ReqwestClient {
256 inner: reqwest::Client,
257}
258
259impl Default for ReqwestClient {
260 fn default() -> Self {
261 Self {
262 inner: reqwest::Client::builder()
263 .https_only(true)
264 .http2_prior_knowledge()
265 .build()
266 .unwrap()
267 }
268 }
269}
270
271fn unexpected<T: std::error::Error + Send + Sync>(e: T, msg: &str) -> Error {
272 Error::UnexpectedResponse(format!("{msg}: {e}"))
273}
274
275impl HttpClient for ReqwestClient {
276 type Request = ReqwestRequest;
277
278 fn execute(
279 &self,
280 request: Self::Request,
281 body: Bytes,
282 ) -> impl Future<Output = Result<HttpRequestResultRaw, Error>> + Send {
283 let mut req = match request.req.build() {
284 Ok(req) => req,
285 Err(e) => {
286 return ready(Err(Error::HttpClient(Box::new(e)))).boxed();
287 }
288 };
289 debug!("request for {}", req.url());
290 if !body.is_empty() {
291 *req.body_mut() = Some(reqwest::Body::from(body));
292 }
293 self.inner.execute(req)
294 .map_ok_or_else(
295 |e| Err(Error::HttpClient(Box::new(e))),
296 |resp| {
297 let status = resp.status().as_u16();
298
299 let result_header = resp
300 .headers()
301 .get("Dropbox-API-Result")
302 .map(|v| v.to_str())
303 .transpose()
304 .map_err(|e| unexpected(e, "invalid Dropbox-API-Result header"))?
305 .map(ToOwned::to_owned);
306
307 let content_length = resp
308 .headers()
309 .get("Content-Length")
310 .map(|v| {
311 v.to_str()
312 .map_err(|e| unexpected(e, "invalid Content-Length"))
313 .and_then(|s| {
314 u64::from_str(s)
315 .map_err(|e| unexpected(e, "invalid Content-Length"))
316 })
317 })
318 .transpose()?;
319
320 let body = resp.bytes_stream()
321 .map_err(|e| futures::io::Error::new(futures::io::ErrorKind::Other, e))
322 .into_async_read();
323
324 Ok(HttpRequestResultRaw {
325 status,
326 result_header,
327 content_length,
328 body: Box::new(body),
329 })
330 }
331 )
332 .boxed()
333 }
334
335 fn new_request(&self, url: &str) -> Self::Request {
336 ReqwestRequest {
337 req: self.inner.post(url),
338 }
339 }
340}
341
342pub struct ReqwestRequest {
344 req: reqwest::RequestBuilder,
345}
346
347impl HttpRequest for ReqwestRequest {
348 fn set_header(mut self, name: &str, value: &str) -> Self {
349 self.req = self.req.header(name, value);
350 self
351 }
352}