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 _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org https://mail.zope.org/mailman/listinfo/zope-checkins