[Zope-Checkins] SVN: Zope/branches/2.9/ Collector #2300: delimit *all* headers with CRLF; accumulated_headers and appendHeader use \n for delimeters and never get corrected on output

Martijn Pieters mj at zopatista.com
Tue Mar 27 08:43:32 EDT 2007


Log message for revision 73721:
  Collector #2300: delimit *all* headers with CRLF; accumulated_headers and appendHeader use \n for delimeters and never get corrected on output

Changed:
  U   Zope/branches/2.9/doc/CHANGES.txt
  U   Zope/branches/2.9/lib/python/ZServer/HTTPResponse.py
  U   Zope/branches/2.9/lib/python/ZServer/tests/test_responses.py

-=-
Modified: Zope/branches/2.9/doc/CHANGES.txt
===================================================================
--- Zope/branches/2.9/doc/CHANGES.txt	2007-03-27 12:38:13 UTC (rev 73720)
+++ Zope/branches/2.9/doc/CHANGES.txt	2007-03-27 12:43:31 UTC (rev 73721)
@@ -4,6 +4,12 @@
   Change information for previous versions of Zope can be found in the
   file HISTORY.txt.
 
+  Zope 2.9.8 (unreleased)
+
+   Bugs fixed
+  
+      - Collector #2300: delimit *all* HTTP Response headers with CRLF.
+
   Zope 2.9.7 (2007/03/25)
 
    Bugs fixed

Modified: Zope/branches/2.9/lib/python/ZServer/HTTPResponse.py
===================================================================
--- Zope/branches/2.9/lib/python/ZServer/HTTPResponse.py	2007-03-27 12:38:13 UTC (rev 73720)
+++ Zope/branches/2.9/lib/python/ZServer/HTTPResponse.py	2007-03-27 12:43:31 UTC (rev 73721)
@@ -114,8 +114,15 @@
                     self._chunking=1
                 else:
                     self.setHeader('Connection','close')
+                    
+        headers = headers.items()
+        for line in self.accumulated_headers.splitlines():
+            if line[0] == '\t':
+                headers[-1][1] += '\n' + line
+                continue
+            headers.append(line.split(': ', 1))
 
-        for key, val in headers.items():
+        for key, val in headers:
             if key.lower()==key:
                 # only change non-literal header names
                 key="%s%s" % (key[:1].upper(), key[1:])
@@ -125,10 +132,13 @@
                     key="%s-%s%s" % (key[:l],key[l+1:l+2].upper(),key[l+2:])
                     start=l+1
                     l=key.find('-',start)
+                val = val.replace('\n\t', '\r\n\t')
             append("%s: %s" % (key, val))
         if self.cookies:
-            headersl=headersl+self._cookie_list()
-        headersl[len(headersl):]=[self.accumulated_headers, body]
+            headersl.extend(self._cookie_list())
+            
+        append('')
+        append(body)
         return "\r\n".join(headersl)
 
     _tempfile=None
@@ -151,6 +161,7 @@
 
         """
 
+
         if type(data) != type(''):
             raise TypeError('Value must be a string')
 

Modified: Zope/branches/2.9/lib/python/ZServer/tests/test_responses.py
===================================================================
--- Zope/branches/2.9/lib/python/ZServer/tests/test_responses.py	2007-03-27 12:38:13 UTC (rev 73720)
+++ Zope/branches/2.9/lib/python/ZServer/tests/test_responses.py	2007-03-27 12:43:31 UTC (rev 73721)
@@ -56,7 +56,7 @@
         one = ZServerHTTPResponse(stdout=DummyChannel())
         self.assertRaises(AssertionError,
                           one.setBody, test_streamiterator())
-
+        
 class DummyChannel:
     def __init__(self):
         self.out = StringIO()
@@ -92,8 +92,46 @@
             return self.data
         raise StopIteration
 
+class ZServerHTTPResponseTestCase(unittest.TestCase):
+    """Test ZServer HTTPResponse object"""
+    
+    def _makeOne(self):
+        return ZServerHTTPResponse()
+    
+    def testToString(self):
+        response = self._makeOne()
+        response.headers = {
+            'content-type': 'text/plain',
+            'all-lower-case': 'foo',
+            'Title-Cased': 'bar',
+            'mixed-CasED': 'spam',
+            'multilined': 'eggs\n\tham'}
+        response.accumulated_headers = 'foo-bar: bar\n\tbaz\nFoo-bar: monty\n'
+        response.cookies = dict(foo=dict(value='bar'))
+        response.body = 'A body\nwith multiple lines\n'
+        
+        result = str(response)
+        headers, body = result.rsplit('\r\n\r\n')
+        
+        self.assertEqual(body, response.body)
+        
+        self.assertTrue(headers.startswith('HTTP/1.0 200 OK\r\n'))
+        
+        # 15 header lines all delimited by \r\n
+        self.assertEqual(
+            ['\n' in line for line in headers.split('\r\n')],
+            15 * [False])
+
+        self.assertTrue('Multilined: eggs\r\n\tham\r\n' in headers)
+        self.assertTrue('Foo-Bar: bar\r\n\tbaz\r\n' in headers)
+    
 def test_suite():
-    return unittest.makeSuite(ZServerResponseTestCase)
+    suite = unittest.TestSuite()
+    suite.addTests((
+        unittest.makeSuite(ZServerResponseTestCase),
+        unittest.makeSuite(ZServerHTTPResponseTestCase)
+    ))
+    return suite
 
 if __name__ == "__main__":
     unittest.main(defaultTest="test_suite")



More information about the Zope-Checkins mailing list