[Repoze-checkins] r765 - in repoze.zope2/trunk: . repoze/zope2 repoze/zope2/tests

Chris McDonough chrism at agendaless.com
Tue Mar 4 02:22:16 UTC 2008


Author: Chris McDonough <chrism at agendaless.com>
Date: Mon Mar  3 21:22:16 2008
New Revision: 765

Log:
  - Fix bug reported by Martin Aspeli: repoze.zope2 would choke on
    large images and files (symptom: broken images when images were
    large).  This was due to the fact that the Zope File- and
    Image-rendering machinery used HTTPResponse.write, which
    repoze.zope2's response handling didn't handle properly.  We now
    subclass HTTPResponse (as RepozeHTTPResponse) to solve the issue.



Added:
   repoze.zope2/trunk/repoze/zope2/tests/test_request.py   (contents, props changed)
Modified:
   repoze.zope2/trunk/CHANGES.txt
   repoze.zope2/trunk/repoze/zope2/request.py
   repoze.zope2/trunk/repoze/zope2/tests/base.py
   repoze.zope2/trunk/repoze/zope2/tests/test_z2bob.py
   repoze.zope2/trunk/repoze/zope2/z2bob.py
   repoze.zope2/trunk/setup.py

Modified: repoze.zope2/trunk/CHANGES.txt
==============================================================================
--- repoze.zope2/trunk/CHANGES.txt	(original)
+++ repoze.zope2/trunk/CHANGES.txt	Mon Mar  3 21:22:16 2008
@@ -1,3 +1,12 @@
+0.3.2 (2008-03-03)
+
+  - Fix bug reported by Martin Aspeli: repoze.zope2 would choke on
+    large images and files (symptom: broken images when images were
+    large).  This was due to the fact that the Zope File- and
+    Image-rendering machinery used HTTPResponse.write, which
+    repoze.zope2's response handling didn't handle properly.  We now
+    subclass HTTPResponse (as RepozeHTTPResponse) to solve the issue.
+
 0.3.1 (2008-03-02)
 
   - mkzope2instance now:

Modified: repoze.zope2/trunk/repoze/zope2/request.py
==============================================================================
--- repoze.zope2/trunk/repoze/zope2/request.py	(original)
+++ repoze.zope2/trunk/repoze/zope2/request.py	Mon Mar  3 21:22:16 2008
@@ -18,9 +18,18 @@
 from ZPublisher.HTTPRequest import HTTPRequest
 from tempfile import NamedTemporaryFile
 
+class RepozeHTTPResponse(HTTPResponse):
+    def write(self, data):
+        """
+        Override HTTPResponse.write in order to prevent it from writing
+        headers to output (we'll do that later).
+        """
+        self._wrote = True
+        self.stdout.write(data)
+
 def makeRequest(environ):
     temp = NamedTemporaryFile()
-    response = HTTPResponse(stdout=temp)
+    response = RepozeHTTPResponse(stdout=temp)
     stdin = environ['wsgi.input']
     # The clean = True argument is important.  If we don't pass it,
     # our WSGI environment is copied and replaced, and thus we won't

Modified: repoze.zope2/trunk/repoze/zope2/tests/base.py
==============================================================================
--- repoze.zope2/trunk/repoze/zope2/tests/base.py	(original)
+++ repoze.zope2/trunk/repoze/zope2/tests/base.py	Mon Mar  3 21:22:16 2008
@@ -56,6 +56,9 @@
     def setStatus(self, v):
         self.setHeader('status', v)
         self.status = v
+    def write(self, data):
+        self._wrote = True
+        self.stdout.write(data)
 
 class DummyRequest:
     def __init__(self, env):

