Skip to content

Commit

Permalink
feat: Support signing http_types::Request (#72)
Browse files Browse the repository at this point in the history
* feat: Support signing http_types::Request

Signed-off-by: Xuanwo <github@xuanwo.io>

* Export singable request in top level

Signed-off-by: Xuanwo <github@xuanwo.io>
  • Loading branch information
Xuanwo authored May 31, 2022
1 parent 985cc20 commit 4219d16
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 40 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ version = "0.0.5"
all-features = true

[features]
reqwest_request = ["reqwest"]
http_types_request = ["http-types"]
reqwest_blocking_request = ["reqwest/blocking"]
reqwest_request = ["reqwest"]

[[bench]]
harness = false
Expand All @@ -29,6 +30,7 @@ form_urlencoded = "1"
hex = "0.4"
hmac = "0.12"
http = "0.2"
http-types = { version = "2.12.0", optional = true }
isahc = "1.7.2"
jsonwebtoken = "8.0.1"
log = "0.4"
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,16 @@
//!
//! - `reqwest_request`: Enable to support signing [`reqwest::Request`]
//! - `reqwest_blocking_request`: Enable to support signing [`reqwest::blocking::Request`]
//! - `http_types_request`: Enable to support signing [`http_types::Request`]
// Make sure all our public APIs have docs.
#![warn(missing_docs)]

pub mod request;
pub mod services;
pub mod time;

pub(crate) mod dirs;
pub(crate) mod hash;
pub mod time;

mod request;
pub use request::SignableRequest;
111 changes: 86 additions & 25 deletions src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ use http::{HeaderMap, HeaderValue, Method};
/// uri components instead of a complete struct.
pub trait SignableRequest {
/// Get method of request.
fn method(&self) -> &Method;
fn method(&self) -> Method;
/// Get header of request.
fn headers(&self) -> &HeaderMap;
fn headers(&self) -> HeaderMap;

/// Get path of request.
///
Expand Down Expand Up @@ -72,16 +72,16 @@ pub trait SignableRequest {
fn apply_header(&mut self, name: HeaderName, value: &str) -> Result<()>;
}

/// Implement `SignableRequest` for `http::Request`
/// Implement `SignableRequest` for [`http::Request`]
impl<T> SignableRequest for http::Request<T> {
fn method(&self) -> &Method {
fn method(&self) -> Method {
let this = self as &http::Request<T>;
this.method()
this.method().clone()
}

fn headers(&self) -> &HeaderMap {
fn headers(&self) -> HeaderMap {
let this = self as &http::Request<T>;
this.headers()
this.headers().clone()
}

fn path(&self) -> &str {
Expand Down Expand Up @@ -113,21 +113,17 @@ impl<T> SignableRequest for http::Request<T> {
}
}

/// Implement `SignableRequest` for `reqwest::Request`
///
/// # TODO
///
/// Make this under feature so that we don't need to depend on reqwest directly.
/// Implement `SignableRequest` for [`reqwest::Request`]
#[cfg(feature = "reqwest_request")]
impl SignableRequest for reqwest::Request {
fn method(&self) -> &Method {
fn method(&self) -> Method {
let this = self as &reqwest::Request;
this.method()
this.method().clone()
}

fn headers(&self) -> &HeaderMap {
fn headers(&self) -> HeaderMap {
let this = self as &reqwest::Request;
this.headers()
this.headers().clone()
}

fn path(&self) -> &str {
Expand Down Expand Up @@ -159,21 +155,17 @@ impl SignableRequest for reqwest::Request {
}
}

/// Implement `SignableRequest` for `reqwest::blocking::Request`
///
/// # TODO
///
/// Make this under feature so that we don't need to depend on reqwest directly.
/// Implement `SignableRequest` for [`reqwest::blocking::Request`]
#[cfg(feature = "reqwest_blocking_request")]
impl SignableRequest for reqwest::blocking::Request {
fn method(&self) -> &Method {
fn method(&self) -> Method {
let this = self as &reqwest::blocking::Request;
this.method()
this.method().clone()
}

fn headers(&self) -> &HeaderMap {
fn headers(&self) -> HeaderMap {
let this = self as &reqwest::blocking::Request;
this.headers()
this.headers().clone()
}

fn path(&self) -> &str {
Expand Down Expand Up @@ -204,3 +196,72 @@ impl SignableRequest for reqwest::blocking::Request {
Ok(())
}
}

/// Implement `SignableRequest` for [`http_types::Request`]
#[cfg(feature = "http_types_request")]
impl SignableRequest for http_types::Request {
fn method(&self) -> Method {
use std::str::FromStr;

let this = self as &http_types::Request;
match this.method() {
http_types::Method::Connect => Method::CONNECT,
http_types::Method::Delete => Method::DELETE,
http_types::Method::Get => Method::GET,
http_types::Method::Head => Method::HEAD,
http_types::Method::Options => Method::OPTIONS,
http_types::Method::Patch => Method::PATCH,
http_types::Method::Post => Method::POST,
http_types::Method::Put => Method::PUT,
http_types::Method::Trace => Method::TRACE,
v => Method::from_str(v.as_ref()).expect("must be valid http method"),
}
}

fn headers(&self) -> HeaderMap {
use std::str::FromStr;

let this = self as &http_types::Request;
let mut map = HeaderMap::new();
for name in this.header_names() {
map.insert(
HeaderName::from_str(name.as_str()).expect("must be valid header name"),
HeaderValue::from_str(this.header(name).expect("header value must exist").as_str())
.expect("must be valid header value"),
);
}

map
}

fn path(&self) -> &str {
let this = self as &http_types::Request;
this.url().path()
}

fn query(&self) -> Option<&str> {
let this = self as &http_types::Request;
this.url().query()
}

fn host(&self) -> &str {
let this = self as &http_types::Request;
this.url().host_str().expect("request url must have host")
}

fn port(&self) -> Option<usize> {
let this = self as &http_types::Request;
this.url().port().map(|v| v as usize)
}

fn apply_header(&mut self, name: HeaderName, value: &str) -> Result<()> {
self.insert_header(
name.as_str(),
value
.parse::<http_types::headers::HeaderValue>()
.expect("header value must be valid"),
);

Ok(())
}
}
2 changes: 1 addition & 1 deletion src/services/aws/v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ impl Debug for Signer {

#[derive(Clone)]
struct CanonicalRequest<'a> {
method: &'a http::Method,
method: http::Method,
path: &'a str,
params: Option<String>,
headers: http::HeaderMap,
Expand Down
22 changes: 11 additions & 11 deletions src/services/azure/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,21 +259,21 @@ fn string_to_sign(req: &impl SignableRequest, cred: &Credential, now: DateTime)
let mut s = String::new();

writeln!(&mut s, "{}", req.method().as_str())?;
writeln!(&mut s, "{}", get_or_default(h, &CONTENT_ENCODING)?)?;
writeln!(&mut s, "{}", get_or_default(h, &CONTENT_LANGUAGE)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &CONTENT_ENCODING)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &CONTENT_LANGUAGE)?)?;
writeln!(
&mut s,
"{}",
get_or_default(h, &CONTENT_LENGTH).map(|v| if v == "0" { "" } else { v })?
get_or_default(&h, &CONTENT_LENGTH).map(|v| if v == "0" { "" } else { v })?
)?;
writeln!(&mut s, "{}", get_or_default(h, &CONTENT_MD5.parse()?)?)?;
writeln!(&mut s, "{}", get_or_default(h, &CONTENT_TYPE)?)?;
writeln!(&mut s, "{}", get_or_default(h, &DATE)?)?;
writeln!(&mut s, "{}", get_or_default(h, &IF_MODIFIED_SINCE)?)?;
writeln!(&mut s, "{}", get_or_default(h, &IF_MATCH)?)?;
writeln!(&mut s, "{}", get_or_default(h, &IF_NONE_MATCH)?)?;
writeln!(&mut s, "{}", get_or_default(h, &IF_UNMODIFIED_SINCE)?)?;
writeln!(&mut s, "{}", get_or_default(h, &RANGE)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &CONTENT_MD5.parse()?)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &CONTENT_TYPE)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &DATE)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &IF_MODIFIED_SINCE)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &IF_MATCH)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &IF_NONE_MATCH)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &IF_UNMODIFIED_SINCE)?)?;
writeln!(&mut s, "{}", get_or_default(&h, &RANGE)?)?;
writeln!(&mut s, "{}", canonicalize_header(req, now)?)?;
write!(&mut s, "{}", canonicalize_resource(req, cred))?;

Expand Down

1 comment on commit 4219d16

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for reqsign ready!

✅ Preview
https://reqsign-6u93zvu0o-xuanwo.vercel.app

Built with commit 4219d16.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.