@@ -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
0 commit comments