Skip to content

Commit 39b018f

Browse files
JordonPhillipsnateprewitt
authored andcommitted
Use special payload hash for event streams
This updates the async signer to use the special payload hash for event stream operations.
1 parent 8561a1c commit 39b018f

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

packages/aws-sdk-signers/src/aws_sdk_signers/signers.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636

3737
SIGV4_TIMESTAMP_FORMAT: str = "%Y%m%dT%H%M%SZ"
3838
UNSIGNED_PAYLOAD: str = "UNSIGNED-PAYLOAD"
39+
EVENT_STREAM_CONTENT_TYPE = "application/vnd.amazon.eventstream"
40+
EVENT_STREAM_HASH = "STREAMING-AWS4-HMAC-SHA256-EVENTS"
3941
EMPTY_SHA256_HASH = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
4042

4143

@@ -755,6 +757,12 @@ async def _format_canonical_payload(
755757
):
756758
return request.fields["X-Amz-Content-SHA256"].values[0]
757759

760+
if self._is_event_stream(request=request):
761+
request.fields.set_field(
762+
Field(name="X-Amz-Content-SHA256", values=[EVENT_STREAM_HASH])
763+
)
764+
return EVENT_STREAM_HASH
765+
758766
payload_hash = await self._compute_payload_hash(
759767
request=request, signing_properties=signing_properties
760768
)
@@ -804,6 +812,12 @@ async def _compute_payload_hash(
804812
request.body = AsyncBytesReader(buffer)
805813
return checksum.hexdigest()
806814

815+
def _is_event_stream(self, request: AWSRequest) -> bool:
816+
if "Content-Type" not in request.fields:
817+
return False
818+
content_type = request.fields["Content-Type"].as_string()
819+
return content_type == EVENT_STREAM_CONTENT_TYPE
820+
807821
def _seekable(self, body: AsyncIterable[bytes]) -> TypeGuard[AsyncSeekable]:
808822
if isinstance(body, ConditionallySeekable):
809823
return body.seekable()

packages/aws-sdk-signers/tests/unit/test_signers.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
AsyncSigV4Signer,
1313
AWSCredentialIdentity,
1414
AWSRequest,
15+
Field,
1516
Fields,
1617
SigV4Signer,
1718
SigV4SigningProperties,
@@ -125,6 +126,14 @@ def test_sign_with_expired_identity(
125126
)
126127

127128

129+
class UnreadableAsyncStream:
130+
def __aiter__(self) -> typing.Self:
131+
return self
132+
133+
async def __anext__(self) -> bytes:
134+
raise Exception("Read should not have been called!")
135+
136+
128137
class TestAsyncSigV4Signer:
129138
SIGV4_ASYNC_SIGNER = AsyncSigV4Signer()
130139

@@ -191,3 +200,34 @@ async def test_sign_with_expired_identity(
191200
request=aws_request,
192201
identity=identity,
193202
)
203+
204+
async def test_sign_event_stream(
205+
self,
206+
aws_identity: AWSCredentialIdentity,
207+
) -> None:
208+
headers = Fields()
209+
headers.set_field(
210+
Field(name="Content-Type", values=["application/vnd.amazon.eventstream"])
211+
)
212+
request = AWSRequest(
213+
destination=URI(
214+
scheme="https",
215+
host="127.0.0.1",
216+
port=8000,
217+
),
218+
method="GET",
219+
body=UnreadableAsyncStream(),
220+
fields=headers,
221+
)
222+
signed = await self.SIGV4_ASYNC_SIGNER.sign(
223+
properties=SigV4SigningProperties(
224+
region="us-west-2",
225+
service="ec2",
226+
date="0",
227+
),
228+
request=request,
229+
identity=aws_identity,
230+
)
231+
assert "X-Amz-Content-SHA256" in signed.fields
232+
payload_hash = signed.fields["X-Amz-Content-SHA256"].as_string()
233+
assert payload_hash == "STREAMING-AWS4-HMAC-SHA256-EVENTS"

0 commit comments

Comments
 (0)