11// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22// SPDX-License-Identifier: Apache-2.0
33
4+ use std:: collections:: HashMap ;
45use std:: result:: Result ;
56
67use crate :: HttpHeaderError ;
@@ -94,6 +95,8 @@ pub struct Headers {
9495 /// `Accept` header might be used by HTTP clients to enforce server responses with content
9596 /// formatted in a specific way.
9697 accept : MediaType ,
98+ /// Hashmap reserved for storing custom headers.
99+ custom_entries : HashMap < String , String > ,
97100}
98101
99102impl Default for Headers {
@@ -106,6 +109,7 @@ impl Default for Headers {
106109 // The default `Accept` media type is plain text. This is inclusive enough
107110 // for structured and unstructured text.
108111 accept : MediaType :: PlainText ,
112+ custom_entries : HashMap :: default ( ) ,
109113 }
110114 }
111115}
@@ -206,12 +210,11 @@ impl Headers {
206210 Header :: AcceptEncoding => Encoding :: try_from ( entry[ 1 ] . trim ( ) . as_bytes ( ) ) ,
207211 }
208212 } else {
209- Err ( RequestError :: HeaderError (
210- HttpHeaderError :: UnsupportedValue (
211- entry[ 0 ] . to_string ( ) ,
212- entry[ 1 ] . to_string ( ) ,
213- ) ,
214- ) )
213+ self . insert_custom_header (
214+ entry[ 0 ] . trim ( ) . to_string ( ) ,
215+ entry[ 1 ] . trim ( ) . to_string ( ) ,
216+ ) ?;
217+ Ok ( ( ) )
215218 }
216219 }
217220 Err ( utf8_err) => Err ( RequestError :: HeaderError (
@@ -289,6 +292,17 @@ impl Headers {
289292 pub fn set_accept ( & mut self , media_type : MediaType ) {
290293 self . accept = media_type;
291294 }
295+
296+ /// Insert a new custom header and value pair into the `HashMap`.
297+ pub fn insert_custom_header ( & mut self , key : String , value : String ) -> Result < ( ) , RequestError > {
298+ self . custom_entries . insert ( key, value) ;
299+ Ok ( ( ) )
300+ }
301+
302+ /// Returns the custom header `HashMap`.
303+ pub fn custom_entries ( & self ) -> & HashMap < String , String > {
304+ & self . custom_entries
305+ }
292306}
293307
294308/// Wrapper over supported AcceptEncoding.
@@ -414,6 +428,7 @@ impl MediaType {
414428#[ cfg( test) ]
415429mod tests {
416430 use super :: * ;
431+ use std:: collections:: HashMap ;
417432
418433 impl Headers {
419434 pub fn new ( content_length : u32 , expect : bool , chunked : bool ) -> Self {
@@ -422,6 +437,7 @@ mod tests {
422437 expect,
423438 chunked,
424439 accept : MediaType :: PlainText ,
440+ custom_entries : HashMap :: default ( ) ,
425441 }
426442 }
427443 }
@@ -433,6 +449,7 @@ mod tests {
433449 assert_eq ! ( headers. chunked( ) , false ) ;
434450 assert_eq ! ( headers. expect( ) , false ) ;
435451 assert_eq ! ( headers. accept( ) , MediaType :: PlainText ) ;
452+ assert_eq ! ( headers. custom_entries( ) , & HashMap :: default ( ) ) ;
436453 }
437454
438455 #[ test]
@@ -512,6 +529,11 @@ mod tests {
512529 . unwrap ( ) ;
513530 assert_eq ! ( headers. content_length, 55 ) ;
514531 assert_eq ! ( headers. accept, MediaType :: ApplicationJson ) ;
532+ assert_eq ! (
533+ headers. custom_entries( ) . get( "Last-Modified" ) . unwrap( ) ,
534+ "Tue, 15 Nov 1994 12:45:26 GMT"
535+ ) ;
536+ assert_eq ! ( headers. custom_entries( ) . len( ) , 1 ) ;
515537
516538 // Valid headers. (${HEADER_NAME} : WHITESPACE ${HEADER_VALUE})
517539 // Any number of whitespace characters should be accepted including zero.
@@ -529,7 +551,17 @@ mod tests {
529551 . unwrap ( ) ;
530552 assert_eq ! ( headers. content_length, 29 ) ;
531553
532- // Valid headers.
554+ // Custom headers only.
555+ let headers = Headers :: try_from (
556+ b"Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r \n foo: bar\r \n bar: 15\r \n \r \n " ,
557+ )
558+ . unwrap ( ) ;
559+ let custom_entries = headers. custom_entries ( ) ;
560+ assert_eq ! ( custom_entries. get( "foo" ) . unwrap( ) , "bar" ) ;
561+ assert_eq ! ( custom_entries. get( "bar" ) . unwrap( ) , "15" ) ;
562+ assert_eq ! ( custom_entries. len( ) , 3 ) ;
563+
564+ // Valid headers, invalid value.
533565 assert_eq ! (
534566 Headers :: try_from(
535567 b"Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r \n Content-Length: -55\r \n \r \n "
@@ -656,6 +688,15 @@ mod tests {
656688 " identity;q=0" . to_string( )
657689 ) ) )
658690 ) ;
691+
692+ // Test custom header.
693+ assert_eq ! ( header. custom_entries( ) . len( ) , 0 ) ;
694+ assert ! ( header. parse_header_line( b"Custom-Header: foo" ) . is_ok( ) ) ;
695+ assert_eq ! (
696+ header. custom_entries( ) . get( "Custom-Header" ) . unwrap( ) ,
697+ & "foo" . to_string( )
698+ ) ;
699+ assert_eq ! ( header. custom_entries( ) . len( ) , 1 ) ;
659700 }
660701
661702 #[ test]
@@ -697,6 +738,13 @@ mod tests {
697738 // For Expect
698739 assert ! ( header. parse_header_line( b"Expect:100-continue" ) . is_ok( ) ) ;
699740 assert ! ( header. parse_header_line( b"Expect: 100-continue" ) . is_ok( ) ) ;
741+
742+ // Test that custom headers' names and values are trimmed before being stored
743+ // inside the HashMap.
744+ assert ! ( header. parse_header_line( b"Foo:bar" ) . is_ok( ) ) ;
745+ assert_eq ! ( header. custom_entries( ) . get( "Foo" ) . unwrap( ) , "bar" ) ;
746+ assert ! ( header. parse_header_line( b" Bar : foo " ) . is_ok( ) ) ;
747+ assert_eq ! ( header. custom_entries( ) . get( "Bar" ) . unwrap( ) , "foo" ) ;
700748 }
701749
702750 #[ test]
0 commit comments