1use crate::client_trait::{
15 AppAuthClient, HttpClient, HttpRequest, HttpRequestResultRaw, NoauthClient, TeamAuthClient,
16 TeamSelect, UserAuthClient,
17};
18use crate::default_client_common::impl_set_path_root;
19use crate::oauth2::{Authorization, TokenCache};
20use crate::Error;
21use futures::FutureExt;
22use std::str::FromStr;
23use std::sync::Arc;
24use ureq::typestate::WithBody;
25use ureq::Agent;
26
27macro_rules! impl_update_token {
28 ($self:ident) => {
29 fn update_token(&$self, old_token: Arc<String>) -> Result<bool, Error> {
30 info!("refreshing auth token");
31 match $self.tokens.update_token(
32 TokenUpdateClient { inner: &$self.inner },
33 old_token,
34 ).now_or_never().unwrap() {
35 Ok(_) => Ok(true),
36 Err(e) => {
37 error!("failed to update auth token: {e}");
38 Err(e.into())
39 }
40 }
41 }
42 };
43}
44
45pub struct UserAuthDefaultClient {
47 inner: UreqClient,
48 tokens: Arc<TokenCache>,
49 path_root: Option<String>, }
51
52impl UserAuthDefaultClient {
53 pub fn new(auth: Authorization) -> Self {
55 Self::from_token_cache(Arc::new(TokenCache::new(auth)))
56 }
57
58 pub fn from_token_cache(tokens: Arc<TokenCache>) -> Self {
61 Self {
62 inner: UreqClient::default(),
63 tokens,
64 path_root: None,
65 }
66 }
67
68 impl_set_path_root!(self);
69}
70
71impl HttpClient for UserAuthDefaultClient {
72 type Request = UreqRequest;
73
74 fn execute(&self, request: Self::Request, body: &[u8]) -> Result<HttpRequestResultRaw, Error> {
75 self.inner.execute(request, body)
76 }
77
78 fn new_request(&self, url: &str) -> Self::Request {
79 self.inner.new_request(url)
80 }
81
82 impl_update_token!(self);
83
84 fn token(&self) -> Option<Arc<String>> {
85 self.tokens.get_token()
86 }
87
88 fn path_root(&self) -> Option<&str> {
89 self.path_root.as_deref()
90 }
91}
92
93impl UserAuthClient for UserAuthDefaultClient {}
94
95pub struct TeamAuthDefaultClient {
97 inner: UreqClient,
98 tokens: Arc<TokenCache>,
99 path_root: Option<String>, team_select: Option<TeamSelect>,
101}
102
103impl TeamAuthDefaultClient {
104 pub fn new(tokens: impl Into<Arc<TokenCache>>) -> Self {
106 Self {
107 inner: UreqClient::default(),
108 tokens: tokens.into(),
109 path_root: None,
110 team_select: None,
111 }
112 }
113
114 pub fn select(&mut self, team_select: Option<TeamSelect>) {
116 self.team_select = team_select;
117 }
118
119 impl_set_path_root!(self);
120}
121
122impl HttpClient for TeamAuthDefaultClient {
123 type Request = UreqRequest;
124
125 fn execute(&self, request: Self::Request, body: &[u8]) -> Result<HttpRequestResultRaw, Error> {
126 self.inner.execute(request, body)
127 }
128
129 fn new_request(&self, url: &str) -> Self::Request {
130 self.inner.new_request(url)
131 }
132
133 fn token(&self) -> Option<Arc<String>> {
134 self.tokens.get_token()
135 }
136
137 impl_update_token!(self);
138
139 fn path_root(&self) -> Option<&str> {
140 self.path_root.as_deref()
141 }
142
143 fn team_select(&self) -> Option<&TeamSelect> {
144 self.team_select.as_ref()
145 }
146}
147
148impl TeamAuthClient for TeamAuthDefaultClient {}
149
150#[derive(Debug)]
152pub struct AppAuthDefaultClient {
153 inner: UreqClient,
154 path_root: Option<String>,
155 auth: String,
156}
157
158impl AppAuthDefaultClient {
159 pub fn new(app_key: &str, app_secret: &str) -> Self {
161 use base64::prelude::*;
162 let encoded = BASE64_STANDARD.encode(format!("{app_key}:{app_secret}"));
163 Self {
164 inner: UreqClient::default(),
165 path_root: None,
166 auth: format!("Basic {encoded}"),
167 }
168 }
169
170 impl_set_path_root!(self);
171}
172
173impl HttpClient for AppAuthDefaultClient {
174 type Request = UreqRequest;
175
176 fn execute(&self, request: Self::Request, body: &[u8]) -> Result<HttpRequestResultRaw, Error> {
177 self.inner.execute(request, body)
178 }
179
180 fn new_request(&self, url: &str) -> Self::Request {
181 self.inner
182 .new_request(url)
183 .set_header("Authorization", &self.auth)
184 }
185}
186
187impl AppAuthClient for AppAuthDefaultClient {}
188
189#[derive(Debug, Default)]
191pub struct NoauthDefaultClient {
192 inner: UreqClient,
193 path_root: Option<String>,
194}
195
196impl NoauthDefaultClient {
197 impl_set_path_root!(self);
198}
199
200impl HttpClient for NoauthDefaultClient {
201 type Request = UreqRequest;
202
203 fn execute(&self, request: Self::Request, body: &[u8]) -> Result<HttpRequestResultRaw, Error> {
204 self.inner.execute(request, body)
205 }
206
207 fn new_request(&self, url: &str) -> Self::Request {
208 self.inner.new_request(url)
209 }
210
211 fn path_root(&self) -> Option<&str> {
212 self.path_root.as_deref()
213 }
214}
215
216impl NoauthClient for NoauthDefaultClient {}
217
218struct TokenUpdateClient<'a> {
221 inner: &'a UreqClient,
222}
223
224impl HttpClient for TokenUpdateClient<'_> {
225 type Request = UreqRequest;
226
227 fn execute(&self, request: Self::Request, body: &[u8]) -> Result<HttpRequestResultRaw, Error> {
228 self.inner.execute(request, body)
229 }
230
231 fn new_request(&self, url: &str) -> Self::Request {
232 self.inner.new_request(url)
233 }
234}
235
236impl crate::async_client_trait::NoauthClient for TokenUpdateClient<'_> {}
237
238#[derive(Debug)]
239struct UreqClient {
240 agent: Agent,
241}
242
243impl Default for UreqClient {
244 fn default() -> Self {
245 Self {
246 agent: Agent::new_with_config(
247 Agent::config_builder()
248 .https_only(true)
249 .http_status_as_error(false)
250 .build(),
251 ),
252 }
253 }
254}
255
256impl HttpClient for UreqClient {
257 type Request = UreqRequest;
258
259 fn execute(&self, request: Self::Request, body: &[u8]) -> Result<HttpRequestResultRaw, Error> {
260 let resp = if body.is_empty() {
261 request.req.send_empty()
262 } else {
263 request.req.send(body)
264 };
265
266 let (status, resp) = match resp {
267 Ok(resp) => (resp.status().as_u16(), resp),
268 Err(ureq::Error::Io(e)) => {
269 return Err(e.into());
270 }
271 Err(e) => {
272 return Err(RequestError { inner: e }.into());
273 }
274 };
275
276 let result_header = resp
277 .headers()
278 .get("Dropbox-API-Result")
279 .map(|v| String::from_utf8(v.as_bytes().to_vec()))
280 .transpose()
281 .map_err(|e| e.utf8_error())?;
282
283 let content_length = resp
284 .headers()
285 .get("Content-Length")
286 .map(|v| {
287 let s = std::str::from_utf8(v.as_bytes())?;
288 u64::from_str(s).map_err(|e| {
289 Error::UnexpectedResponse(format!("invalid Content-Length {s:?}: {e}"))
290 })
291 })
292 .transpose()?;
293
294 Ok(HttpRequestResultRaw {
295 status,
296 result_header,
297 content_length,
298 body: Box::new(resp.into_body().into_reader()),
299 })
300 }
301
302 fn new_request(&self, url: &str) -> Self::Request {
303 UreqRequest {
304 req: self.agent.post(url),
305 }
306 }
307}
308
309pub struct UreqRequest {
311 req: ureq::RequestBuilder<WithBody>,
312}
313
314impl HttpRequest for UreqRequest {
315 fn set_header(mut self, name: &str, value: &str) -> Self {
316 self.req = self.req.header(name, value);
317 self
318 }
319}
320
321#[derive(thiserror::Error, Debug)]
323#[allow(clippy::large_enum_variant)] pub enum DefaultClientError {
325 #[error("invalid UTF-8 string")]
327 Utf8(#[from] std::str::Utf8Error),
328
329 #[error("I/O error: {0}")]
331 #[allow(clippy::upper_case_acronyms)]
332 IO(#[from] std::io::Error),
333
334 #[error(transparent)]
336 Request(#[from] RequestError),
337}
338
339macro_rules! wrap_error {
340 ($e:ty) => {
341 impl From<$e> for crate::Error {
342 fn from(e: $e) -> Self {
343 Self::HttpClient(Box::new(DefaultClientError::from(e)))
344 }
345 }
346 };
347}
348
349wrap_error!(std::io::Error);
350wrap_error!(std::str::Utf8Error);
351wrap_error!(RequestError);
352
353pub struct RequestError {
358 inner: ureq::Error,
359}
360
361impl std::fmt::Display for RequestError {
362 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363 <ureq::Error as std::fmt::Display>::fmt(&self.inner, f)
364 }
365}
366
367impl std::fmt::Debug for RequestError {
368 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369 <ureq::Error as std::fmt::Debug>::fmt(&self.inner, f)
370 }
371}
372
373impl std::error::Error for RequestError {
374 fn cause(&self) -> Option<&dyn std::error::Error> {
375 Some(&self.inner)
376 }
377}