smithy-http 0.4.2__tar.gz → 0.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smithy_http-0.4.4/.changes/0.4.3.json +12 -0
- smithy_http-0.4.4/.changes/0.4.4.json +16 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/CHANGELOG.md +15 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/PKG-INFO +2 -2
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/__init__.py +1 -1
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/aiohttp.py +25 -2
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/crt.py +4 -1
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/protocols.py +34 -3
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/testing/mockhttp.py +5 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.0.1.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.1.0.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.2.0.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.2.1.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.3.0.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.3.1.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.4.0.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.4.1.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.changes/0.4.2.json +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/.gitignore +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/NOTICE +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/README.md +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/pyproject.toml +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/__init__.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/auth/__init__.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/auth/apikey.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/identity/__init__.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/identity/apikey.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/interfaces/__init__.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/aio/restjson.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/bindings.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/deserializers.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/endpoints.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/exceptions.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/interceptors/__init__.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/interceptors/user_agent.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/interfaces/__init__.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/plugins/__init__.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/py.typed +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/restjson.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/serializers.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/testing/__init__.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/testing/utils.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/user_agent.py +0 -0
- {smithy_http-0.4.2 → smithy_http-0.4.4}/src/smithy_http/utils.py +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"changes": [
|
|
3
|
+
{
|
|
4
|
+
"type": "enhancement",
|
|
5
|
+
"description": "Enabled HTTP binding protocols to resolve modeled response errors when wire identifiers do not exactly match registered shape IDs. ([#742](https://github.com/smithy-lang/smithy-python/pull/742))"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"type": "bugfix",
|
|
9
|
+
"description": "Fixed `AIOHTTPClient` sending omitted request payloads with chunked transfer encoding."
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"changes": [
|
|
3
|
+
{
|
|
4
|
+
"type": "enhancement",
|
|
5
|
+
"description": "Added a protocol hook for extracting server-provided retry delays from error responses."
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"type": "enhancement",
|
|
9
|
+
"description": "Declared `AWSCRTHTTPClient` as duplex (bidirectional) streaming capable via `SUPPORTS_DUPLEX_STREAMING`. `AIOHTTPClient` explicitly does not support duplex streaming."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "enhancement",
|
|
13
|
+
"description": "Disabled automatic redirects in `AIOHTTPClient` so that redirect responses are returned to the caller instead of being followed transparently."
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.4.4
|
|
4
|
+
|
|
5
|
+
### Enhancements
|
|
6
|
+
* Added a protocol hook for extracting server-provided retry delays from error responses.
|
|
7
|
+
* Declared `AWSCRTHTTPClient` as duplex (bidirectional) streaming capable via `SUPPORTS_DUPLEX_STREAMING`. `AIOHTTPClient` explicitly does not support duplex streaming.
|
|
8
|
+
* Disabled automatic redirects in `AIOHTTPClient` so that redirect responses are returned to the caller instead of being followed transparently.
|
|
9
|
+
|
|
10
|
+
## v0.4.3
|
|
11
|
+
|
|
12
|
+
### Enhancements
|
|
13
|
+
* Enabled HTTP binding protocols to resolve modeled response errors when wire identifiers do not exactly match registered shape IDs. ([#742](https://github.com/smithy-lang/smithy-python/pull/742))
|
|
14
|
+
|
|
15
|
+
### Bug fixes
|
|
16
|
+
* Fixed `AIOHTTPClient` sending omitted request payloads with chunked transfer encoding.
|
|
17
|
+
|
|
3
18
|
## v0.4.2
|
|
4
19
|
|
|
5
20
|
### Bug fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: smithy-http
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: HTTP components for Smithy tooling.
|
|
5
5
|
Project-URL: Changelog, https://github.com/smithy-lang/smithy-python/blob/develop/packages/smithy-http/CHANGELOG.md
|
|
6
6
|
Project-URL: Code, https://github.com/smithy-lang/smithy-python/blob/develop/packages/smithy-http/
|
|
@@ -53,6 +53,10 @@ class AIOHTTPClient(HTTPClient):
|
|
|
53
53
|
|
|
54
54
|
TIMEOUT_EXCEPTIONS = (TimeoutError,)
|
|
55
55
|
|
|
56
|
+
# aiohttp has no HTTP/2 support and this client fully buffers the response
|
|
57
|
+
# before returning, so it can never interleave request and response data.
|
|
58
|
+
SUPPORTS_DUPLEX_STREAMING = False
|
|
59
|
+
|
|
56
60
|
def __init__(
|
|
57
61
|
self,
|
|
58
62
|
*,
|
|
@@ -90,8 +94,13 @@ class AIOHTTPClient(HTTPClient):
|
|
|
90
94
|
chain.from_iterable(fld.as_tuples() for fld in request.fields)
|
|
91
95
|
)
|
|
92
96
|
|
|
93
|
-
body: StreamingBlob = request.body
|
|
94
|
-
if
|
|
97
|
+
body: StreamingBlob | None = request.body
|
|
98
|
+
if (
|
|
99
|
+
"content-length" not in request.fields
|
|
100
|
+
and "transfer-encoding" not in request.fields
|
|
101
|
+
):
|
|
102
|
+
body = await self._prepare_body(body)
|
|
103
|
+
elif not isinstance(body, AsyncBytesReader):
|
|
95
104
|
body = AsyncBytesReader(body)
|
|
96
105
|
|
|
97
106
|
# The typing on `params` is incorrect, it'll happily accept a mapping whose
|
|
@@ -103,9 +112,23 @@ class AIOHTTPClient(HTTPClient):
|
|
|
103
112
|
params=parse_qs(request.destination.query), # type: ignore
|
|
104
113
|
headers=headers_list,
|
|
105
114
|
data=body,
|
|
115
|
+
allow_redirects=False,
|
|
106
116
|
) as resp:
|
|
107
117
|
return await self._marshal_response(resp)
|
|
108
118
|
|
|
119
|
+
async def _prepare_body(self, body: StreamingBlob) -> AsyncBytesReader | None:
|
|
120
|
+
"""Convert a body for aiohttp, omitting seekable bodies with no data."""
|
|
121
|
+
if not isinstance(body, AsyncBytesReader):
|
|
122
|
+
body = AsyncBytesReader(body)
|
|
123
|
+
|
|
124
|
+
if not body.seekable():
|
|
125
|
+
return body
|
|
126
|
+
|
|
127
|
+
position = await body.seek(0, 1)
|
|
128
|
+
end = await body.seek(0, 2)
|
|
129
|
+
await body.seek(position)
|
|
130
|
+
return None if position == end else body
|
|
131
|
+
|
|
109
132
|
def _serialize_uri_without_query(self, uri: URI) -> yarl.URL:
|
|
110
133
|
"""Serialize all parts of the URI up to and including the path."""
|
|
111
134
|
return yarl.URL.build(
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
# pyright: reportMissingTypeStubs=false,reportUnknownMemberType=false
|
|
4
|
-
# flake8: noqa: F811
|
|
5
4
|
from collections.abc import AsyncGenerator, AsyncIterable
|
|
6
5
|
from copy import deepcopy
|
|
7
6
|
from dataclasses import dataclass
|
|
@@ -143,6 +142,10 @@ class AWSCRTHTTPClient(http_aio_interfaces.HTTPClient):
|
|
|
143
142
|
|
|
144
143
|
TIMEOUT_EXCEPTIONS = (_CRTTimeoutError,)
|
|
145
144
|
|
|
145
|
+
# CRT can stream request and response data concurrently when the connection
|
|
146
|
+
# negotiates HTTP/2.
|
|
147
|
+
SUPPORTS_DUPLEX_STREAMING = True
|
|
148
|
+
|
|
146
149
|
def __init__(
|
|
147
150
|
self,
|
|
148
151
|
eventloop: _AWSCRTEventLoop | None = None,
|
|
@@ -23,6 +23,7 @@ from smithy_core.interfaces import StreamingBlob as SyncStreamingBlob
|
|
|
23
23
|
from smithy_core.prelude import DOCUMENT
|
|
24
24
|
from smithy_core.schemas import APIOperation
|
|
25
25
|
from smithy_core.serializers import SerializeableShape
|
|
26
|
+
from smithy_core.shapes import ShapeID
|
|
26
27
|
from smithy_core.traits import EndpointTrait, HTTPTrait
|
|
27
28
|
|
|
28
29
|
from ..deserializers import HTTPResponseDeserializer
|
|
@@ -185,15 +186,28 @@ class HttpBindingClientProtocol(HttpClientProtocol):
|
|
|
185
186
|
error_id = self.error_identifier.identify(
|
|
186
187
|
operation=operation, response=response
|
|
187
188
|
)
|
|
189
|
+
if error_id is not None and error_id not in error_registry:
|
|
190
|
+
error_id = self._resolve_error_id(
|
|
191
|
+
operation=operation,
|
|
192
|
+
error_id=error_id,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
retry_after = self._retry_after(response)
|
|
188
196
|
|
|
189
197
|
if error_id is None and self._matches_content_type(response):
|
|
190
198
|
if isinstance(response_body, bytearray):
|
|
191
199
|
response_body = bytes(response_body)
|
|
192
200
|
deserializer = self.payload_codec.create_deserializer(source=response_body)
|
|
193
201
|
document = deserializer.read_document(schema=DOCUMENT)
|
|
202
|
+
document_error_id = document.discriminator
|
|
203
|
+
if document_error_id not in error_registry:
|
|
204
|
+
document_error_id = self._resolve_error_id(
|
|
205
|
+
operation=operation,
|
|
206
|
+
error_id=document_error_id,
|
|
207
|
+
)
|
|
194
208
|
|
|
195
|
-
if
|
|
196
|
-
error_id =
|
|
209
|
+
if document_error_id in error_registry:
|
|
210
|
+
error_id = document_error_id
|
|
197
211
|
if isinstance(response_body, SeekableBytesReader):
|
|
198
212
|
response_body.seek(0)
|
|
199
213
|
|
|
@@ -213,7 +227,10 @@ class HttpBindingClientProtocol(HttpClientProtocol):
|
|
|
213
227
|
response=response,
|
|
214
228
|
body=response_body,
|
|
215
229
|
)
|
|
216
|
-
|
|
230
|
+
modeled_error = error_shape.deserialize(deserializer)
|
|
231
|
+
if retry_after is not None:
|
|
232
|
+
modeled_error.retry_after = retry_after
|
|
233
|
+
return modeled_error
|
|
217
234
|
|
|
218
235
|
message = (
|
|
219
236
|
f"Unknown error for operation {operation.schema.id} "
|
|
@@ -234,8 +251,22 @@ class HttpBindingClientProtocol(HttpClientProtocol):
|
|
|
234
251
|
is_throttling_error=is_throttle,
|
|
235
252
|
is_timeout_error=is_timeout,
|
|
236
253
|
is_retry_safe=is_throttle or is_timeout or None,
|
|
254
|
+
retry_after=retry_after,
|
|
237
255
|
)
|
|
238
256
|
|
|
257
|
+
def _retry_after(self, response: HTTPResponse) -> float | None:
|
|
258
|
+
"""The retry delay in seconds requested by the server, if the response carries one."""
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
def _resolve_error_id(
|
|
262
|
+
self,
|
|
263
|
+
*,
|
|
264
|
+
operation: APIOperation[Any, Any],
|
|
265
|
+
error_id: ShapeID,
|
|
266
|
+
) -> ShapeID:
|
|
267
|
+
"""Resolve a response discriminator to its modeled error shape ID."""
|
|
268
|
+
return error_id
|
|
269
|
+
|
|
239
270
|
def _matches_content_type(self, response: HTTPResponse) -> bool:
|
|
240
271
|
if "content-type" not in response.fields:
|
|
241
272
|
return False
|
|
@@ -22,6 +22,11 @@ class MockHTTPClient(HTTPClient):
|
|
|
22
22
|
|
|
23
23
|
TIMEOUT_EXCEPTIONS = (TimeoutError,)
|
|
24
24
|
|
|
25
|
+
# Declared so duplex (bidirectional) stream operations can be unit tested
|
|
26
|
+
# against this client. Queued responses are returned without consuming the
|
|
27
|
+
# request body, which is compatible with duplex streaming.
|
|
28
|
+
SUPPORTS_DUPLEX_STREAMING = True
|
|
29
|
+
|
|
25
30
|
def __init__(
|
|
26
31
|
self,
|
|
27
32
|
*,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|