Commit 8852bb90 authored by Laurence Rowe's avatar Laurence Rowe

Launchpad #267834: proper separation of HTTP header fields

        using CRLF as requested by RFC 2616. (merged 90980, 92625)
parent 9cd3743d
......@@ -27,6 +27,9 @@ Zope Changes
Bugs fixed
- Launchpad #267834: proper separation of HTTP header fields
using CRLF as requested by RFC 2616. (merged 90980, 92625)
- Launchpad #348223: optimize catalog query by breaking out early from
loop over indexes if the result set is already empty.
......
......@@ -102,7 +102,7 @@ class TestRequestRange(unittest.TestCase):
# Chop off any printed headers (only when response.write was used)
if body:
body = string.split(body, '\n\n', 1)[1]
body = string.split(body, '\r\n\r\n', 1)[1]
return body + rv
......
......@@ -106,7 +106,7 @@ class Functional(sandbox.Sandboxed):
class ResponseWrapper:
'''Decorates a response object with additional introspective methods.'''
_bodyre = re.compile('^$^\n(.*)', re.MULTILINE | re.DOTALL)
_bodyre = re.compile('\r\n\r\n(.*)', re.MULTILINE | re.DOTALL)
def __init__(self, response, outstream, path):
self._response = response
......
......@@ -51,7 +51,7 @@ class MakerequestTests(unittest.TestCase):
item.REQUEST.RESPONSE.write('aaa')
out.seek(0)
written = out.read()
self.failUnless(written.startswith('Status: 200 OK\n'))
self.failUnless(written.startswith('Status: 200 OK\r\n'))
self.failUnless(written.endswith('\naaa'))
def test_environ(self):
......
......@@ -221,7 +221,7 @@ class HTTPResponse(BaseResponse):
# It has already been determined.
return
if (isinstance(status, types.ClassType)
if (isinstance(status, (type, types.ClassType))
and issubclass(status, Exception)):
status = status.__name__
......@@ -246,17 +246,18 @@ class HTTPResponse(BaseResponse):
if lock:
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
the previous value set for the header, if one exists. If the
literal flag is true, the case of the header name is preserved,
otherwise the header name will be lowercased.'''
name, value = _scrubHeader(name, value)
if not scrubbed:
name, value = _scrubHeader(name, value)
key = name.lower()
if accumulate_header(key):
self.accumulated_headers = (
"%s%s: %s\n" % (self.accumulated_headers, name, value))
"%s%s: %s\r\n" % (self.accumulated_headers, name, value))
return
name = literal and name or key
self.headers[name] = value
......@@ -279,7 +280,7 @@ class HTTPResponse(BaseResponse):
any previously set headers with the same name.'''
name, value = _scrubHeader(name, value)
self.accumulated_headers = (
"%s%s: %s\n" % (self.accumulated_headers, name, value))
"%s%s: %s\r\n" % (self.accumulated_headers, name, value))
__setitem__ = setHeader
......@@ -471,21 +472,20 @@ class HTTPResponse(BaseResponse):
# Encode the Unicode data as requested
if self.headers.has_key('content-type'):
match = charset_re.match(self.headers['content-type'])
ct = self.headers.get('content-type')
if ct:
match = charset_re.match(ct)
if match:
encoding = match.group(1)
body = body.encode(encoding)
body = fix_xml_preamble(body, encoding)
return body
else:
ct = self.headers['content-type']
if ct.startswith('text/') or ct.startswith('application/'):
self.headers['content-type'] = '%s; charset=%s' % (ct, default_encoding)
# Use the default character encoding
body = body.encode(default_encoding,'replace')
body = body.encode(default_encoding, 'replace')
body = fix_xml_preamble(body, default_encoding)
return body
......@@ -593,10 +593,10 @@ class HTTPResponse(BaseResponse):
headers = self.headers
if headers.has_key(name):
h = headers[name]
h = "%s%s\n\t%s" % (h,delimiter,value)
h = "%s%s\r\n\t%s" % (h,delimiter,value)
else:
h = value
self.setHeader(name,h)
self.setHeader(name,h, scrubbed=True)
def isHTML(self, s):
s = s.lstrip()
......@@ -895,7 +895,7 @@ class HTTPResponse(BaseResponse):
if self.cookies:
headersl = headersl+self._cookie_list()
headersl[len(headersl):] = [self.accumulated_headers, body]
return '\n'.join(headersl)
return '\r\n'.join(headersl)
def write(self,data):
"""\
......
......@@ -72,10 +72,10 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne()
response.setHeader('foo', 'bar')
response.appendHeader('foo', 'foo')
self.assertEqual(response.headers.get('foo'), 'bar,\n\tfoo')
self.assertEqual(response.headers.get('foo'), 'bar,\r\n\tfoo')
response.setHeader('xxx', 'bar')
response.appendHeader('XXX', 'foo')
self.assertEqual(response.headers.get('xxx'), 'bar,\n\tfoo')
self.assertEqual(response.headers.get('xxx'), 'bar,\r\n\tfoo')
def test_setHeader(self):
response = self._makeOne()
......@@ -151,7 +151,7 @@ class HTTPResponseTests(unittest.TestCase):
response.addHeader('Location',
'http://www.ietf.org/rfc/\r\nrfc2616.txt')
self.assertEqual(response.accumulated_headers,
'Location: http://www.ietf.org/rfc/rfc2616.txt\n')
'Location: http://www.ietf.org/rfc/rfc2616.txt\r\n')
def test_appendHeader_drops_CRLF(self):
# RFC2616 disallows CRLF in a header value.
......@@ -176,8 +176,9 @@ class HTTPResponseTests(unittest.TestCase):
response.setHeader('Set-Cookie',
'violation="http://www.ietf.org/rfc/\r\nrfc2616.txt"')
self.assertEqual(response.accumulated_headers,
'Set-Cookie: allowed="OK"\n' +
'Set-Cookie: violation="http://www.ietf.org/rfc/rfc2616.txt"\n')
'Set-Cookie: allowed="OK"\r\n' +
'Set-Cookie: '
'violation="http://www.ietf.org/rfc/rfc2616.txt"\r\n')
def test_suite():
......
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