Skip to content

Commit d0a7526

Browse files
chuenchen309claude
andcommitted
fix(datastructures): extend_header_value only reads the first duplicate header entry, dropping the rest
`extend_header_value()` used `self.get(key)` to read the existing value before joining in the new one. `MutableMapping.get()` is backed by `__getitem__`, which returns only the FIRST raw header entry matching `key` -- but a header can legitimately appear as multiple separate raw entries in `self.headers` (see `add()`'s own docstring: "This method keeps duplicates"), not just as one comma-joined value. Concrete failure: add two raw "vary" entries ("accept", "accept-encoding"), then extend_header_value("vary", "user-agent") -- the result silently drops "accept-encoding" entirely, leaving only "accept,user-agent". Fix: use `getall()` instead of `get()` to collect ALL duplicate entries, flattening each entry's own comma-separated list (matching the existing single-entry behavior) before joining in the new value. `getall()` currently raises `KeyError` when the header isn't present at all (a separate falsy-default bug also present in this method, which I've already reported/fixed independently in PR #4924) -- catching that `KeyError` here keeps this fix correct regardless of merge order between the two PRs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 4a43322 commit d0a7526

2 files changed

Lines changed: 18 additions & 3 deletions

File tree

litestar/datastructures/headers.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,13 @@ def extend_header_value(self, key: str, value: str) -> None:
188188
Returns:
189189
None
190190
"""
191-
existing = self.get(key)
192-
if existing is not None:
193-
value = ",".join([*existing.split(","), value])
191+
try:
192+
existing_entries = self.getall(key)
193+
except KeyError:
194+
existing_entries = []
195+
existing_values = [v for existing in existing_entries for v in existing.split(",")]
196+
if existing_values:
197+
value = ",".join([*existing_values, value])
194198
self[key] = value
195199

196200
def __getitem__(self, key: str) -> str:

tests/unit/test_datastructures/test_headers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,17 @@ def test_mutable_scope_headers_from_tuple_extend_header_value_new_header(
184184
assert list(raw_headers_tuple) == [(b"foo", b"bar"), (b"bar", b"baz")]
185185

186186

187+
def test_mutable_scope_headers_extend_header_value_preserves_duplicate_entries(
188+
mutable_headers: MutableScopeHeaders,
189+
) -> None:
190+
# A header can legitimately appear as multiple separate raw entries (not just a
191+
# single comma-joined value). extend_header_value must not silently drop any of
192+
# them.
193+
mutable_headers.add("foo", "baz")
194+
mutable_headers.extend_header_value("foo", "qux")
195+
assert mutable_headers.getall("foo") == ["bar,baz,qux"]
196+
197+
187198
def test_mutable_scope_headers_getitem(mutable_headers: MutableScopeHeaders, existing_headers_key: str) -> None:
188199
assert mutable_headers[existing_headers_key] == "bar"
189200

0 commit comments

Comments
 (0)