Skip to content

Commit 590bd50

Browse files
committed
Simplify forwarded
1 parent 8cb5ea4 commit 590bd50

2 files changed

Lines changed: 19 additions & 45 deletions

File tree

aiohttp/web_request.py

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,8 @@ class FileField:
9191
# qdtext includes 0x5C to escape 0x5D ('\]')
9292
# qdtext excludes obs-text (because obsoleted, and encoding not specified)
9393

94-
_QUOTED_PAIR: Final[str] = r"\\[\t !-~]"
95-
96-
_QUOTED_STRING: Final[str] = rf'"(?:{_QUOTED_PAIR}|{_QDTEXT})*"'
97-
9894
# This does not have a ReDOS/performance concern as long as it used with re.match().
99-
_FORWARDED_PAIR: Final[str] = rf"({_TOKEN})=({_TOKEN}|{_QUOTED_STRING})(:\d{{1,4}})?"
100-
101-
_QUOTED_PAIR_REPLACE_RE: Final[Pattern[str]] = re.compile(r"\\([\t !-~])")
102-
# same pattern as _QUOTED_PAIR but contains a capture group
103-
95+
_FORWARDED_PAIR: Final[str] = rf'[ \t]*({_TOKEN})=({_TOKEN}|".*")(:\d{{1,4}})?[ \t]*(?:\Z|;)'
10496
_FORWARDED_PAIR_RE: Final[Pattern[str]] = re.compile(_FORWARDED_PAIR)
10597

10698
############################################################
@@ -316,44 +308,28 @@ def forwarded(self) -> tuple[Mapping[str, str], ...]:
316308
Returns a tuple containing one or more immutable dicts
317309
"""
318310
elems = []
319-
field_value = self._message.headers.get(hdrs.FORWARDED, "")
320-
length = len(field_value)
321-
pos = 0
322-
need_separator = False
323-
elem: dict[str, str] = {}
324-
elems.append(types.MappingProxyType(elem))
325-
while 0 <= pos < length:
326-
match = _FORWARDED_PAIR_RE.match(field_value, pos)
327-
if match is not None: # got a valid forwarded-pair
328-
if need_separator:
329-
# bad syntax here, skip to next comma
330-
pos = field_value.find(",", pos)
331-
else:
311+
for field_value in self._message.headers.getall(hdrs.FORWARDED):
312+
length = len(field_value)
313+
pos = 0
314+
need_separator = False
315+
elem: dict[str, str] = {}
316+
elems.append(types.MappingProxyType(elem))
317+
while 0 <= pos < length:
318+
match = _FORWARDED_PAIR_RE.match(field_value, pos)
319+
if match is not None: # got a valid forwarded-pair
332320
name, value, port = match.groups()
333321
if value[0] == '"':
334-
# quoted string: remove quotes and unescape
335-
value = _QUOTED_PAIR_REPLACE_RE.sub(r"\1", value[1:-1])
322+
value = value[1:-1]
336323
if port:
337324
value += port
338325
elem[name.lower()] = value
339326
pos += len(match.group(0))
340-
need_separator = True
341-
elif field_value[pos] == ",": # next forwarded-element
342-
need_separator = False
343-
elem = {}
344-
elems.append(types.MappingProxyType(elem))
345-
pos += 1
346-
elif field_value[pos] == ";": # next forwarded-pair
347-
need_separator = False
348-
pos += 1
349-
elif field_value[pos] in " \t":
350-
# Allow whitespace even between forwarded-pairs, though
351-
# RFC 7239 doesn't. This simplifies code and is in line
352-
# with Postel's law.
353-
pos += 1
354-
else:
355-
# bad syntax here, skip to next comma
356-
pos = field_value.find(",", pos)
327+
elif not field_value[pos:field_value.find(";", pos)].strip(" \t"):
328+
# Empty value
329+
pos = field_value.find(";", pos) + 1
330+
else:
331+
# bad syntax here, skip to next field value
332+
break
357333
return tuple(elems)
358334

359335
@reify

tests/test_web_request.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -731,14 +731,12 @@ def test_multiple_forwarded_headers_bad_syntax() -> None:
731731
headers = CIMultiDict[str]()
732732
headers.add("Forwarded", "for=_1;by=_2")
733733
headers.add("Forwarded", "invalid value")
734-
headers.add("Forwarded", "")
735734
headers.add("Forwarded", "for=_3;by=_4")
736735
req = make_mocked_request("GET", "/", headers=headers)
737-
assert len(req.forwarded) == 4
736+
assert len(req.forwarded) == 3
738737
assert req.forwarded[0]["for"] == "_1"
739738
assert "for" not in req.forwarded[1]
740-
assert "for" not in req.forwarded[2]
741-
assert req.forwarded[3]["by"] == "_4"
739+
assert req.forwarded[2]["by"] == "_4"
742740

743741

744742
def test_multiple_forwarded_headers_injection() -> None:

0 commit comments

Comments
 (0)