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