Update actix client to automatically set Date header in masto compat

This commit is contained in:
asonix 2022-11-22 18:13:25 -06:00
parent 82b59317cb
commit efc55fd9a1
4 changed files with 36 additions and 13 deletions

View file

@ -2,7 +2,6 @@ use actix_rt::task::JoinError;
use awc::Client; use awc::Client;
use http_signature_normalization_actix::prelude::*; use http_signature_normalization_actix::prelude::*;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::time::SystemTime;
use tracing::{error, info}; use tracing::{error, info};
use tracing_error::ErrorLayer; use tracing_error::ErrorLayer;
use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
@ -14,7 +13,6 @@ async fn request(config: Config) -> Result<(), Box<dyn std::error::Error>> {
.post("http://127.0.0.1:8010/") .post("http://127.0.0.1:8010/")
.append_header(("User-Agent", "Actix Web")) .append_header(("User-Agent", "Actix Web"))
.append_header(("Accept", "text/plain")) .append_header(("Accept", "text/plain"))
.insert_header(actix_web::http::header::Date(SystemTime::now().into()))
.signature_with_digest(config, "my-key-id", digest, "Hewwo-owo", |s| { .signature_with_digest(config, "my-key-id", digest, "Hewwo-owo", |s| {
info!("Signing String\n{}", s); info!("Signing String\n{}", s);
Ok(base64::encode(s)) as Result<_, MyError> Ok(base64::encode(s)) as Result<_, MyError>

View file

@ -1,4 +1,4 @@
use actix_http::{header::InvalidHeaderValue}; use actix_http::header::InvalidHeaderValue;
use actix_rt::task::JoinError; use actix_rt::task::JoinError;
use awc::ClientRequest; use awc::ClientRequest;
use std::{fmt::Display, future::Future, pin::Pin}; use std::{fmt::Display, future::Future, pin::Pin};

View file

@ -273,6 +273,9 @@ pub struct Config {
/// Whether to set the Host header /// Whether to set the Host header
set_host: bool, set_host: bool,
/// Whether to set the Date header
set_date: bool,
} }
#[cfg(feature = "client")] #[cfg(feature = "client")]
@ -324,17 +327,21 @@ mod client {
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
/// An error when preparing to sign a request /// An error when preparing to sign a request
pub enum PrepareSignError { pub enum PrepareSignError {
#[error("Failed to read header, {0}")] #[error("Failed to read header")]
/// An error occurred when reading the request's headers /// An error occurred when reading the request's headers
Header(#[from] ToStrError), Header(#[from] ToStrError),
#[error("{0}")] #[error("Missing required header")]
/// Some headers were marked as required, but are missing /// Some headers were marked as required, but are missing
RequiredError(#[from] RequiredError), RequiredError(#[from] RequiredError),
#[error("No host provided for URL, {0}")] #[error("No host provided for URL, {0}")]
/// Missing host /// Missing host
Host(String), Host(String),
#[error("Failed to set header")]
/// Invalid Date header
InvalidHeader(#[from] actix_http::header::InvalidHeaderValue),
} }
} }
@ -421,6 +428,7 @@ impl Config {
Config { Config {
config: self.config, config: self.config,
set_host: true, set_host: true,
set_date: self.set_date,
} }
} }
@ -432,6 +440,7 @@ impl Config {
Config { Config {
config: self.config.mastodon_compat(), config: self.config.mastodon_compat(),
set_host: true, set_host: true,
set_date: true,
} }
} }
@ -442,6 +451,7 @@ impl Config {
Config { Config {
config: self.config.require_digest(), config: self.config.require_digest(),
set_host: self.set_host, set_host: self.set_host,
set_date: self.set_date,
} }
} }
@ -453,6 +463,7 @@ impl Config {
Config { Config {
config: self.config.dont_use_created_field(), config: self.config.dont_use_created_field(),
set_host: self.set_host, set_host: self.set_host,
set_date: self.set_date,
} }
} }
@ -461,6 +472,7 @@ impl Config {
Config { Config {
config: self.config.set_expiration(expires_after), config: self.config.set_expiration(expires_after),
set_host: self.set_host, set_host: self.set_host,
set_date: self.set_date,
} }
} }
@ -469,6 +481,7 @@ impl Config {
Config { Config {
config: self.config.require_header(header), config: self.config.require_header(header),
set_host: self.set_host, set_host: self.set_host,
set_date: self.set_date,
} }
} }

View file

@ -1,9 +1,10 @@
use actix_http::{header::InvalidHeaderValue};
use awc::ClientRequest;
use actix_rt::task::JoinError;
use std::{fmt::Display, future::Future, pin::Pin};
use crate::{create::Signed, Config, PrepareSignError, Sign}; use crate::{create::Signed, Config, PrepareSignError, Sign};
use actix_rt::task::JoinError;
use awc::{
http::header::{HttpDate, InvalidHeaderValue, TryIntoHeaderValue},
ClientRequest,
};
use std::{fmt::Display, future::Future, pin::Pin, time::SystemTime};
impl Sign for ClientRequest { impl Sign for ClientRequest {
fn authorization_signature<F, E, K>( fn authorization_signature<F, E, K>(
@ -24,7 +25,7 @@ impl Sign for ClientRequest {
Self: Sized, Self: Sized,
{ {
Box::pin(async move { Box::pin(async move {
let signed = prepare(&self, &config, key_id, f).await?; let signed = prepare(&mut self, &config, key_id, f).await?;
signed.authorization_header(self.headers_mut())?; signed.authorization_header(self.headers_mut())?;
Ok(self) Ok(self)
}) })
@ -48,7 +49,7 @@ impl Sign for ClientRequest {
Self: Sized, Self: Sized,
{ {
Box::pin(async move { Box::pin(async move {
let signed = prepare(&self, &config, key_id, f).await?; let signed = prepare(&mut self, &config, key_id, f).await?;
signed.signature_header(self.headers_mut())?; signed.signature_header(self.headers_mut())?;
Ok(self) Ok(self)
}) })
@ -56,7 +57,7 @@ impl Sign for ClientRequest {
} }
async fn prepare<F, E, K>( async fn prepare<F, E, K>(
request: &ClientRequest, request: &mut ClientRequest,
config: &Config, config: &Config,
key_id: K, key_id: K,
f: F, f: F,
@ -66,6 +67,17 @@ where
E: From<JoinError> + From<PrepareSignError> + std::fmt::Debug + Send + 'static, E: From<JoinError> + From<PrepareSignError> + std::fmt::Debug + Send + 'static,
K: Display, K: Display,
{ {
if config.set_date {
if !request.headers().contains_key("date") {
request.headers_mut().insert(
actix_http::header::DATE,
HttpDate::from(SystemTime::now())
.try_into_value()
.expect("Date is valid"),
);
}
}
let mut headers = request.headers().clone(); let mut headers = request.headers().clone();
if config.set_host { if config.set_host {
let header_string = request let header_string = request