@@ -16,6 +16,7 @@ use crate::sign::v4::sha256_hex_string;
1616use crate :: SignatureVersion ;
1717use aws_smithy_http:: query_writer:: QueryWriter ;
1818use http0:: header:: { AsHeaderName , HeaderName , HOST } ;
19+ use http0:: uri:: { Port , Scheme } ;
1920use http0:: { HeaderMap , HeaderValue , Uri } ;
2021use std:: borrow:: Cow ;
2122use std:: cmp:: Ordering ;
@@ -389,10 +390,28 @@ impl<'a> CanonicalRequest<'a> {
389390 match canonical_headers. get ( & HOST ) {
390391 Some ( header) => header. clone ( ) ,
391392 None => {
393+ let port = uri. port ( ) ;
394+ let scheme = uri. scheme ( ) ;
392395 let authority = uri
393396 . authority ( )
394- . expect ( "request uri authority must be set for signing" ) ;
395- let header = HeaderValue :: try_from ( authority. as_str ( ) )
397+ . expect ( "request uri authority must be set for signing" )
398+ . as_str ( ) ;
399+ let host = uri
400+ . host ( )
401+ . expect ( "request uri host must be set for signing" ) ;
402+
403+ // Check if port is default (80 for HTTP, 443 for HTTPS) and if so exclude it from the
404+ // Host header when signing since RFC 2616 indicates that the default port should not be
405+ // sent in the Host header (and Hyper strips default ports if they are present)
406+ // https://datatracker.ietf.org/doc/html/rfc2616#section-14.23
407+ // https://github.com/awslabs/aws-sdk-rust/issues/1244
408+ let header_value = if is_port_scheme_default ( scheme, port) {
409+ host
410+ } else {
411+ authority
412+ } ;
413+
414+ let header = HeaderValue :: try_from ( header_value)
396415 . expect ( "endpoint must contain valid header characters" ) ;
397416 canonical_headers. insert ( HOST , header. clone ( ) ) ;
398417 header
@@ -475,6 +494,15 @@ fn normalize_header_value(header_value: &str) -> Result<HeaderValue, CanonicalRe
475494 HeaderValue :: from_str ( & trimmed_value) . map_err ( CanonicalRequestError :: from)
476495}
477496
497+ #[ inline]
498+ fn is_port_scheme_default ( scheme : Option < & Scheme > , port : Option < Port < & str > > ) -> bool {
499+ if let ( Some ( scheme) , Some ( port) ) = ( scheme, port) {
500+ return [ ( "http" , "80" ) , ( "https" , "443" ) ] . contains ( & ( scheme. as_str ( ) , port. as_str ( ) ) ) ;
501+ }
502+
503+ false
504+ }
505+
478506#[ derive( Debug , PartialEq , Default ) ]
479507pub ( crate ) struct SignedHeaders {
480508 headers : Vec < CanonicalHeaderName > ,
@@ -692,6 +720,40 @@ mod tests {
692720 ) ;
693721 }
694722
723+ #[ test]
724+ fn test_host_header_properly_handles_ports ( ) {
725+ fn host_header_test_setup ( endpoint : String ) -> String {
726+ let mut req = test:: v4:: test_request ( "get-vanilla" ) ;
727+ req. uri = endpoint;
728+ let req = SignableRequest :: from ( & req) ;
729+ let settings = SigningSettings {
730+ payload_checksum_kind : PayloadChecksumKind :: XAmzSha256 ,
731+ session_token_mode : SessionTokenMode :: Exclude ,
732+ ..Default :: default ( )
733+ } ;
734+ let identity = Credentials :: for_tests ( ) . into ( ) ;
735+ let signing_params = signing_params ( & identity, settings) ;
736+ let creq = CanonicalRequest :: from ( & req, & signing_params) . unwrap ( ) ;
737+ creq. header_values_for ( "host" )
738+ }
739+
740+ // HTTP request with 80 port should not be signed with that port
741+ let http_80_host_header = host_header_test_setup ( "http://localhost:80" . into ( ) ) ;
742+ assert_eq ! ( http_80_host_header, "localhost" , ) ;
743+
744+ // HTTP request with non-80 port should be signed with that port
745+ let http_1234_host_header = host_header_test_setup ( "http://localhost:1234" . into ( ) ) ;
746+ assert_eq ! ( http_1234_host_header, "localhost:1234" , ) ;
747+
748+ // HTTPS request with 443 port should not be signed with that port
749+ let https_443_host_header = host_header_test_setup ( "https://localhost:443" . into ( ) ) ;
750+ assert_eq ! ( https_443_host_header, "localhost" , ) ;
751+
752+ // HTTPS request with non-443 port should be signed with that port
753+ let https_1234_host_header = host_header_test_setup ( "https://localhost:1234" . into ( ) ) ;
754+ assert_eq ! ( https_1234_host_header, "localhost:1234" , ) ;
755+ }
756+
695757 #[ test]
696758 fn test_set_xamz_sha_256 ( ) {
697759 let req = test:: v4:: test_request ( "get-vanilla-query-order-key-case" ) ;
0 commit comments