99# existence of which force quoting of the parameter value.
1010import re
1111tspecials = re .compile (r'[ \(\)<>@,;:\\"/\[\]\?=]' )
12- _control_chars_re = re .compile (r'[\x00-\x1F\x7F]' )
12+ # Disallowed characters for headers and values.
13+ # HTAB (\x09) is allowed in header values, but
14+ # not in header names. (RFC 9110 Section 5.5)
15+ _name_disallowed_re = re .compile (r'[\x00-\x1F\x7F]' )
16+ _value_disallowed_re = re .compile (r'[\x00-\x08\x0A-\x1F\x7F]' )
1317
1418def _formatparam (param , value = None , quote = 1 ):
1519 """Convenience function to format and return a key=value pair.
@@ -36,13 +40,14 @@ def __init__(self, headers=None):
3640 self ._headers = headers
3741 if __debug__ :
3842 for k , v in headers :
39- self ._convert_string_type (k )
40- self ._convert_string_type (v )
43+ self ._convert_string_type (k , name = True )
44+ self ._convert_string_type (v , name = False )
4145
42- def _convert_string_type (self , value ):
46+ def _convert_string_type (self , value , * , name ):
4347 """Convert/check value type."""
4448 if type (value ) is str :
45- if _control_chars_re .search (value ):
49+ regex = (_name_disallowed_re if name else _value_disallowed_re )
50+ if regex .search (value ):
4651 raise ValueError ("Control characters not allowed in headers" )
4752 return value
4853 raise AssertionError ("Header names/values must be"
@@ -56,14 +61,14 @@ def __setitem__(self, name, val):
5661 """Set the value of a header."""
5762 del self [name ]
5863 self ._headers .append (
59- (self ._convert_string_type (name ), self ._convert_string_type (val )))
64+ (self ._convert_string_type (name , name = True ), self ._convert_string_type (val , name = False )))
6065
6166 def __delitem__ (self ,name ):
6267 """Delete all occurrences of a header, if present.
6368
6469 Does *not* raise an exception if the header is missing.
6570 """
66- name = self ._convert_string_type (name .lower ())
71+ name = self ._convert_string_type (name .lower (), name = True )
6772 self ._headers [:] = [kv for kv in self ._headers if kv [0 ].lower () != name ]
6873
6974 def __getitem__ (self ,name ):
@@ -90,13 +95,13 @@ def get_all(self, name):
9095 fields deleted and re-inserted are always appended to the header list.
9196 If no fields exist with the given name, returns an empty list.
9297 """
93- name = self ._convert_string_type (name .lower ())
98+ name = self ._convert_string_type (name .lower (), name = True )
9499 return [kv [1 ] for kv in self ._headers if kv [0 ].lower ()== name ]
95100
96101
97102 def get (self ,name ,default = None ):
98103 """Get the first header value for 'name', or return 'default'"""
99- name = self ._convert_string_type (name .lower ())
104+ name = self ._convert_string_type (name .lower (), name = True )
100105 for k ,v in self ._headers :
101106 if k .lower ()== name :
102107 return v
@@ -151,8 +156,8 @@ def setdefault(self,name,value):
151156 and value 'value'."""
152157 result = self .get (name )
153158 if result is None :
154- self ._headers .append ((self ._convert_string_type (name ),
155- self ._convert_string_type (value )))
159+ self ._headers .append ((self ._convert_string_type (name , name = True ),
160+ self ._convert_string_type (value , name = False )))
156161 return value
157162 else :
158163 return result
@@ -175,13 +180,13 @@ def add_header(self, _name, _value, **_params):
175180 """
176181 parts = []
177182 if _value is not None :
178- _value = self ._convert_string_type (_value )
183+ _value = self ._convert_string_type (_value , name = False )
179184 parts .append (_value )
180185 for k , v in _params .items ():
181- k = self ._convert_string_type (k )
186+ k = self ._convert_string_type (k , name = True )
182187 if v is None :
183188 parts .append (k .replace ('_' , '-' ))
184189 else :
185- v = self ._convert_string_type (v )
190+ v = self ._convert_string_type (v , name = False )
186191 parts .append (_formatparam (k .replace ('_' , '-' ), v ))
187- self ._headers .append ((self ._convert_string_type (_name ), "; " .join (parts )))
192+ self ._headers .append ((self ._convert_string_type (_name , name = True ), "; " .join (parts )))
0 commit comments