1 1 | /*
|
2 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3 3 | * SPDX-License-Identifier: Apache-2.0
|
4 4 | */
|
5 5 |
|
6 6 | //! Functions shared between the tests of several modules.
|
7 7 |
|
8 - | use crate::http_request::{SignableBody, SignableRequest};
|
8 + | use crate::http_request::canonical_request::{CanonicalRequest, StringToSign};
|
9 + | use crate::http_request::{
|
10 + | PayloadChecksumKind, SessionTokenMode, SignableBody, SignableRequest, SignatureLocation,
|
11 + | SigningSettings,
|
12 + | };
|
13 + | use aws_credential_types::Credentials;
|
14 + | use aws_smithy_runtime_api::client::identity::Identity;
|
9 15 | use http0::{Method, Uri};
|
16 + | use std::borrow::Cow;
|
10 17 | use std::error::Error as StdError;
|
18 + | use std::time::{Duration, SystemTime};
|
19 + | use time::format_description::well_known::Rfc3339;
|
20 + | use time::OffsetDateTime;
|
21 + |
|
22 + | /// Common test suite collection
|
23 + | #[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
24 + | enum Collection {
|
25 + | V4,
|
26 + | V4A,
|
27 + | }
|
11 28 |
|
12 - | pub(crate) mod v4 {
|
13 - | use super::*;
|
29 + | /// A test from the common CRT test suite
|
30 + | #[derive(Debug, Clone)]
|
31 + | pub(crate) struct SigningSuiteTest {
|
32 + | test_name: &'static str,
|
33 + | collection: Collection,
|
34 + | }
|
14 35 |
|
15 - | fn path(name: &str, ext: &str) -> String {
|
16 - | format!("aws-sig-v4-test-suite/{}/{}.{}", name, name, ext)
|
36 + | impl SigningSuiteTest {
|
37 + | /// Create a new test from the V4 test suite
|
38 + | pub(crate) fn v4(test_name: &'static str) -> Self {
|
39 + | Self {
|
40 + | test_name,
|
41 + | collection: Collection::V4,
|
17 42 | }
|
18 - |
|
19 - | pub(crate) fn test_canonical_request(name: &str) -> String {
|
20 - | // Tests fail if there's a trailing newline in the file, and pre-commit requires trailing newlines
|
21 - | read(&path(name, "creq")).trim().to_string()
|
22 43 | }
|
23 44 |
|
24 - | pub(crate) fn test_sts(name: &str) -> String {
|
25 - | read(&path(name, "sts"))
|
45 + | /// Create a new test from the V4a test suite
|
46 + | pub(crate) fn v4a(test_name: &'static str) -> Self {
|
47 + | Self {
|
48 + | test_name,
|
49 + | collection: Collection::V4A,
|
26 50 | }
|
27 - |
|
28 - | pub(crate) fn test_request(name: &str) -> TestRequest {
|
29 - | test_parsed_request(name, "req")
|
30 51 | }
|
31 52 |
|
32 - | pub(crate) fn test_signed_request(name: &str) -> TestRequest {
|
33 - | test_parsed_request(name, "sreq")
|
53 + | /// Get the path to a file in this test suite directory
|
54 + | fn path(&self, filename: &str) -> String {
|
55 + | let dir = match self.collection {
|
56 + | Collection::V4 => "v4",
|
57 + | Collection::V4A => "v4a",
|
58 + | };
|
59 + | format!("aws-signing-test-suite/{dir}/{}/{filename}", self.test_name)
|
34 60 | }
|
35 61 |
|
36 - | pub(crate) fn test_signed_request_query_params(name: &str) -> TestRequest {
|
37 - | test_parsed_request(name, "qpsreq")
|
62 + | /// Get the HTTP request for the test
|
63 + | pub(crate) fn request(&self) -> TestRequest {
|
64 + | test_parsed_request(&self.path("request.txt"))
|
38 65 | }
|
39 66 |
|
40 - | fn test_parsed_request(name: &str, ext: &str) -> TestRequest {
|
41 - | let path = path(name, ext);
|
42 - | match parse_request(read(&path).as_bytes()) {
|
43 - | Ok(parsed) => parsed,
|
44 - | Err(err) => panic!("Failed to parse {}: {}", path, err),
|
67 + | /// Get the signed HTTP request for the test
|
68 + | pub(crate) fn signed_request(&self, signature_location: SignatureLocation) -> TestRequest {
|
69 + | match signature_location {
|
70 + | SignatureLocation::QueryParams => {
|
71 + | test_parsed_request(&self.path("query-signed-request.txt"))
|
45 72 | }
|
73 + | SignatureLocation::Headers => {
|
74 + | test_parsed_request(&self.path("header-signed-request.txt"))
|
46 75 | }
|
47 - |
|
48 - | #[test]
|
49 - | fn test_parse() {
|
50 - | test_request("post-header-key-case");
|
51 76 | }
|
52 - |
|
53 - | #[test]
|
54 - | fn test_read_query_params() {
|
55 - | test_request("get-vanilla-query-order-key-case");
|
56 77 | }
|
57 - | }
|
58 - |
|
59 - | #[cfg(feature = "sigv4a")]
|
60 - | pub(crate) mod v4a {
|
61 - | use super::*;
|
62 - | use crate::http_request::{
|
63 - | PayloadChecksumKind, SessionTokenMode, SignatureLocation, SigningSettings,
|
64 - | };
|
65 - | use aws_credential_types::Credentials;
|
66 - | use aws_smithy_runtime_api::client::identity::Identity;
|
67 - | use serde_derive::Deserialize;
|
68 - | use std::time::{Duration, SystemTime};
|
69 - | use time::format_description::well_known::Rfc3339;
|
70 - | use time::OffsetDateTime;
|
71 78 |
|
72 - | fn path(test_name: &str, definition_name: &str) -> String {
|
73 - | format!("aws-sig-v4a-test-suite/{test_name}/{definition_name}.txt")
|
79 + | /// Get the canonical request for the test
|
80 + | pub(crate) fn canonical_request(&self, signature_location: SignatureLocation) -> String {
|
81 + | match signature_location {
|
82 + | SignatureLocation::QueryParams => read(&self.path("query-canonical-request.txt")),
|
83 + | SignatureLocation::Headers => read(&self.path("header-canonical-request.txt")),
|
74 84 | }
|
75 - |
|
76 - | pub(crate) fn test_request(name: &str) -> TestRequest {
|
77 - | test_parsed_request(&path(name, "request"))
|
78 85 | }
|
79 86 |
|
80 - | pub(crate) fn test_canonical_request(
|
81 - | name: &str,
|
82 - | signature_location: SignatureLocation,
|
83 - | ) -> String {
|
87 + | /// Get the string to sign for the test
|
88 + | pub(crate) fn string_to_sign(&self, signature_location: SignatureLocation) -> String {
|
84 89 | match signature_location {
|
85 - | SignatureLocation::QueryParams => read(&path(name, "query-canonical-request")),
|
86 - | SignatureLocation::Headers => read(&path(name, "header-canonical-request")),
|
90 + | SignatureLocation::QueryParams => read(&self.path("query-string-to-sign.txt")),
|
91 + | SignatureLocation::Headers => read(&self.path("header-string-to-sign.txt")),
|
87 92 | }
|
88 93 | }
|
89 94 |
|
90 - | pub(crate) fn test_string_to_sign(name: &str, signature_location: SignatureLocation) -> String {
|
95 + | /// Get the signature for the test
|
96 + | pub(crate) fn signature(&self, signature_location: SignatureLocation) -> String {
|
91 97 | match signature_location {
|
92 - | SignatureLocation::QueryParams => read(&path(name, "query-string-to-sign")),
|
93 - | SignatureLocation::Headers => read(&path(name, "header-string-to-sign")),
|
98 + | SignatureLocation::QueryParams => read(&self.path("query-signature.txt")),
|
99 + | SignatureLocation::Headers => read(&self.path("header-signature.txt")),
|
100 + | }
|
94 101 | }
|
102 + |
|
103 + | /// Get the test context for the test
|
104 + | pub(crate) fn context(&self) -> TestContext {
|
105 + | let context = read(&self.path("context.json"));
|
106 + | let tc_builder: TestContextBuilder = serde_json::from_str(&context).unwrap();
|
107 + | tc_builder.build()
|
95 108 | }
|
109 + | }
|
96 110 |
|
97 - | fn test_parsed_request(path: &str) -> TestRequest {
|
111 + | fn test_parsed_request(path: &str) -> TestRequest {
|
98 112 | match parse_request(read(path).as_bytes()) {
|
99 113 | Ok(parsed) => parsed,
|
100 114 | Err(err) => panic!("Failed to parse {}: {}", path, err),
|
101 115 | }
|
102 - | }
|
116 + | }
|
103 117 |
|
104 - | pub(crate) fn test_context(test_name: &str) -> TestContext {
|
105 - | let path = format!("aws-sig-v4a-test-suite/{test_name}/context.json");
|
106 - | let context = read(&path);
|
107 - | let tc_builder: TestContextBuilder = serde_json::from_str(&context).unwrap();
|
108 - | tc_builder.build()
|
109 - | }
|
118 + | fn new_v4_signing_params_from_context(
|
119 + | test_context: &'_ TestContext,
|
120 + | signature_location: SignatureLocation,
|
121 + | ) -> crate::http_request::SigningParams<'_> {
|
122 + | let mut params = crate::sign::v4::SigningParams::from(test_context);
|
123 + | params.settings.signature_location = signature_location;
|
124 + | params.into()
|
125 + | }
|
126 + |
|
127 + | /// Run the given test from the v4 suite for both header and query
|
128 + | /// signature locations
|
129 + | pub(crate) fn run_test_suite_v4(test_name: &'static str) {
|
130 + | run_v4_test(test_name, SignatureLocation::Headers);
|
131 + | run_v4_test(test_name, SignatureLocation::QueryParams);
|
132 + | }
|
133 + |
|
134 + | fn assert_uri_eq(expected: &Uri, actual: &Uri) {
|
135 + | assert_eq!(expected.scheme(), actual.scheme());
|
136 + | assert_eq!(expected.authority(), actual.authority());
|
137 + | assert_eq!(expected.path(), actual.path());
|
138 + |
|
139 + | // query params may be out of order
|
140 + | let mut expected_params: Vec<(Cow<'_, str>, Cow<'_, str>)> =
|
141 + | form_urlencoded::parse(expected.query().unwrap_or_default().as_bytes()).collect();
|
142 + | expected_params.sort();
|
143 + |
|
144 + | let mut actual_params: Vec<(Cow<'_, str>, Cow<'_, str>)> =
|
145 + | form_urlencoded::parse(actual.query().unwrap_or_default().as_bytes()).collect();
|
146 + | actual_params.sort();
|
147 + |
|
148 + | assert_eq!(expected_params, actual_params);
|
149 + | }
|
110 150 |
|
111 - | pub(crate) struct TestContext {
|
151 + | fn assert_requests_eq(expected: TestRequest, actual: http0::Request<&str>) {
|
152 + | let expected = expected.as_http_request();
|
153 + | let actual = actual;
|
154 + | assert_eq!(expected.method(), actual.method());
|
155 + | assert_eq!(
|
156 + | expected.headers().len(),
|
157 + | actual.headers().len(),
|
158 + | "extra or missing headers"
|
159 + | );
|
160 + | assert_eq!(expected.headers(), actual.headers(), "headers mismatch");
|
161 + | assert_uri_eq(expected.uri(), actual.uri());
|
162 + | assert_eq!(*expected.body(), *actual.body(), "body mismatch");
|
163 + | }
|
164 + |
|
165 + | /// Run the given test from the v4 suite for the given signature location
|
166 + | pub(crate) fn run_v4_test(test_name: &'static str, signature_location: SignatureLocation) {
|
167 + | let test = SigningSuiteTest::v4(test_name);
|
168 + | let tc = test.context();
|
169 + | let params = new_v4_signing_params_from_context(&tc, signature_location);
|
170 + |
|
171 + | let req = test.request();
|
172 + | let expected_creq = test.canonical_request(signature_location);
|
173 + | let signable_req = SignableRequest::from(&req);
|
174 + | let actual_creq = CanonicalRequest::from(&signable_req, ¶ms).unwrap();
|
175 + |
|
176 + | // check canonical request
|
177 + | assert_eq!(
|
178 + | expected_creq,
|
179 + | actual_creq.to_string(),
|
180 + | "canonical request didn't match (signature location: {signature_location:?})"
|
181 + | );
|
182 + |
|
183 + | let expected_string_to_sign = test.string_to_sign(signature_location);
|
184 + | let hashed_creq = &crate::sign::v4::sha256_hex_string(actual_creq.to_string().as_bytes());
|
185 + | let actual_string_to_sign = StringToSign::new_v4(
|
186 + | *params.time(),
|
187 + | params.region().unwrap(),
|
188 + | params.name(),
|
189 + | hashed_creq,
|
190 + | )
|
191 + | .to_string();
|
192 + |
|
193 + | // check string to sign
|
194 + | assert_eq!(
|
195 + | expected_string_to_sign, actual_string_to_sign,
|
196 + | "'string to sign' didn't match (signature location: {signature_location:?})"
|
197 + | );
|
198 + |
|
199 + | let out = crate::http_request::sign(signable_req, ¶ms).unwrap();
|
200 + | let mut signed = req.as_http_request();
|
201 + | out.output.apply_to_request_http0x(&mut signed);
|
202 + |
|
203 + | // check signature
|
204 + | assert_eq!(
|
205 + | test.signature(signature_location),
|
206 + | out.signature,
|
207 + | "signature didn't match (signature location: {signature_location:?})"
|
208 + | );
|
209 + |
|
210 + | let expected = test.signed_request(signature_location);
|
211 + | assert_requests_eq(expected, signed);
|
212 + | }
|
213 + |
|
214 + | /// Test suite context.json
|
215 + | pub(crate) struct TestContext {
|
112 216 | pub(crate) identity: Identity,
|
113 217 | pub(crate) expiration_in_seconds: u64,
|
114 218 | pub(crate) normalize: bool,
|
115 219 | pub(crate) region: String,
|
116 220 | pub(crate) service: String,
|
117 221 | pub(crate) timestamp: String,
|
118 222 | pub(crate) omit_session_token: bool,
|
119 223 | pub(crate) sign_body: bool,
|
120 - | }
|
121 - |
|
122 - | impl<'a> From<&'a TestContext> for crate::sign::v4a::SigningParams<'a, SigningSettings> {
|
123 - | fn from(tc: &'a TestContext) -> Self {
|
124 - | crate::sign::v4a::SigningParams {
|
125 - | identity: &tc.identity,
|
126 - | region_set: &tc.region,
|
127 - | name: &tc.service,
|
128 - | time: OffsetDateTime::parse(&tc.timestamp, &Rfc3339)
|
129 - | .unwrap()
|
130 - | .into(),
|
131 - | settings: SigningSettings {
|
132 - | // payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
|
133 - | expires_in: Some(Duration::from_secs(tc.expiration_in_seconds)),
|
134 - | uri_path_normalization_mode: tc.normalize.into(),
|
135 - | session_token_mode: if tc.omit_session_token {
|
136 - | SessionTokenMode::Exclude
|
137 - | } else {
|
138 - | SessionTokenMode::Include
|
139 - | },
|
140 - | payload_checksum_kind: if tc.sign_body {
|
141 - | PayloadChecksumKind::XAmzSha256
|
142 - | } else {
|
143 - | PayloadChecksumKind::NoHeader
|
144 - | },
|
145 - | ..Default::default()
|
146 - | },
|
147 - | }
|
148 - | }
|
149 - | }
|
224 + | }
|
150 225 |
|
151 - | // Serde has limitations requiring this odd workaround.
|
152 - | // See https://github.com/serde-rs/serde/issues/368 for more info.
|
153 - | fn return_true() -> bool {
|
226 + | // Serde has limitations requiring this odd workaround.
|
227 + | // See https://github.com/serde-rs/serde/issues/368 for more info.
|
228 + | fn return_true() -> bool {
|
154 229 | true
|
155 - | }
|
230 + | }
|
156 231 |
|
157 - | #[derive(Deserialize)]
|
158 - | pub(crate) struct TestContextBuilder {
|
232 + | #[derive(serde_derive::Deserialize)]
|
233 + | pub(crate) struct TestContextBuilder {
|
159 234 | credentials: TestContextCreds,
|
160 235 | expiration_in_seconds: u64,
|
161 236 | normalize: bool,
|
162 237 | region: String,
|
163 238 | service: String,
|
164 239 | timestamp: String,
|
165 240 | #[serde(default)]
|
166 241 | omit_session_token: bool,
|
167 242 | #[serde(default = "return_true")]
|
168 243 | sign_body: bool,
|
169 - | }
|
244 + | }
|
170 245 |
|
171 - | impl TestContextBuilder {
|
246 + | impl TestContextBuilder {
|
172 247 | pub(crate) fn build(self) -> TestContext {
|
173 248 | let identity = Identity::new(
|
174 249 | Credentials::from_keys(
|
175 250 | &self.credentials.access_key_id,
|
176 251 | &self.credentials.secret_access_key,
|
177 252 | self.credentials.token.clone(),
|
178 253 | ),
|
179 254 | Some(SystemTime::UNIX_EPOCH + Duration::from_secs(self.expiration_in_seconds)),
|
180 255 | );
|
181 256 |
|
182 257 | TestContext {
|
183 258 | identity,
|
184 259 | expiration_in_seconds: self.expiration_in_seconds,
|
185 260 | normalize: self.normalize,
|
186 261 | region: self.region,
|
187 262 | service: self.service,
|
188 263 | timestamp: self.timestamp,
|
189 264 | omit_session_token: self.omit_session_token,
|
190 265 | sign_body: self.sign_body,
|
191 266 | }
|
192 267 | }
|
193 - | }
|
268 + | }
|
194 269 |
|
195 - | #[derive(Deserialize)]
|
196 - | pub(crate) struct TestContextCreds {
|
270 + | #[derive(serde_derive::Deserialize)]
|
271 + | pub(crate) struct TestContextCreds {
|
197 272 | access_key_id: String,
|
198 273 | secret_access_key: String,
|
199 274 | token: Option<String>,
|
275 + | }
|
276 + |
|
277 + | impl<'a> From<&'a TestContext> for crate::sign::v4::SigningParams<'a, SigningSettings> {
|
278 + | fn from(tc: &'a TestContext) -> Self {
|
279 + | crate::sign::v4::SigningParams {
|
280 + | identity: &tc.identity,
|
281 + | region: &tc.region,
|
282 + | name: &tc.service,
|
283 + | time: OffsetDateTime::parse(&tc.timestamp, &Rfc3339)
|
284 + | .unwrap()
|
285 + | .into(),
|
286 + | settings: SigningSettings {
|
287 + | // payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
|
288 + | expires_in: Some(Duration::from_secs(tc.expiration_in_seconds)),
|
289 + | uri_path_normalization_mode: tc.normalize.into(),
|
290 + | session_token_mode: if tc.omit_session_token {
|
291 + | SessionTokenMode::Exclude
|
292 + | } else {
|
293 + | SessionTokenMode::Include
|
294 + | },
|
295 + | payload_checksum_kind: if tc.sign_body {
|
296 + | PayloadChecksumKind::XAmzSha256
|
297 + | } else {
|
298 + | PayloadChecksumKind::NoHeader
|
299 + | },
|
300 + | ..Default::default()
|
301 + | },
|
302 + | }
|
303 + | }
|
304 + | }
|
305 + |
|
306 + | #[cfg(feature = "sigv4a")]
|
307 + | pub(crate) mod v4a {
|
308 + | use super::*;
|
309 + | use crate::http_request::{
|
310 + | sign, PayloadChecksumKind, SessionTokenMode, SignatureLocation, SigningSettings,
|
311 + | };
|
312 + | use crate::sign::v4a;
|
313 + | use p256::ecdsa::signature::{Signature, Verifier};
|
314 + | use p256::ecdsa::{DerSignature, SigningKey};
|
315 + | use std::time::Duration;
|
316 + | use time::format_description::well_known::Rfc3339;
|
317 + | use time::OffsetDateTime;
|
318 + |
|
319 + | fn new_v4a_signing_params_from_context(
|
320 + | test_context: &'_ TestContext,
|
321 + | signature_location: SignatureLocation,
|
322 + | ) -> crate::http_request::SigningParams<'_> {
|
323 + | let mut params = crate::sign::v4a::SigningParams::from(test_context);
|
324 + | params.settings.signature_location = signature_location;
|
325 + | params.into()
|
326 + | }
|
327 + |
|
328 + | pub(crate) fn run_test_suite_v4a(test_name: &'static str) {
|
329 + | run_v4a_test(test_name, SignatureLocation::Headers);
|
330 + | run_v4a_test(test_name, SignatureLocation::QueryParams);
|
331 + | }
|
332 + |
|
333 + | pub(crate) fn run_v4a_test(test_name: &'static str, signature_location: SignatureLocation) {
|
334 + | let test = SigningSuiteTest::v4a(test_name);
|
335 + | let tc = test.context();
|
336 + | let params = new_v4a_signing_params_from_context(&tc, signature_location);
|
337 + |
|
338 + | let req = test.request();
|
339 + | let expected_creq = test.canonical_request(signature_location);
|
340 + | let signable_req = SignableRequest::from(&req);
|
341 + | let actual_creq = CanonicalRequest::from(&signable_req, ¶ms).unwrap();
|
342 + |
|
343 + | assert_eq!(
|
344 + | expected_creq,
|
345 + | actual_creq.to_string(),
|
346 + | "canonical request didn't match (signature location: {signature_location:?})"
|
347 + | );
|
348 + |
|
349 + | let expected_string_to_sign = test.string_to_sign(signature_location);
|
350 + | let hashed_creq = &crate::sign::v4::sha256_hex_string(actual_creq.to_string().as_bytes());
|
351 + | let actual_string_to_sign = StringToSign::new_v4a(
|
352 + | *params.time(),
|
353 + | params.region_set().unwrap(),
|
354 + | params.name(),
|
355 + | hashed_creq,
|
356 + | )
|
357 + | .to_string();
|
358 + |
|
359 + | assert_eq!(
|
360 + | expected_string_to_sign, actual_string_to_sign,
|
361 + | "'string to sign' didn't match (signature location: {signature_location:?})"
|
362 + | );
|
363 + |
|
364 + | let out = sign(signable_req, ¶ms).unwrap();
|
365 + | // Sigv4a signatures are non-deterministic, so we can't compare the signature directly.
|
366 + | out.output
|
367 + | .apply_to_request_http0x(&mut req.as_http_request());
|
368 + |
|
369 + | let creds = params.credentials().unwrap();
|
370 + | let signing_key =
|
371 + | v4a::generate_signing_key(creds.access_key_id(), creds.secret_access_key());
|
372 + | let sig = DerSignature::from_bytes(&hex::decode(out.signature).unwrap()).unwrap();
|
373 + | let sig = sig
|
374 + | .try_into()
|
375 + | .expect("DER-style signatures are always convertible into fixed-size signatures");
|
376 + |
|
377 + | let signing_key = SigningKey::from_bytes(signing_key.as_ref()).unwrap();
|
378 + | let peer_public_key = signing_key.verifying_key();
|
379 + | let sts = actual_string_to_sign.as_bytes();
|
380 + | peer_public_key.verify(sts, &sig).unwrap();
|
381 + | // TODO(sigv4a) - use public.key.json as verifying key?
|
382 + | }
|
383 + |
|
384 + | impl<'a> From<&'a TestContext> for crate::sign::v4a::SigningParams<'a, SigningSettings> {
|
385 + | fn from(tc: &'a TestContext) -> Self {
|
386 + | crate::sign::v4a::SigningParams {
|
387 + | identity: &tc.identity,
|
388 + | region_set: &tc.region,
|
389 + | name: &tc.service,
|
390 + | time: OffsetDateTime::parse(&tc.timestamp, &Rfc3339)
|
391 + | .unwrap()
|
392 + | .into(),
|
393 + | settings: SigningSettings {
|
394 + | // payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
|
395 + | expires_in: Some(Duration::from_secs(tc.expiration_in_seconds)),
|
396 + | uri_path_normalization_mode: tc.normalize.into(),
|
397 + | session_token_mode: if tc.omit_session_token {
|
398 + | SessionTokenMode::Exclude
|
399 + | } else {
|
400 + | SessionTokenMode::Include
|
401 + | },
|
402 + | payload_checksum_kind: if tc.sign_body {
|
403 + | PayloadChecksumKind::XAmzSha256
|
404 + | } else {
|
405 + | PayloadChecksumKind::NoHeader
|
406 + | },
|
407 + | ..Default::default()
|
408 + | },
|
409 + | }
|
410 + | }
|
200 411 | }
|
201 412 |
|
202 413 | #[test]
|
203 414 | fn test_parse() {
|
204 - | let req = test_request("post-header-key-case");
|
415 + | let req = SigningSuiteTest::v4a("post-header-key-case").request();
|
205 416 | assert_eq!(req.method, "POST");
|
206 417 | assert_eq!(req.uri, "https://example.amazonaws.com/");
|
207 418 | assert!(req.headers.is_empty());
|
208 419 | }
|
209 420 |
|
210 421 | #[test]
|
211 422 | fn test_read_query_params() {
|
212 - | let req = test_request("get-header-value-trim");
|
423 + | let req = SigningSuiteTest::v4a("get-header-value-trim").request();
|
213 424 | assert_eq!(req.method, "GET");
|
214 425 | assert_eq!(req.uri, "https://example.amazonaws.com/");
|
215 426 | assert!(!req.headers.is_empty());
|
216 427 | }
|
217 428 | }
|
218 429 |
|
219 430 | fn read(path: &str) -> String {
|
220 431 | println!("Loading `{}` for test case...", path);
|
221 432 | let v = {
|
222 433 | match std::fs::read_to_string(path) {
|