Commit edb155f4 authored by Tres Seaver's avatar Tres Seaver

Actually land the CRLF response header fix.

parent ad645a67
...@@ -122,6 +122,10 @@ otherTypes = os.environ.get('DONT_GZIP_MAJOR_MIME_TYPES','').lower() ...@@ -122,6 +122,10 @@ otherTypes = os.environ.get('DONT_GZIP_MAJOR_MIME_TYPES','').lower()
if otherTypes: if otherTypes:
uncompressableMimeMajorTypes += tuple(otherTypes.split(',')) uncompressableMimeMajorTypes += tuple(otherTypes.split(','))
_CRLF = re.compile(r'\r[\n]?')
def _scrubHeader(name, value):
return ''.join(_CRLF.split(str(name))), ''.join(_CRLF.split(str(value)))
class HTTPResponse(BaseResponse): class HTTPResponse(BaseResponse):
"""\ """\
...@@ -242,15 +246,14 @@ class HTTPResponse(BaseResponse): ...@@ -242,15 +246,14 @@ class HTTPResponse(BaseResponse):
if lock: if lock:
self._locked_status = 1 self._locked_status = 1
def setHeader(self, name, value, literal=0): def setHeader(self, name, value, literal=0, scrubbed=False):
'''\ '''\
Sets an HTTP return header "name" with value "value", clearing Sets an HTTP return header "name" with value "value", clearing
the previous value set for the header, if one exists. If the the previous value set for the header, if one exists. If the
literal flag is true, the case of the header name is preserved, literal flag is true, the case of the header name is preserved,
otherwise the header name will be lowercased.''' otherwise the header name will be lowercased.'''
if not scrubbed:
name = str(name) name, value = _scrubHeader(name, value)
value = str(value)
key = name.lower() key = name.lower()
if accumulate_header(key): if accumulate_header(key):
self.accumulated_headers = ( self.accumulated_headers = (
...@@ -275,8 +278,7 @@ class HTTPResponse(BaseResponse): ...@@ -275,8 +278,7 @@ class HTTPResponse(BaseResponse):
'''\ '''\
Set a new HTTP return header with the given value, while retaining Set a new HTTP return header with the given value, while retaining
any previously set headers with the same name.''' any previously set headers with the same name.'''
name = str(name) name, value = _scrubHeader(name, value)
value = str(value)
self.accumulated_headers = ( self.accumulated_headers = (
"%s%s: %s\r\n" % (self.accumulated_headers, name, value)) "%s%s: %s\r\n" % (self.accumulated_headers, name, value))
...@@ -585,8 +587,8 @@ class HTTPResponse(BaseResponse): ...@@ -585,8 +587,8 @@ class HTTPResponse(BaseResponse):
Sets an HTTP return header "name" with value "value", Sets an HTTP return header "name" with value "value",
appending it following a comma if there was a previous value appending it following a comma if there was a previous value
set for the header. ''' set for the header. '''
name = str(name).lower() name, value = _scrubHeader(name, value)
value = str(value) name = name.lower()
headers = self.headers headers = self.headers
if headers.has_key(name): if headers.has_key(name):
...@@ -594,7 +596,7 @@ class HTTPResponse(BaseResponse): ...@@ -594,7 +596,7 @@ class HTTPResponse(BaseResponse):
h = "%s%s\r\n\t%s" % (h,delimiter,value) h = "%s%s\r\n\t%s" % (h,delimiter,value)
else: else:
h = value h = value
self.setHeader(name,h) self.setHeader(name,h, scrubbed=True)
def isHTML(self, s): def isHTML(self, s):
s = s.lstrip() s = s.lstrip()
......
...@@ -145,6 +145,41 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -145,6 +145,41 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne(body=xml, headers={'content-type': 'text/xml; charset=iso-8859-15'}) response = self._makeOne(body=xml, headers={'content-type': 'text/xml; charset=iso-8859-15'})
self.assertEqual(xml==response.body, True) self.assertEqual(xml==response.body, True)
def test_addHeader_drops_CRLF(self):
# RFC2616 disallows CRLF in a header value.
response = self._makeOne()
response.addHeader('Location',
'http://www.ietf.org/rfc/\r\nrfc2616.txt')
self.assertEqual(response.accumulated_headers,
'Location: http://www.ietf.org/rfc/rfc2616.txt\r\n')
def test_appendHeader_drops_CRLF(self):
# RFC2616 disallows CRLF in a header value.
response = self._makeOne()
response.appendHeader('Location',
'http://www.ietf.org/rfc/\r\nrfc2616.txt')
self.assertEqual(response.headers['location'],
'http://www.ietf.org/rfc/rfc2616.txt')
def test_setHeader_drops_CRLF(self):
# RFC2616 disallows CRLF in a header value.
response = self._makeOne()
response.setHeader('Location',
'http://www.ietf.org/rfc/\r\nrfc2616.txt')
self.assertEqual(response.headers['location'],
'http://www.ietf.org/rfc/rfc2616.txt')
def test_setHeader_drops_CRLF_when_accumulating(self):
# RFC2616 disallows CRLF in a header value.
response = self._makeOne()
response.setHeader('Set-Cookie', 'allowed="OK"')
response.setHeader('Set-Cookie',
'violation="http://www.ietf.org/rfc/\r\nrfc2616.txt"')
self.assertEqual(response.accumulated_headers,
'Set-Cookie: allowed="OK"\r\n' +
'Set-Cookie: '
'violation="http://www.ietf.org/rfc/rfc2616.txt"\r\n')
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment