smithy-http 0.3.1__tar.gz → 0.4.1__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.1/.changes/0.4.0.json +12 -0
- smithy_http-0.4.1/.changes/0.4.1.json +8 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/CHANGELOG.md +13 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/PKG-INFO +3 -2
- {smithy_http-0.3.1 → smithy_http-0.4.1}/pyproject.toml +2 -1
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/__init__.py +10 -7
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/aiohttp.py +1 -2
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/crt.py +25 -11
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/interfaces/__init__.py +9 -20
- {smithy_http-0.3.1 → smithy_http-0.4.1}/.changes/0.0.1.json +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/.changes/0.1.0.json +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/.changes/0.2.0.json +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/.changes/0.2.1.json +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/.changes/0.3.0.json +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/.changes/0.3.1.json +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/.gitignore +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/NOTICE +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/README.md +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/__init__.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/auth/__init__.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/auth/apikey.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/identity/__init__.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/identity/apikey.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/interfaces/__init__.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/protocols.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/aio/restjson.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/bindings.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/deserializers.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/endpoints.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/exceptions.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/interceptors/__init__.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/interceptors/user_agent.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/plugins/__init__.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/py.typed +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/restjson.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/serializers.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/testing/__init__.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/testing/mockhttp.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/testing/utils.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/user_agent.py +0 -0
- {smithy_http-0.3.1 → smithy_http-0.4.1}/src/smithy_http/utils.py +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"changes": [
|
|
3
|
+
{
|
|
4
|
+
"type": "breaking",
|
|
5
|
+
"description": "Replace `FieldPosition` enum values with string literals for `Field.kind`. Use \"header\" and \"trailer\" instead of `FieldPosition.HEADER` and `FieldPosition.TRAILER`."
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"type": "dependency",
|
|
9
|
+
"description": "Upgrade CRT to 0.32.0 to fix bug where HTTP2 exceptions could hang indefinitely"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.4.1
|
|
4
|
+
|
|
5
|
+
### Bug fixes
|
|
6
|
+
* Fixed `AWSCRTHTTPClient` to send HTTP/1.1 request bodies via `body_stream` instead of the HTTP/2-only `request_body_generator`.
|
|
7
|
+
|
|
8
|
+
## v0.4.0
|
|
9
|
+
|
|
10
|
+
### Breaking Changes
|
|
11
|
+
* Replace `FieldPosition` enum values with string literals for `Field.kind`. Use "header" and "trailer" instead of `FieldPosition.HEADER` and `FieldPosition.TRAILER`.
|
|
12
|
+
|
|
13
|
+
### Dependencies
|
|
14
|
+
* Upgrade CRT to 0.32.0 to fix bug where HTTP2 exceptions could hang indefinitely
|
|
15
|
+
|
|
3
16
|
## v0.3.1
|
|
4
17
|
|
|
5
18
|
### Bug fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: smithy-http
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
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/
|
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
|
|
24
25
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
25
26
|
Classifier: Topic :: Software Development :: Libraries
|
|
26
27
|
Requires-Python: >=3.12
|
|
@@ -29,7 +30,7 @@ Provides-Extra: aiohttp
|
|
|
29
30
|
Requires-Dist: aiohttp<4.0,>=3.11.12; extra == 'aiohttp'
|
|
30
31
|
Requires-Dist: yarl; extra == 'aiohttp'
|
|
31
32
|
Provides-Extra: awscrt
|
|
32
|
-
Requires-Dist: awscrt~=0.
|
|
33
|
+
Requires-Dist: awscrt~=0.32.0; extra == 'awscrt'
|
|
33
34
|
Description-Content-Type: text/markdown
|
|
34
35
|
|
|
35
36
|
# smithy-http
|
|
@@ -23,6 +23,7 @@ classifiers = [
|
|
|
23
23
|
"Programming Language :: Python :: 3.13",
|
|
24
24
|
"Programming Language :: Python :: 3.14",
|
|
25
25
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
26
|
+
"Programming Language :: Python :: Free Threading :: 2 - Beta",
|
|
26
27
|
"Topic :: Software Development :: Libraries"
|
|
27
28
|
]
|
|
28
29
|
dependencies = [
|
|
@@ -36,7 +37,7 @@ dependencies = [
|
|
|
36
37
|
|
|
37
38
|
[project.optional-dependencies]
|
|
38
39
|
awscrt = [
|
|
39
|
-
"awscrt~=0.
|
|
40
|
+
"awscrt~=0.32.0",
|
|
40
41
|
]
|
|
41
42
|
aiohttp = [
|
|
42
43
|
"aiohttp>=3.11.12, <4.0",
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
from collections import Counter, OrderedDict
|
|
4
4
|
from collections.abc import Iterable, Iterator
|
|
5
|
+
from typing import get_args
|
|
5
6
|
|
|
6
7
|
from . import interfaces
|
|
7
8
|
from .interfaces import FieldPosition
|
|
8
9
|
|
|
9
|
-
__version__ = "0.
|
|
10
|
+
__version__ = "0.4.1"
|
|
11
|
+
|
|
12
|
+
_VALID_FIELD_POSITIONS = frozenset(get_args(FieldPosition))
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class Field(interfaces.Field):
|
|
@@ -24,10 +27,12 @@ class Field(interfaces.Field):
|
|
|
24
27
|
*,
|
|
25
28
|
name: str,
|
|
26
29
|
values: Iterable[str] | None = None,
|
|
27
|
-
kind: FieldPosition =
|
|
30
|
+
kind: FieldPosition = "header",
|
|
28
31
|
):
|
|
29
32
|
self.name = name
|
|
30
33
|
self.values: list[str] = list(values) if values is not None else []
|
|
34
|
+
if kind not in _VALID_FIELD_POSITIONS:
|
|
35
|
+
raise ValueError(f"Unknown field kind: {kind!r}")
|
|
31
36
|
self.kind = kind
|
|
32
37
|
|
|
33
38
|
def add(self, value: str) -> None:
|
|
@@ -79,7 +84,7 @@ class Field(interfaces.Field):
|
|
|
79
84
|
return False
|
|
80
85
|
return (
|
|
81
86
|
self.name == other.name
|
|
82
|
-
and self.kind
|
|
87
|
+
and self.kind == other.kind
|
|
83
88
|
and self.values == other.values
|
|
84
89
|
)
|
|
85
90
|
|
|
@@ -153,7 +158,7 @@ class Fields(interfaces.Fields):
|
|
|
153
158
|
|
|
154
159
|
Used to grab all headers or all trailers.
|
|
155
160
|
"""
|
|
156
|
-
return [entry for entry in self.entries.values() if entry.kind
|
|
161
|
+
return [entry for entry in self.entries.values() if entry.kind == kind]
|
|
157
162
|
|
|
158
163
|
def extend(self, other: interfaces.Fields) -> None:
|
|
159
164
|
"""Merges ``entries`` of ``other`` into the current ``entries``.
|
|
@@ -225,8 +230,6 @@ def tuples_to_fields(
|
|
|
225
230
|
try:
|
|
226
231
|
fields[name].add(value)
|
|
227
232
|
except KeyError:
|
|
228
|
-
fields[name] = Field(
|
|
229
|
-
name=name, values=[value], kind=kind or FieldPosition.HEADER
|
|
230
|
-
)
|
|
233
|
+
fields[name] = Field(name=name, values=[value], kind=kind or "header")
|
|
231
234
|
|
|
232
235
|
return fields
|
|
@@ -28,7 +28,6 @@ from smithy_core.interfaces import URI
|
|
|
28
28
|
|
|
29
29
|
from .. import Field, Fields
|
|
30
30
|
from ..interfaces import (
|
|
31
|
-
FieldPosition,
|
|
32
31
|
HTTPClientConfiguration,
|
|
33
32
|
HTTPRequestConfiguration,
|
|
34
33
|
)
|
|
@@ -125,7 +124,7 @@ class AIOHTTPClient(HTTPClient):
|
|
|
125
124
|
headers[header_name] = Field(
|
|
126
125
|
name=header_name,
|
|
127
126
|
values=[header_val],
|
|
128
|
-
kind=
|
|
127
|
+
kind="header",
|
|
129
128
|
)
|
|
130
129
|
|
|
131
130
|
return HTTPResponse(
|
|
@@ -6,6 +6,7 @@ from collections.abc import AsyncGenerator, AsyncIterable
|
|
|
6
6
|
from copy import deepcopy
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from inspect import iscoroutinefunction
|
|
9
|
+
from io import BytesIO
|
|
9
10
|
from typing import TYPE_CHECKING, Any
|
|
10
11
|
|
|
11
12
|
from awscrt.exceptions import AwsCrtError
|
|
@@ -36,12 +37,12 @@ except ImportError:
|
|
|
36
37
|
from smithy_core import interfaces as core_interfaces
|
|
37
38
|
from smithy_core.aio import interfaces as core_aio_interfaces
|
|
38
39
|
from smithy_core.aio.types import AsyncBytesReader
|
|
40
|
+
from smithy_core.aio.utils import read_streaming_blob_async
|
|
39
41
|
from smithy_core.exceptions import MissingDependencyError
|
|
40
42
|
|
|
41
43
|
from .. import Field, Fields
|
|
42
44
|
from .. import interfaces as http_interfaces
|
|
43
45
|
from ..exceptions import SmithyHTTPError
|
|
44
|
-
from ..interfaces import FieldPosition
|
|
45
46
|
from . import interfaces as http_aio_interfaces
|
|
46
47
|
|
|
47
48
|
# Default buffer size for reading from streams (8 KB)
|
|
@@ -176,13 +177,18 @@ class AWSCRTHTTPClient(http_aio_interfaces.HTTPClient):
|
|
|
176
177
|
crt_request = self._marshal_request(request)
|
|
177
178
|
connection = await self._get_connection(request.destination)
|
|
178
179
|
|
|
179
|
-
#
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
# request_body_generator is HTTP/2-only in CRT; HTTP/1.1 must use body_stream
|
|
181
|
+
if connection.version is crt_http.HttpVersion.Http2:
|
|
182
|
+
crt_stream = connection.request(
|
|
183
|
+
crt_request,
|
|
184
|
+
request_body_generator=self._create_body_generator(request.body),
|
|
185
|
+
)
|
|
186
|
+
else:
|
|
187
|
+
if (
|
|
188
|
+
body_stream := await self._create_body_stream(request.body)
|
|
189
|
+
) is not None:
|
|
190
|
+
crt_request.body_stream = body_stream
|
|
191
|
+
crt_stream = connection.request(crt_request)
|
|
186
192
|
|
|
187
193
|
return await self._await_response(crt_stream)
|
|
188
194
|
except AwsCrtError as e:
|
|
@@ -203,7 +209,7 @@ class AWSCRTHTTPClient(http_aio_interfaces.HTTPClient):
|
|
|
203
209
|
fields[header_name] = Field(
|
|
204
210
|
name=header_name,
|
|
205
211
|
values=[header_val],
|
|
206
|
-
kind=
|
|
212
|
+
kind="header",
|
|
207
213
|
)
|
|
208
214
|
return AWSCRTHTTPResponse(
|
|
209
215
|
status=status_code,
|
|
@@ -294,8 +300,7 @@ class AWSCRTHTTPClient(http_aio_interfaces.HTTPClient):
|
|
|
294
300
|
request.fields.set_field(Field(name="accept", values=["*/*"]))
|
|
295
301
|
|
|
296
302
|
for fld in request.fields.entries.values():
|
|
297
|
-
|
|
298
|
-
if fld.kind.value != FieldPosition.HEADER.value:
|
|
303
|
+
if fld.kind != "header":
|
|
299
304
|
continue
|
|
300
305
|
for val in fld.values:
|
|
301
306
|
headers_list.append((fld.name, val))
|
|
@@ -310,6 +315,15 @@ class AWSCRTHTTPClient(http_aio_interfaces.HTTPClient):
|
|
|
310
315
|
)
|
|
311
316
|
return crt_request
|
|
312
317
|
|
|
318
|
+
async def _create_body_stream(
|
|
319
|
+
self, body: core_aio_interfaces.StreamingBlob
|
|
320
|
+
) -> core_interfaces.BytesReader | None:
|
|
321
|
+
"""Convert various body types to a bytes reader for CRT HTTP/1.1."""
|
|
322
|
+
if core_interfaces.is_bytes_reader(body):
|
|
323
|
+
return body
|
|
324
|
+
buffered = await read_streaming_blob_async(body)
|
|
325
|
+
return BytesIO(buffered) if buffered else None
|
|
326
|
+
|
|
313
327
|
async def _create_body_generator(
|
|
314
328
|
self, body: core_aio_interfaces.StreamingBlob
|
|
315
329
|
) -> AsyncGenerator[bytes, None]:
|
|
@@ -2,29 +2,18 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
from collections.abc import Iterator
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from
|
|
6
|
-
from typing import Protocol
|
|
5
|
+
from typing import Literal, Protocol
|
|
7
6
|
|
|
7
|
+
FieldPosition = Literal["header", "trailer"]
|
|
8
|
+
"""The type of a field.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
"""The type of a field.
|
|
10
|
+
Defines its placement in a request or response.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
HEADER = 0
|
|
16
|
-
"""Header field.
|
|
17
|
-
|
|
18
|
-
In HTTP this is a header as defined in RFC 9110 Section 6.3. Implementations of
|
|
19
|
-
other protocols may use this FieldPosition for similar types of metadata.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
TRAILER = 1
|
|
23
|
-
"""Trailer field.
|
|
12
|
+
header: Header field. In HTTP this is a header as defined in RFC 9110 Section 6.3.
|
|
13
|
+
trailer: Trailer field. In HTTP this is a trailer as defined in RFC 9110 Section 6.5.
|
|
24
14
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"""
|
|
15
|
+
Implementations of other protocols may use this FieldPosition for similar types of metadata.
|
|
16
|
+
"""
|
|
28
17
|
|
|
29
18
|
|
|
30
19
|
class Field(Protocol):
|
|
@@ -40,7 +29,7 @@ class Field(Protocol):
|
|
|
40
29
|
|
|
41
30
|
name: str
|
|
42
31
|
values: list[str]
|
|
43
|
-
kind: FieldPosition =
|
|
32
|
+
kind: FieldPosition = "header"
|
|
44
33
|
|
|
45
34
|
def add(self, value: str) -> None:
|
|
46
35
|
"""Append a value to a field."""
|
|
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
|