1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
use std::convert::TryInto; use http::header::{HeaderName, HeaderValue}; use super::*; impl ClientRequestLike for reqwest::Request { fn host(&self) -> Option<String> { self.url().host_str().map(Into::into) } fn header(&self, header: &Header) -> Option<HeaderValue> { match header { Header::Normal(header_name) => self.headers().get(header_name).cloned(), Header::Pseudo(PseudoHeader::RequestTarget) => { let method = self.method().as_str().to_ascii_lowercase(); let path = self.url().path(); format!("{} {}", method, path).try_into().ok() } } } fn compute_digest(&mut self, digest: &dyn HttpDigest) -> Option<String> { self.body()?.as_bytes().map(|b| digest.http_digest(b)) } fn set_header(&mut self, header: HeaderName, value: HeaderValue) { self.headers_mut().insert(header, value); } } impl ClientRequestLike for reqwest::blocking::Request { fn host(&self) -> Option<String> { self.url().host_str().map(Into::into) } fn header(&self, header: &Header) -> Option<HeaderValue> { match header { Header::Normal(header_name) => self.headers().get(header_name).cloned(), Header::Pseudo(PseudoHeader::RequestTarget) => { let method = self.method().as_str().to_ascii_lowercase(); let path = self.url().path(); format!("{} {}", method, path).try_into().ok() } } } fn compute_digest(&mut self, _digest: &dyn HttpDigest) -> Option<String> { None } fn set_header(&mut self, header: HeaderName, value: HeaderValue) { self.headers_mut().insert(header, value); } } #[cfg(test)] mod tests { use chrono::{offset::TimeZone, Utc}; use http::header::{AUTHORIZATION, CONTENT_TYPE, DATE}; use super::*; #[test] fn it_works() { let config = SigningConfig::new_default("test_key", "abcdefgh".as_bytes()); let client = reqwest::Client::new(); let without_sig = client .post("http://test.com/foo/bar") .header(CONTENT_TYPE, "application/json") .header( DATE, Utc.ymd(2014, 7, 8) .and_hms(9, 10, 11) .format("%a, %d %b %Y %T GMT") .to_string(), ) .body(&br#"{ "x": 1, "y": 2}"#[..]) .build() .unwrap(); let with_sig = without_sig.signed(&config).unwrap(); assert_eq!(with_sig.headers().get(AUTHORIZATION).unwrap(), "Signature keyId=\"test_key\",algorithm=\"hmac-sha256\",signature=\"uH2I9FSuCGUrIEygs7hR29oz0Afkz0bZyHpz6cW/mLQ=\",headers=\"(request-target) date digest host"); assert_eq!( with_sig .headers() .get(HeaderName::from_static("digest")) .unwrap(), "SHA-256=2vgEVkfe4d6VW+tSWAziO7BUx7uT/rA9hn1EoxUJi2o=" ); } #[test] #[ignore] fn it_can_talk_to_reference_integration() { let config = SigningConfig::new_default("dummykey", &base64::decode("dummykey").unwrap()); let client = reqwest::blocking::Client::new(); let req = client .get("http://localhost:8080/config") .build() .unwrap() .signed(&config) .unwrap(); let result = client.execute(req).unwrap(); println!("{:?}", result.text().unwrap()); } }