1use http_signature_normalization::create::Signed;
2use httpdate::HttpDate;
3use reqwest::{
4 header::{InvalidHeaderValue, ToStrError},
5 Request, RequestBuilder,
6};
7use std::{
8 convert::TryInto,
9 fmt::Display,
10 time::{Duration, SystemTime},
11};
12
13pub use http_signature_normalization::RequiredError;
14
15#[cfg(feature = "digest")]
16pub mod digest;
17
18pub mod prelude {
19 pub use crate::{Config, Sign, SignError};
20
21 #[cfg(feature = "default-spawner")]
22 pub use crate::default_spawner::DefaultSpawner;
23
24 #[cfg(feature = "digest")]
25 pub use crate::digest::{DigestCreate, SignExt};
26}
27
28#[cfg(feature = "default-spawner")]
29pub use default_spawner::DefaultSpawner;
30
31#[cfg(feature = "default-spawner")]
32#[derive(Clone, Debug, Default)]
33pub struct Config<Spawner = DefaultSpawner> {
38 config: http_signature_normalization::Config,
40
41 set_host: bool,
43
44 set_date: bool,
46
47 spawner: Spawner,
49}
50
51#[cfg(not(feature = "default-spawner"))]
52#[derive(Clone, Debug, Default)]
53pub struct Config<Spawner> {
58 config: http_signature_normalization::Config,
60
61 set_host: bool,
63
64 set_date: bool,
66
67 spawner: Spawner,
69}
70
71#[cfg(feature = "default-spawner")]
72mod default_spawner {
73 use super::{Canceled, Config, Spawn};
74
75 impl Config<DefaultSpawner> {
76 pub fn new() -> Self {
78 Default::default()
79 }
80 }
81
82 #[derive(Clone, Copy, Debug, Default)]
84 pub struct DefaultSpawner;
85
86 pub struct DefaultSpawnerFuture<Out> {
89 inner: tokio::task::JoinHandle<Out>,
90 }
91
92 impl Spawn for DefaultSpawner {
93 type Future<T>
94 = DefaultSpawnerFuture<T>
95 where
96 T: Send;
97
98 fn spawn_blocking<Func, Out>(&self, func: Func) -> Self::Future<Out>
99 where
100 Func: FnOnce() -> Out + Send + 'static,
101 Out: Send + 'static,
102 {
103 DefaultSpawnerFuture {
104 inner: tokio::task::spawn_blocking(func),
105 }
106 }
107 }
108
109 impl<Out> std::future::Future for DefaultSpawnerFuture<Out> {
110 type Output = Result<Out, Canceled>;
111
112 fn poll(
113 mut self: std::pin::Pin<&mut Self>,
114 cx: &mut std::task::Context<'_>,
115 ) -> std::task::Poll<Self::Output> {
116 let res = std::task::ready!(std::pin::Pin::new(&mut self.inner).poll(cx));
117
118 std::task::Poll::Ready(res.map_err(|_| Canceled))
119 }
120 }
121}
122
123#[derive(Debug)]
125pub struct Canceled;
126
127impl std::fmt::Display for Canceled {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 write!(f, "Operation was canceled")
130 }
131}
132
133impl std::error::Error for Canceled {}
134
135pub trait Spawn {
139 type Future<T>: std::future::Future<Output = Result<T, Canceled>> + Send
141 where
142 T: Send;
143
144 fn spawn_blocking<Func, Out>(&self, func: Func) -> Self::Future<Out>
146 where
147 Func: FnOnce() -> Out + Send + 'static,
148 Out: Send + 'static;
149}
150
151#[async_trait::async_trait]
153pub trait Sign {
154 async fn authorization_signature<F, E, K, S>(
156 self,
157 config: &Config<S>,
158 key_id: K,
159 f: F,
160 ) -> Result<Request, E>
161 where
162 Self: Sized,
163 F: FnOnce(&str) -> Result<String, E> + Send + 'static,
164 E: From<SignError> + From<reqwest::Error> + Send + 'static,
165 K: Display + Send,
166 S: Spawn + Send + Sync;
167
168 async fn signature<F, E, K, S>(self, config: &Config<S>, key_id: K, f: F) -> Result<Request, E>
170 where
171 Self: Sized,
172 F: FnOnce(&str) -> Result<String, E> + Send + 'static,
173 E: From<SignError> + From<reqwest::Error> + Send + 'static,
174 K: Display + Send,
175 S: Spawn + Send + Sync;
176}
177
178#[derive(Debug)]
179pub enum SignError {
180 Header(ToStrError),
182
183 NewHeader(InvalidHeaderValue),
185
186 RequiredError(RequiredError),
188
189 Host(String),
191
192 BodyPresent,
194
195 Canceled,
197}
198
199impl std::fmt::Display for SignError {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 match self {
202 Self::Header(_) => f.write_str("Failed to read header"),
203 Self::NewHeader(_) => f.write_str("Failed to write header"),
204 Self::RequiredError(_) => f.write_str("Missing required field"),
205 Self::Host(s) => write!(f, "No host provided for URL, {s}"),
206 Self::BodyPresent => f.write_str("Cannog sign request with body already present"),
207 Self::Canceled => f.write_str("Panic in spawn blocking"),
208 }
209 }
210}
211
212impl std::error::Error for SignError {
213 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
214 match self {
215 Self::Header(h) => Some(h),
216 Self::NewHeader(n) => Some(n),
217 Self::RequiredError(r) => Some(r),
218 Self::Host(_) | Self::BodyPresent | Self::Canceled => None,
219 }
220 }
221}
222
223impl<Spawner> Config<Spawner> {
224 pub fn new_with_spawner(spawner: Spawner) -> Self {
226 Config {
227 config: Default::default(),
228 set_host: Default::default(),
229 set_date: Default::default(),
230 spawner,
231 }
232 }
233
234 pub fn set_host_header(self) -> Self {
237 Config {
238 config: self.config,
239 set_host: true,
240 set_date: self.set_date,
241 spawner: self.spawner,
242 }
243 }
244
245 pub fn mastodon_compat(self) -> Self {
250 Config {
251 config: self.config.mastodon_compat(),
252 set_host: true,
253 set_date: true,
254 spawner: self.spawner,
255 }
256 }
257
258 pub fn require_digest(self) -> Self {
262 Config {
263 config: self.config.require_digest(),
264 set_host: self.set_host,
265 set_date: self.set_date,
266 spawner: self.spawner,
267 }
268 }
269
270 pub fn dont_use_created_field(self) -> Self {
275 Config {
276 config: self.config.dont_use_created_field(),
277 set_host: self.set_host,
278 set_date: self.set_date,
279 spawner: self.spawner,
280 }
281 }
282
283 pub fn set_expiration(self, expiries_after: Duration) -> Self {
285 Config {
286 config: self.config.set_expiration(expiries_after),
287 set_host: self.set_host,
288 set_date: self.set_date,
289 spawner: self.spawner,
290 }
291 }
292
293 pub fn require_header(self, header: &str) -> Self {
295 Config {
296 config: self.config.require_header(header),
297 set_host: self.set_host,
298 set_date: self.set_date,
299 spawner: self.spawner,
300 }
301 }
302
303 pub fn set_spawner<NewSpawner: Spawn>(self, spawner: NewSpawner) -> Config<NewSpawner> {
304 Config {
305 config: self.config,
306 set_host: self.set_host,
307 set_date: self.set_date,
308 spawner,
309 }
310 }
311}
312
313#[async_trait::async_trait]
314impl Sign for RequestBuilder {
315 async fn authorization_signature<F, E, K, S>(
316 self,
317 config: &Config<S>,
318 key_id: K,
319 f: F,
320 ) -> Result<Request, E>
321 where
322 F: FnOnce(&str) -> Result<String, E> + Send + 'static,
323 E: From<SignError> + From<reqwest::Error> + Send + 'static,
324 K: Display + Send,
325 S: Spawn + Send + Sync,
326 {
327 let mut request = self.build()?;
328 let signed = prepare(&mut request, config, key_id, f).await?;
329
330 let auth_header = signed.authorization_header();
331 request.headers_mut().insert(
332 "Authorization",
333 auth_header.parse().map_err(SignError::NewHeader)?,
334 );
335
336 Ok(request)
337 }
338
339 async fn signature<F, E, K, S>(self, config: &Config<S>, key_id: K, f: F) -> Result<Request, E>
340 where
341 F: FnOnce(&str) -> Result<String, E> + Send + 'static,
342 E: From<SignError> + From<reqwest::Error> + Send + 'static,
343 K: Display + Send,
344 S: Spawn + Send + Sync,
345 {
346 let mut request = self.build()?;
347 let signed = prepare(&mut request, config, key_id, f).await?;
348
349 let sig_header = signed.signature_header();
350
351 request.headers_mut().insert(
352 "Signature",
353 sig_header.parse().map_err(SignError::NewHeader)?,
354 );
355
356 Ok(request)
357 }
358}
359
360async fn prepare<F, E, K, S>(
361 req: &mut Request,
362 config: &Config<S>,
363 key_id: K,
364 f: F,
365) -> Result<Signed, E>
366where
367 F: FnOnce(&str) -> Result<String, E> + Send + 'static,
368 E: From<SignError> + Send + 'static,
369 K: Display + Send,
370 S: Spawn,
371{
372 if config.set_date && !req.headers().contains_key("date") {
373 req.headers_mut().insert(
374 "date",
375 HttpDate::from(SystemTime::now())
376 .to_string()
377 .try_into()
378 .map_err(SignError::NewHeader)?,
379 );
380 }
381 let mut bt = std::collections::BTreeMap::new();
382 for (k, v) in req.headers().iter() {
383 bt.insert(
384 k.as_str().to_owned(),
385 v.to_str().map_err(SignError::Header)?.to_owned(),
386 );
387 }
388 if config.set_host {
389 let header_string = req
390 .url()
391 .host()
392 .ok_or_else(|| SignError::Host(req.url().to_string()))?
393 .to_string();
394
395 let header_string = match req.url().port() {
396 None | Some(443) | Some(80) => header_string,
397 Some(port) => format!("{}:{}", header_string, port),
398 };
399
400 bt.insert("Host".to_string(), header_string);
401 }
402 let path_and_query = if let Some(query) = req.url().query() {
403 format!("{}?{}", req.url().path(), query)
404 } else {
405 req.url().path().to_string()
406 };
407 let unsigned = config
408 .config
409 .begin_sign(req.method().as_str(), &path_and_query, bt)
410 .map_err(SignError::RequiredError)?;
411
412 let key_string = key_id.to_string();
413 let signed = config
414 .spawner
415 .spawn_blocking(move || unsigned.sign(key_string, f))
416 .await
417 .map_err(|_| SignError::Canceled)??;
418 Ok(signed)
419}
420
421#[cfg(feature = "middleware")]
422mod middleware {
423 use super::{prepare, Config, Sign, SignError, Spawn};
424 use reqwest::Request;
425 use reqwest_middleware::RequestBuilder;
426 use std::fmt::Display;
427
428 #[async_trait::async_trait]
429 impl Sign for RequestBuilder {
430 async fn authorization_signature<F, E, K, S>(
431 self,
432 config: &Config<S>,
433 key_id: K,
434 f: F,
435 ) -> Result<Request, E>
436 where
437 F: FnOnce(&str) -> Result<String, E> + Send + 'static,
438 E: From<SignError> + From<reqwest::Error> + Send + 'static,
439 K: Display + Send,
440 S: Spawn + Send + Sync,
441 {
442 let mut request = self.build()?;
443 let signed = prepare(&mut request, config, key_id, f).await?;
444
445 let auth_header = signed.authorization_header();
446 request.headers_mut().insert(
447 "Authorization",
448 auth_header.parse().map_err(SignError::NewHeader)?,
449 );
450
451 Ok(request)
452 }
453
454 async fn signature<F, E, K, S>(
455 self,
456 config: &Config<S>,
457 key_id: K,
458 f: F,
459 ) -> Result<Request, E>
460 where
461 F: FnOnce(&str) -> Result<String, E> + Send + 'static,
462 E: From<SignError> + From<reqwest::Error> + Send + 'static,
463 K: Display + Send,
464 S: Spawn + Send + Sync,
465 {
466 let mut request = self.build()?;
467 let signed = prepare(&mut request, config, key_id, f).await?;
468
469 let sig_header = signed.signature_header();
470
471 request.headers_mut().insert(
472 "Signature",
473 sig_header.parse().map_err(SignError::NewHeader)?,
474 );
475
476 Ok(request)
477 }
478 }
479}