[Zope-Checkins] SVN: Zope/branches/tseaver-fix_wsgi/src/ZPublisher/ Factor out computation of the list of response headers from stringifying them.
Tres Seaver
tseaver at palladion.com
Sat May 29 00:24:45 EDT 2010
Log message for revision 112831:
Factor out computation of the list of response headers from stringifying them.
Allows WSGIHTTPResponse do reuse them as tuples.
Changed:
U Zope/branches/tseaver-fix_wsgi/src/ZPublisher/HTTPResponse.py
U Zope/branches/tseaver-fix_wsgi/src/ZPublisher/tests/testHTTPResponse.py
-=-
Modified: Zope/branches/tseaver-fix_wsgi/src/ZPublisher/HTTPResponse.py
===================================================================
--- Zope/branches/tseaver-fix_wsgi/src/ZPublisher/HTTPResponse.py 2010-05-28 21:55:20 UTC (rev 112830)
+++ Zope/branches/tseaver-fix_wsgi/src/ZPublisher/HTTPResponse.py 2010-05-29 04:24:44 UTC (rev 112831)
@@ -887,9 +887,9 @@
# of name=value pairs may be quoted.
if attrs.get('quoted', True):
- cookie = 'Set-Cookie: %s="%s"' % (name, quote(attrs['value']))
+ cookie = '%s="%s"' % (name, quote(attrs['value']))
else:
- cookie = 'Set-Cookie: %s=%s' % (name, quote(attrs['value']))
+ cookie = '%s=%s' % (name, quote(attrs['value']))
for name, v in attrs.items():
name = name.lower()
if name == 'expires':
@@ -908,32 +908,27 @@
# and block read/write access via JavaScript
elif name == 'http_only' and v:
cookie = '%s; HTTPOnly' % cookie
- cookie_list.append(cookie)
+ cookie_list.append(('Set-Cookie', cookie))
# Should really check size of cookies here!
return cookie_list
- def __str__(self,
- html_search=re.compile('<html>',re.I).search,
- ):
- if self._wrote:
- return '' # Streaming output was used.
+ def listHeaders(self):
+ """ Return a list of (key, value) pairs for our headers.
- headers = self.headers
+ o Do appropriate case normalization.
+ """
body = self.body
+ if (not 'content-length' in self.headers and
+ not 'transfer-encoding' in self.headers):
+ self.setHeader('content-length', len(body))
- if not headers.has_key('content-length') and \
- not headers.has_key('transfer-encoding'):
- self.setHeader('content-length',len(body))
+ result = [
+ ('X-Powered-By', 'Zope (www.zope.org), Python (www.python.org)')
+ ]
- chunks = []
- append = chunks.append
-
- # status header must come first.
- append("Status: %d %s" % (self.status, self.errmsg))
- append("X-Powered-By: Zope (www.zope.org), Python (www.python.org)")
- for key, value in headers.items():
+ for key, value in self.headers.items():
if key.lower() == key:
# only change non-literal header names
key = "%s%s" % (key[:1].upper(), key[1:])
@@ -943,12 +938,31 @@
key = "%s-%s%s" % (key[:l],key[l+1:l+2].upper(),key[l+2:])
start = l + 1
l = key.find('-', start)
- append("%s: %s" % (key, value))
- chunks.extend(self._cookie_list())
- for key, value in self.accumulated_headers:
- append("%s: %s" % (key, value))
- append('') # RFC 2616 mandates empty line between headers and payload
- append(body)
+ result.append((key, value))
+
+ result.extend(self._cookie_list())
+ result.extend(self.accumulated_headers)
+ return result
+
+ def __str__(self,
+ html_search=re.compile('<html>',re.I).search,
+ ):
+ if self._wrote:
+ return '' # Streaming output was used.
+
+ headers = self.headers
+ body = self.body
+
+ chunks = []
+
+ # status header must come first.
+ chunks.append("Status: %d %s" % (self.status, self.errmsg))
+
+ for key, value in self.listHeaders():
+ chunks.append("%s: %s" % (key, value))
+ # RFC 2616 mandates empty line between headers and payload
+ chunks.append('')
+ chunks.append(body)
return '\r\n'.join(chunks)
def write(self,data):
Modified: Zope/branches/tseaver-fix_wsgi/src/ZPublisher/tests/testHTTPResponse.py
===================================================================
--- Zope/branches/tseaver-fix_wsgi/src/ZPublisher/tests/testHTTPResponse.py 2010-05-28 21:55:20 UTC (rev 112830)
+++ Zope/branches/tseaver-fix_wsgi/src/ZPublisher/tests/testHTTPResponse.py 2010-05-29 04:24:44 UTC (rev 112831)
@@ -206,7 +206,7 @@
self.assertEqual(cookie.get('quoted'), True)
cookies = response._cookie_list()
self.assertEqual(len(cookies), 1)
- self.assertEqual(cookies[0], 'Set-Cookie: foo="bar"')
+ self.assertEqual(cookies[0], ('Set-Cookie', 'foo="bar"'))
def test_setCookie_w_expires(self):
EXPIRES = 'Wed, 31-Dec-97 23:59:59 GMT'
@@ -221,7 +221,7 @@
cookies = response._cookie_list()
self.assertEqual(len(cookies), 1)
self.assertEqual(cookies[0],
- 'Set-Cookie: foo="bar"; Expires=%s' % EXPIRES)
+ ('Set-Cookie', 'foo="bar"; Expires=%s' % EXPIRES))
def test_setCookie_w_domain(self):
response = self._makeOne()
@@ -235,7 +235,7 @@
cookies = response._cookie_list()
self.assertEqual(len(cookies), 1)
self.assertEqual(cookies[0],
- 'Set-Cookie: foo="bar"; Domain=example.com')
+ ('Set-Cookie', 'foo="bar"; Domain=example.com'))
def test_setCookie_w_path(self):
response = self._makeOne()
@@ -248,7 +248,7 @@
cookies = response._cookie_list()
self.assertEqual(len(cookies), 1)
- self.assertEqual(cookies[0], 'Set-Cookie: foo="bar"; Path=/')
+ self.assertEqual(cookies[0], ('Set-Cookie', 'foo="bar"; Path=/'))
def test_setCookie_w_comment(self):
response = self._makeOne()
@@ -261,7 +261,8 @@
cookies = response._cookie_list()
self.assertEqual(len(cookies), 1)
- self.assertEqual(cookies[0], 'Set-Cookie: foo="bar"; Comment=COMMENT')
+ self.assertEqual(cookies[0],
+ ('Set-Cookie', 'foo="bar"; Comment=COMMENT'))
def test_setCookie_w_secure_true_value(self):
response = self._makeOne()
@@ -274,7 +275,7 @@
cookies = response._cookie_list()
self.assertEqual(len(cookies), 1)
- self.assertEqual(cookies[0], 'Set-Cookie: foo="bar"; Secure')
+ self.assertEqual(cookies[0], ('Set-Cookie','foo="bar"; Secure'))
def test_setCookie_w_secure_false_value(self):
response = self._makeOne()
@@ -287,7 +288,7 @@
cookies = response._cookie_list()
self.assertEqual(len(cookies), 1)
- self.assertEqual(cookies[0], 'Set-Cookie: foo="bar"')
+ self.assertEqual(cookies[0], ('Set-Cookie', 'foo="bar"'))
def test_setCookie_w_httponly_true_value(self):
response = self._makeOne()
@@ -300,7 +301,7 @@
cookie_list = response._cookie_list()
self.assertEqual(len(cookie_list), 1)
- self.assertEqual(cookie_list[0], 'Set-Cookie: foo="bar"; HTTPOnly')
+ self.assertEqual(cookie_list[0], ('Set-Cookie', 'foo="bar"; HTTPOnly'))
def test_setCookie_w_httponly_false_value(self):
response = self._makeOne()
@@ -313,7 +314,7 @@
cookie_list = response._cookie_list()
self.assertEqual(len(cookie_list), 1)
- self.assertEqual(cookie_list[0], 'Set-Cookie: foo="bar"')
+ self.assertEqual(cookie_list[0], ('Set-Cookie', 'foo="bar"'))
def test_setCookie_unquoted(self):
response = self._makeOne()
@@ -325,7 +326,7 @@
cookie_list = response._cookie_list()
self.assertEqual(len(cookie_list), 1)
- self.assertEqual(cookie_list[0], 'Set-Cookie: foo=bar')
+ self.assertEqual(cookie_list[0], ('Set-Cookie', 'foo=bar'))
def test_appendCookie_w_existing(self):
response = self._makeOne()
@@ -928,6 +929,130 @@
else:
self.fail("Didn't raise Unauthorized")
+ def test_listHeaders_empty(self):
+ response = self._makeOne()
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ])
+
+ def test_listHeaders_already_wrote(self):
+ # listHeaders doesn't do the short-circuit on _wrote.
+ response = self._makeOne()
+ response._wrote = True
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ])
+
+ def test_listHeaders_existing_content_length(self):
+ response = self._makeOne()
+ response.setHeader('Content-Length', 42)
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '42'),
+ ])
+
+ def test_listHeaders_existing_transfer_encoding(self):
+ # If 'Transfer-Encoding' is set, don't force 'Content-Length'.
+ response = self._makeOne()
+ response.setHeader('Transfer-Encoding', 'slurry')
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Transfer-Encoding', 'slurry'),
+ ])
+
+ def test_listHeaders_after_setHeader(self):
+ response = self._makeOne()
+ response.setHeader('x-consistency', 'Foolish')
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ('X-Consistency', 'Foolish'),
+ ])
+
+ def test_listHeaders_after_setHeader_literal(self):
+ response = self._makeOne()
+ response.setHeader('X-consistency', 'Foolish', literal=True)
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ('X-consistency', 'Foolish'),
+ ])
+
+ def test_listHeaders_after_redirect(self):
+ response = self._makeOne()
+ response.redirect('http://example.com/')
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ('Location', 'http://example.com/'),
+ ])
+
+ def test_listHeaders_after_setCookie_appendCookie(self):
+ response = self._makeOne()
+ response.setCookie('foo', 'bar', path='/')
+ response.appendCookie('foo', 'baz')
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ('Set-Cookie', 'foo="bar%3Abaz"; Path=/'),
+ ])
+
+ def test_listHeaders_after_expireCookie(self):
+ response = self._makeOne()
+ response.expireCookie('qux', path='/')
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ('Set-Cookie', 'qux="deleted"; '
+ 'Path=/; '
+ 'Expires=Wed, 31-Dec-97 23:59:59 GMT; '
+ 'Max-Age=0'),
+ ])
+
+ def test_listHeaders_after_addHeader(self):
+ response = self._makeOne()
+ response.addHeader('X-Consistency', 'Foolish')
+ response.addHeader('X-Consistency', 'Oatmeal')
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '0'),
+ ('X-Consistency', 'Foolish'),
+ ('X-Consistency', 'Oatmeal'),
+ ])
+
+ def test_listHeaders_w_body(self):
+ response = self._makeOne()
+ response.setBody('BLAH')
+ headers = response.listHeaders()
+ self.assertEqual(headers,
+ [('X-Powered-By', 'Zope (www.zope.org), '
+ 'Python (www.python.org)'),
+ ('Content-Length', '4'),
+ ('Content-Type', 'text/plain; charset=iso-8859-15'),
+ ])
+
def test___str__already_wrote(self):
response = self._makeOne()
response._wrote = True
More information about the Zope-Checkins
mailing list