Skip to content

Commit c295398

Browse files
Split singleton headers
Header lists may be serialized either as the same header multiple times, or as a comma-delimted string. This updates the header list deserializer to split a list value if there is only one. It takes care to ensure that lists of http-date headers are supported with and without quoting.
1 parent 81baaa8 commit c295398

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

packages/smithy-http/src/smithy_http/deserializers.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
from .aio.interfaces import HTTPResponse
2626
from .interfaces import Field, Fields
27+
from .utils import split_header
2728

2829
if TYPE_CHECKING:
2930
from smithy_core.aio.interfaces import StreamingBlob as AsyncStreamingBlob
@@ -177,7 +178,17 @@ def __init__(self, field: Field) -> None:
177178
def read_list(
178179
self, schema: Schema, consumer: Callable[["ShapeDeserializer"], None]
179180
) -> None:
180-
for value in self._field.values:
181+
values = self._field.values
182+
if len(values) == 1:
183+
is_http_date_list = False
184+
value_schema = schema.members["member"]
185+
if value_schema.shape_type is ShapeType.TIMESTAMP:
186+
trait = value_schema.get_trait(TimestampFormatTrait)
187+
is_http_date_list = (
188+
trait is None or trait.format is TimestampFormat.HTTP_DATE
189+
)
190+
values = split_header(values[0], is_http_date_list)
191+
for value in values:
181192
consumer(HTTPHeaderDeserializer(value))
182193

183194

packages/smithy-http/tests/unit/test_serializers.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,90 @@ def header_cases() -> list[HTTPMessageTestCase]:
12901290
]
12911291

12921292

1293+
def header_deser_cases() -> list[HTTPMessageTestCase]:
1294+
return [
1295+
HTTPMessageTestCase(
1296+
HTTPHeaders(string_list_member=["foo", "bar", "baz"]),
1297+
HTTPMessage(fields=tuples_to_fields([("stringList", "foo, bar, baz")])),
1298+
),
1299+
HTTPMessageTestCase(
1300+
HTTPHeaders(string_list_member=["foo, bar", "spam", "eggs"]),
1301+
HTTPMessage(
1302+
fields=tuples_to_fields([("stringList", '"foo, bar", spam, eggs')])
1303+
),
1304+
),
1305+
HTTPMessageTestCase(
1306+
HTTPHeaders(
1307+
http_date_list_timestamp_member=[
1308+
datetime.datetime(2025, 1, 1, tzinfo=UTC),
1309+
datetime.datetime(2024, 1, 1, tzinfo=UTC),
1310+
]
1311+
),
1312+
HTTPMessage(
1313+
fields=tuples_to_fields(
1314+
[
1315+
(
1316+
"httpDateListTimestamp",
1317+
"Wed, 01 Jan 2025 00:00:00 GMT, Mon, 01 Jan 2024 00:00:00 GMT",
1318+
),
1319+
]
1320+
),
1321+
),
1322+
),
1323+
HTTPMessageTestCase(
1324+
HTTPHeaders(
1325+
http_date_list_timestamp_member=[
1326+
datetime.datetime(2025, 1, 1, tzinfo=UTC),
1327+
datetime.datetime(2024, 1, 1, tzinfo=UTC),
1328+
]
1329+
),
1330+
HTTPMessage(
1331+
fields=tuples_to_fields(
1332+
[
1333+
(
1334+
"httpDateListTimestamp",
1335+
'"Wed, 01 Jan 2025 00:00:00 GMT", "Mon, 01 Jan 2024 00:00:00 GMT"',
1336+
),
1337+
]
1338+
),
1339+
),
1340+
),
1341+
HTTPMessageTestCase(
1342+
HTTPHeaders(
1343+
date_time_list_timestamp_member=[
1344+
datetime.datetime(2025, 1, 1, tzinfo=UTC),
1345+
datetime.datetime(2024, 1, 1, tzinfo=UTC),
1346+
]
1347+
),
1348+
HTTPMessage(
1349+
fields=tuples_to_fields(
1350+
[
1351+
(
1352+
"dateTimeListTimestamp",
1353+
"2025-01-01T00:00:00Z, 2024-01-01T00:00:00Z",
1354+
),
1355+
]
1356+
),
1357+
),
1358+
),
1359+
HTTPMessageTestCase(
1360+
HTTPHeaders(
1361+
epoch_list_timestamp_member=[
1362+
datetime.datetime(2025, 1, 1, tzinfo=UTC),
1363+
datetime.datetime(2024, 1, 1, tzinfo=UTC),
1364+
]
1365+
),
1366+
HTTPMessage(
1367+
fields=tuples_to_fields(
1368+
[
1369+
("epochListTimestamp", "1735689600, 1704067200"),
1370+
]
1371+
),
1372+
),
1373+
),
1374+
]
1375+
1376+
12931377
def empty_prefix_header_ser_cases() -> list[HTTPMessageTestCase]:
12941378
return [
12951379
HTTPMessageTestCase(
@@ -1678,7 +1762,10 @@ async def test_serialize_http_response(case: HTTPMessageTestCase) -> None:
16781762

16791763

16801764
RESPONSE_DESER_CASES: list[HTTPMessageTestCase] = (
1681-
header_cases() + empty_prefix_header_deser_cases() + payload_cases()
1765+
header_cases()
1766+
+ header_deser_cases()
1767+
+ empty_prefix_header_deser_cases()
1768+
+ payload_cases()
16821769
)
16831770

16841771

0 commit comments

Comments
 (0)