@@ -13,7 +13,7 @@ use crate::http_request::url_escape::percent_encode_path;
13
13
use crate :: http_request:: PercentEncodingMode ;
14
14
use crate :: http_request:: { PayloadChecksumKind , SignableBody , SignatureLocation , SigningParams } ;
15
15
use crate :: sign:: sha256_hex_string;
16
- use http:: header:: { HeaderName , HOST } ;
16
+ use http:: header:: { AsHeaderName , HeaderName , HOST } ;
17
17
use http:: { HeaderMap , HeaderValue , Method , Uri } ;
18
18
use std:: borrow:: Cow ;
19
19
use std:: cmp:: Ordering ;
@@ -225,7 +225,7 @@ impl<'a> CanonicalRequest<'a> {
225
225
}
226
226
227
227
let mut signed_headers = Vec :: with_capacity ( canonical_headers. len ( ) ) ;
228
- for ( name, _ ) in & canonical_headers {
228
+ for name in canonical_headers. keys ( ) {
229
229
if let Some ( excluded_headers) = params. settings . excluded_headers . as_ref ( ) {
230
230
if excluded_headers. contains ( name) {
231
231
continue ;
@@ -328,6 +328,19 @@ impl<'a> CanonicalRequest<'a> {
328
328
canonical_headers. insert ( x_amz_date, date_header. clone ( ) ) ;
329
329
date_header
330
330
}
331
+
332
+ fn header_values_for ( & self , key : impl AsHeaderName ) -> String {
333
+ let values: Vec < & str > = self
334
+ . headers
335
+ . get_all ( key)
336
+ . into_iter ( )
337
+ . map ( |value| {
338
+ std:: str:: from_utf8 ( value. as_bytes ( ) )
339
+ . expect ( "SDK request header values are valid UTF-8" )
340
+ } )
341
+ . collect ( ) ;
342
+ values. join ( "," )
343
+ }
331
344
}
332
345
333
346
impl < ' a > fmt:: Display for CanonicalRequest < ' a > {
@@ -337,15 +350,8 @@ impl<'a> fmt::Display for CanonicalRequest<'a> {
337
350
writeln ! ( f, "{}" , self . params. as_deref( ) . unwrap_or( "" ) ) ?;
338
351
// write out _all_ the headers
339
352
for header in & self . values . signed_headers ( ) . headers {
340
- // a missing header is a bug, so we should panic.
341
- let value = & self . headers [ & header. 0 ] ;
342
353
write ! ( f, "{}:" , header. 0 . as_str( ) ) ?;
343
- writeln ! (
344
- f,
345
- "{}" ,
346
- std:: str :: from_utf8( value. as_bytes( ) )
347
- . expect( "SDK request header values are valid UTF-8" )
348
- ) ?;
354
+ writeln ! ( f, "{}" , self . header_values_for( & header. 0 ) ) ?;
349
355
}
350
356
writeln ! ( f) ?;
351
357
// write out the signed headers
@@ -538,6 +544,35 @@ mod tests {
538
544
}
539
545
}
540
546
547
+ #[ test]
548
+ fn test_repeated_header ( ) {
549
+ let mut req = test_request ( "get-vanilla-query-order-key-case" ) ;
550
+ req. headers_mut ( ) . append (
551
+ "x-amz-object-attributes" ,
552
+ HeaderValue :: from_static ( "Checksum" ) ,
553
+ ) ;
554
+ req. headers_mut ( ) . append (
555
+ "x-amz-object-attributes" ,
556
+ HeaderValue :: from_static ( "ObjectSize" ) ,
557
+ ) ;
558
+ let req = SignableRequest :: from ( & req) ;
559
+ let settings = SigningSettings {
560
+ payload_checksum_kind : PayloadChecksumKind :: XAmzSha256 ,
561
+ ..Default :: default ( )
562
+ } ;
563
+ let signing_params = signing_params ( settings) ;
564
+ let creq = CanonicalRequest :: from ( & req, & signing_params) . unwrap ( ) ;
565
+
566
+ assert_eq ! (
567
+ creq. values. signed_headers( ) . to_string( ) ,
568
+ "host;x-amz-content-sha256;x-amz-date;x-amz-object-attributes"
569
+ ) ;
570
+ assert_eq ! (
571
+ creq. header_values_for( "x-amz-object-attributes" ) ,
572
+ "Checksum,ObjectSize" ,
573
+ ) ;
574
+ }
575
+
541
576
#[ test]
542
577
fn test_set_xamz_sha_256 ( ) {
543
578
let req = test_request ( "get-vanilla-query-order-key-case" ) ;
0 commit comments