@@ -41,6 +41,7 @@ class SigV4SigningProperties(TypedDict, total=False):
4141 date : str
4242 payload_signing_enabled : bool
4343 content_checksum_enabled : bool
44+ uri_encode_path : bool
4445
4546
4647class SigV4Signer :
@@ -232,7 +233,9 @@ def canonical_request(
232233 canonical_payload = self ._format_canonical_payload (
233234 request = request , signing_properties = signing_properties
234235 )
235- canonical_path = self ._format_canonical_path (path = request .destination .path )
236+ canonical_path = self ._format_canonical_path (
237+ path = request .destination .path , signing_properties = signing_properties
238+ )
236239 canonical_query = self ._format_canonical_query (query = request .destination .query )
237240 normalized_fields = self ._normalize_signing_fields (request = request )
238241 canonical_fields = self ._format_canonical_fields (fields = normalized_fields )
@@ -290,11 +293,17 @@ def _scope(self, signing_properties: SigV4SigningProperties) -> str:
290293 # Scope format: <YYYYMMDD>/<AWS Region>/<AWS Service>/aws4_request
291294 return f"{ formatted_date } /{ region } /{ service } /aws4_request"
292295
293- def _format_canonical_path (self , * , path : str | None ) -> str :
296+ def _format_canonical_path (
297+ self , * , path : str | None , signing_properties : SigV4SigningProperties
298+ ) -> str :
294299 if path is None :
295300 path = "/"
296- normalized_path = _remove_dot_segments (path )
297- return quote (string = normalized_path , safe = "/%" )
301+
302+ if signing_properties .get ("uri_encode_path" , True ):
303+ normalized_path = _remove_dot_segments (path )
304+ return quote (string = normalized_path , safe = "/" )
305+ else :
306+ return _remove_dot_segments (path , remove_consecutive_slashes = False )
298307
299308 def _format_canonical_query (self , * , query : str | None ) -> str :
300309 if query is None :
@@ -596,7 +605,7 @@ async def canonical_request(
596605 request = request , signing_properties = signing_properties
597606 )
598607 canonical_path = await self ._format_canonical_path (
599- path = request .destination .path
608+ path = request .destination .path , signing_properties = signing_properties
600609 )
601610 canonical_query = await self ._format_canonical_query (
602611 query = request .destination .query
@@ -658,11 +667,17 @@ async def _scope(self, signing_properties: SigV4SigningProperties) -> str:
658667 # Scope format: <YYYYMMDD>/<AWS Region>/<AWS Service>/aws4_request
659668 return f"{ formatted_date } /{ region } /{ service } /aws4_request"
660669
661- async def _format_canonical_path (self , * , path : str | None ) -> str :
670+ async def _format_canonical_path (
671+ self , * , path : str | None , signing_properties : SigV4SigningProperties
672+ ) -> str :
662673 if path is None :
663674 path = "/"
664- normalized_path = _remove_dot_segments (path )
665- return quote (string = normalized_path , safe = "/%" )
675+
676+ if signing_properties .get ("uri_encode_path" , True ):
677+ normalized_path = _remove_dot_segments (path )
678+ return quote (string = normalized_path , safe = "/" )
679+ else :
680+ return _remove_dot_segments (path , remove_consecutive_slashes = False )
666681
667682 async def _format_canonical_query (self , * , query : str | None ) -> str :
668683 if query is None :
0 commit comments