Added: repoze.zope2/trunk/repoze/zope2/tests/test_request.py
==============================================================================
--- (empty file)
+++ repoze.zope2/trunk/repoze/zope2/tests/test_request.py	Mon Mar  3 21:22:16 2008
@@ -0,0 +1,39 @@
+import unittest
+
+class TestMakeRequest(unittest.TestCase):
+    def _getFUT(self):
+        from repoze.zope2.z2bob import makeRequest
+        return makeRequest
+        
+    def test_makeRequest(self):
+        from repoze.zope2.tests.base import _makeEnviron
+        makeRequest = self._getFUT()
+        environ = _makeEnviron()
+        request = makeRequest(environ)
+        self.failUnless(request.environ is environ, request.stdin)
+        self.failUnless(request.stdin is environ['wsgi.input'])
+        self.assertNotEqual(request.response, None)
+        from repoze.zope2.request import RepozeHTTPResponse
+        self.failUnless(isinstance(request.response, RepozeHTTPResponse))
+
+class TestRepozeHTTPResponse(unittest.TestCase):
+    def _getTargetClass(self):
+        from repoze.zope2.request import RepozeHTTPResponse
+        return RepozeHTTPResponse
+
+    def _makeOne(self, stdout):
+        return self._getTargetClass()(stdout=stdout)
+
+    def test_write(self):
+        from StringIO import StringIO
+        stdout = StringIO()
+        response = self._makeOne(stdout)
+        response.write('hello')
+        self.assertEqual(stdout.getvalue(), 'hello')
+        self.assertEqual(response._wrote, True)
+        response.write(' there')
+        self.assertEqual(stdout.getvalue(), 'hello there')
+        self.assertEqual(response._wrote, True)
+        
+
+    

Modified: repoze.zope2/trunk/repoze/zope2/tests/test_z2bob.py
==============================================================================
--- repoze.zope2/trunk/repoze/zope2/tests/test_z2bob.py	(original)
+++ repoze.zope2/trunk/repoze/zope2/tests/test_z2bob.py	Mon Mar  3 21:22:16 2008
@@ -64,14 +64,6 @@
         finally:
             z2bob.db = None
 
-    def test_makeRequest(self):
-        from repoze.zope2.z2bob import makeRequest
-        environ = _makeEnviron()
-        request = makeRequest(environ)
-        self.failUnless(request.environ is environ, request.stdin)
-        self.failUnless(request.stdin is environ['wsgi.input'])
-        self.assertNotEqual(request.response, None)
-
     def test_quote(self):
         from repoze.zope2.z2bob import quote
         quoted = quote('http://foo/bar/@@view')
@@ -657,17 +649,18 @@
         self.assertEqual(headers[1], ('Content-Type',
                                       'text/plain; charset=utf-8'))
 
-    def test_map_result_response_stdout(self):
+    def test_map_result_response_written(self):
+        # if response.write is used, a tempfile is used to hold
+        # the result, and it becomes the body of the response
         helper = self._makeOne()
-        helper.request.response.headers['status'] = '200 OK'
-        from StringIO import StringIO
-        stdout = StringIO()
-        stdout.write('Status: 401 Unauthorized\n\n\nbody')
-        helper.request.response.stdout = stdout
+        response = helper.request.response
+        response.headers['status'] = '200 OK'
+        response.write('body')
         status, headers, result = helper.map_result(None)
-        self.assertEqual(status, '401')
-        self.assertEqual(result, stdout)
-        self.assertEqual(stdout.read(), 'body')
+        self.assertEqual(response._wrote, True)
+        self.assertEqual(status, '200 OK')
+        self.assertEqual(result, response.stdout)
+        self.assertEqual(result.read(), 'body')
 
     def test_map_result_headers_normalized(self):
         helper = self._makeOne()

Modified: repoze.zope2/trunk/repoze/zope2/z2bob.py
==============================================================================
--- repoze.zope2/trunk/repoze/zope2/z2bob.py	(original)
+++ repoze.zope2/trunk/repoze/zope2/z2bob.py	Mon Mar  3 21:22:16 2008
@@ -353,29 +353,9 @@
         response = request.response
 
         if response.stdout.tell():
-            stdout = response.stdout
-            stdout.seek(0)
-            result = stdout
-            last = None
-            # the headers get written too, we need to unwind them
-            while 1:
-                line = stdout.readline()
-                if not line:
-                    break
-                if last == '\n' and line == '\n':
-                    break
-                if line:
-                    if ':' in line:
-                        k, v = line.split(':', 1)
-                        k = k.strip()
-                        v = v.strip()
-                        if k.lower() == 'status':
-                            response.setStatus(v.split()[0])
-                        else:
-                            response.setHeader(k, v)
-                last = line
-
-            result = stdout
+            # someone used response.write
+            response.stdout.seek(0)
+            result = response.stdout
 
         status = response.headers.get('status', None)
         if status is None:

Modified: repoze.zope2/trunk/setup.py
==============================================================================
--- repoze.zope2/trunk/setup.py	(original)
+++ repoze.zope2/trunk/setup.py	Mon Mar  3 21:22:16 2008
@@ -12,7 +12,7 @@
 #
 ##############################################################################
 
-__version__ = '0.3.1'
+__version__ = '0.3.2'
 
 from ez_setup import use_setuptools
 use_setuptools()


More information about the Repoze-checkins mailing list