http_sig/
rouille_impls.rs1use std::convert::TryInto;
2use std::fmt::{self, Debug};
3use std::io::{self, Cursor, Read};
4use std::mem;
5
6use http::header::HeaderValue;
7
8use super::*;
9
10pub struct RouilleBody<'a>(RouilleBodyInner<'a>);
16
17enum RouilleBodyInner<'a> {
18 Digested(Result<Cursor<Vec<u8>>, io::Error>),
19 Undigested(rouille::RequestBody<'a>),
20}
21
22impl Debug for RouilleBody<'_> {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 f.write_str("RouilleBody { .. }")
25 }
26}
27
28impl Read for RouilleBody<'_> {
29 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
30 use RouilleBodyInner::*;
31 match self {
32 RouilleBody(Digested(Ok(x))) => x.read(buf),
33 RouilleBody(Digested(Err(e))) => Err(mem::replace(e, io::ErrorKind::Other.into())),
34 RouilleBody(Undigested(x)) => x.read(buf),
35 }
36 }
37}
38
39impl RequestLike for rouille::Request {
40 fn header(&self, header: &Header) -> Option<HeaderValue> {
41 match header {
42 Header::Normal(header) => rouille::Request::header(self, header.as_str())
43 .and_then(|v| HeaderValue::from_str(v).ok()),
44 Header::Pseudo(PseudoHeader::RequestTarget) => {
45 let method = self.method().to_ascii_lowercase();
46 let path = self.raw_url();
47 format!("{} {}", method, path).try_into().ok()
48 }
49 _ => None,
50 }
51 }
52}
53impl<'a> ServerRequestLike for &'a rouille::Request {
54 type Remnant = Option<RouilleBody<'a>>;
55
56 fn complete_with_digest(self, digest: &dyn HttpDigest) -> (Option<String>, Self::Remnant) {
57 if let Some(mut body) = self.data() {
58 let mut result = Vec::new();
59 if let Err(e) = body.read_to_end(&mut result) {
60 (None, Some(RouilleBody(RouilleBodyInner::Digested(Err(e)))))
61 } else if result.is_empty() {
62 (None, None)
63 } else {
64 let computed_digest = digest.http_digest(&result);
65 (
66 Some(computed_digest),
67 Some(RouilleBody(RouilleBodyInner::Digested(Ok(Cursor::new(
68 result,
69 ))))),
70 )
71 }
72 } else {
73 (None, None)
74 }
75 }
76 fn complete(self) -> Self::Remnant {
77 self.data()
78 .map(RouilleBodyInner::Undigested)
79 .map(RouilleBody)
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use std::sync::Arc;
86
87 use chrono::{offset::TimeZone, Utc};
88
89 use super::*;
90
91 #[test]
92 fn can_verify_post_request() {
93 let key_provider = SimpleKeyProvider::new(vec![(
94 "test_key",
95 Arc::new(DefaultSignatureAlgorithm::new("abcdefgh".as_bytes()))
96 as Arc<dyn HttpSignatureVerify>,
97 )]);
98 let config = VerifyingConfig::new(key_provider).with_validate_date(false);
99
100 let request = rouille::Request::fake_http(
101 "POST",
102 "/foo/bar",
103 vec![
104 ("Host".into(), "test.com".into()),
105 ("ContentType".into(), "application/json".into()),
106 ("Date".into(), Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).single().expect("valid date")
107 .format("%a, %d %b %Y %T GMT")
108 .to_string()),
109 ("Digest".into(), "SHA-256=2vgEVkfe4d6VW+tSWAziO7BUx7uT/rA9hn1EoxUJi2o=".into()),
110 ("Authorization".into(), "Signature keyId=\"test_key\",algorithm=\"hmac-sha256\",signature=\"uH2I9FSuCGUrIEygs7hR29oz0Afkz0bZyHpz6cW/mLQ=\",headers=\"(request-target) date digest host".into()),
111 ],
112 br#"{ "x": 1, "y": 2}"#[..].into(),
113 );
114
115 request.verify(&config).unwrap();
116 }
117
118 #[test]
119 fn can_verify_get_request() {
120 let key_provider = SimpleKeyProvider::new(vec![(
121 "test_key",
122 Arc::new(DefaultSignatureAlgorithm::new("abcdefgh".as_bytes()))
123 as Arc<dyn HttpSignatureVerify>,
124 )]);
125 let config = VerifyingConfig::new(key_provider).with_validate_date(false);
126
127 let request = rouille::Request::fake_http(
128 "GET",
129 "/foo/bar",
130 vec![
131 ("Date".into(), Utc.with_ymd_and_hms(2014, 7, 8,
132 9, 10, 11).single().expect("valid date")
133 .format("%a, %d %b %Y %T GMT")
134 .to_string()),
135 ("Authorization".into(), "Signature keyId=\"test_key\",algorithm=\"hmac-sha256\",signature=\"sGQ3hA9KB40CU1pHbRLXLvLdUWYn+c3fcfL+Sw8kIZE=\",headers=\"(request-target) date".into()),
136 ],
137 Vec::new(),
138 );
139
140 request.verify(&config).unwrap();
141 }
142}