[Zope3-checkins] SVN: Zope3/branches/stephan_and_jim-response-refactor/ Got servers running and fixed some tests.

Stephan Richter srichter at cosmos.phy.tufts.edu
Fri Sep 2 19:12:37 EDT 2005


Log message for revision 38273:
  Got servers running and fixed some tests.
  

Changed:
  U   Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/__init__.py
  U   Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/publisherhttpserver.py
  D   Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_publisherserver.py
  A   Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_wsgiserver.py
  U   Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/wsgihttpserver.py
  U   Zope3/branches/stephan_and_jim-response-refactor/zope.conf.in

-=-
Modified: Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/__init__.py
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/__init__.py	2005-09-02 23:08:40 UTC (rev 38272)
+++ Zope3/branches/stephan_and_jim-response-refactor/src/zope/app/wsgi/__init__.py	2005-09-02 23:12:37 UTC (rev 38273)
@@ -42,8 +42,12 @@
         """See zope.app.wsgi.interfaces.IWSGIApplication"""
         request = self.requestFactory(environ['wsgi.input'], environ)
         response = request.response
-        publish(request)
 
+        # Let's support post-mortem debugging
+        handle_errors = environ.get('wsgi.handleErrors', True)
+
+        publish(request, handle_errors=handle_errors)
+
         # Start the WSGI server response
         start_response(response.getStatusString(), response.getHeaders())
 

Modified: Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/publisherhttpserver.py
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/publisherhttpserver.py	2005-09-02 23:08:40 UTC (rev 38272)
+++ Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/publisherhttpserver.py	2005-09-02 23:12:37 UTC (rev 38273)
@@ -15,70 +15,54 @@
 
 $Id$
 """
-from zope.server.http.httpserver import HTTPServer
+import zope.deprecation
+from zope.server.http import wsgihttpserver
 from zope.publisher.publish import publish
 import zope.security.management
 
 
-class PublisherHTTPServer(HTTPServer):
-    """Zope Publisher-specific HTTP Server"""
+class PublisherHTTPServer(wsgihttpserver.WSGIHTTPServer):
 
     def __init__(self, request_factory, sub_protocol=None, *args, **kw):
 
-        # This 'adjustment' to args[0] (the hostname for the HTTP server)
-        # under Windows is to get Zope to accept connections from other machines
-        # when the host name is omitted from the server address (in zope.conf).
-        # The address comes in as 'localhost' from ZConfig by way of some
-        # dubious special handling. See collector issue 383 for more info.
-        import sys
-        if sys.platform[:3] == "win" and args[0] == 'localhost':
-            args = ('',) + args[1:]
+        def application(environ, start_response):
+            request = request_factory(environ['wsgi.input'], environ)
+            response = request.response
+            publish(request)
+            start_response(response.getStatusString(), response.getHeaders())
+            return response.result.body
 
+        return super(PublisherHTTPServer, self).__init__(
+            application, sub_protocol, *args, **kw)
 
-        # The common HTTP
-        self.request_factory = request_factory
 
-        # An HTTP server is not limited to serving up HTML; it can be
-        # used for other protocols, like XML-RPC, SOAP and so as well
-        # Here we just allow the logger to output the sub-protocol type.
-        if sub_protocol:
-            self.SERVER_IDENT += ' (%s)' %str(sub_protocol)
+class PMDBHTTPServer(wsgihttpserver.WSGIHTTPServer):
 
-        HTTPServer.__init__(self, *args, **kw)
+    def __init__(self, request_factory, sub_protocol=None, *args, **kw):
 
-    def executeRequest(self, task):
-        """Overrides HTTPServer.executeRequest()."""
-        env = task.getCGIEnvironment()
-        instream = task.request_data.getBodyStream()
-
-        request = self.request_factory(instream, task, env)
-        response = request.response
-        response.setHeaderOutput(task)
-        response.setHTTPTransaction(task)
-        publish(request)
-
-
-class PMDBHTTPServer(PublisherHTTPServer):
-    """Enter the post-mortem debugger when there's an error"""
-
-    def executeRequest(self, task):
-        """Overrides HTTPServer.executeRequest()."""
-        env = task.getCGIEnvironment()
-        instream = task.request_data.getBodyStream()
-
-        request = self.request_factory(instream, task, env)
-        response = request.response
-        response.setHeaderOutput(task)
-        try:
-            publish(request, handle_errors=False)
-        except:
-            import sys, pdb
-            print "%s:" % sys.exc_info()[0]
-            print sys.exc_info()[1]
-            zope.security.management.restoreInteraction()
+        def application(environ, start_response):
+            request = request_factory(environ['wsgi.input'], environ)
+            response = request.response
             try:
-                pdb.post_mortem(sys.exc_info()[2])
-                raise
-            finally:
-                zope.security.management.endInteraction()
+                publish(request, handle_errors=False)
+            except:
+                import sys, pdb
+                print "%s:" % sys.exc_info()[0]
+                print sys.exc_info()[1]
+                zope.security.management.restoreInteraction()
+                try:
+                    pdb.post_mortem(sys.exc_info()[2])
+                    raise
+                finally:
+                    zope.security.management.endInteraction()
+            start_response(response.getStatusString(), response.getHeaders())
+            return response.result.body
 
+        return super(PublisherHTTPServer, self).__init__(
+            application, sub_protocol, *args, **kw)
+
+zope.deprecation.deprecated(
+    ('PublisherHTTPServer', 'PMDBHTTPServer'),
+    'This plain publisher support has been replaced in favor of the '
+    'WSGI HTTP server '
+    'The reference will be gone in X3.4.')

Deleted: Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_publisherserver.py
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_publisherserver.py	2005-09-02 23:08:40 UTC (rev 38272)
+++ Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_publisherserver.py	2005-09-02 23:12:37 UTC (rev 38273)
@@ -1,194 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001 Zope Corporation and Contributors.  All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 1.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-##############################################################################
-"""Test Puvlisher-based HTTP Server
-
-$Id$
-"""
-import unittest
-from asyncore import socket_map, poll
-from threading import Thread
-
-from zope.server.taskthreads import ThreadedTaskDispatcher
-from zope.server.http.publisherhttpserver import PublisherHTTPServer
-
-from zope.component.testing import PlacelessSetup
-import zope.component
-
-from zope.i18n.interfaces import IUserPreferredCharsets
-
-from zope.publisher.http import IHTTPRequest
-from zope.publisher.http import HTTPCharsets
-from zope.publisher.browser import BrowserRequest
-from zope.publisher.base import DefaultPublication
-from zope.publisher.interfaces import Redirect, Retry
-from zope.publisher.http import HTTPRequest
-
-from httplib import HTTPConnection
-
-from time import sleep
-
-td = ThreadedTaskDispatcher()
-
-LOCALHOST = '127.0.0.1'
-
-HTTPRequest.STAGGER_RETRIES = 0  # Don't pause.
-
-
-class Conflict(Exception):
-    """
-    Pseudo ZODB conflict error.
-    """
-
-
-class PublicationWithConflict(DefaultPublication):
-
-    def handleException(self, object, request, exc_info, retry_allowed=1):
-        if exc_info[0] is Conflict and retry_allowed:
-            # This simulates a ZODB retry.
-            raise Retry(exc_info)
-        else:
-            DefaultPublication.handleException(self, object, request, exc_info,
-                                               retry_allowed)
-
-class Accepted(Exception):
-    pass
-
-class tested_object(object):
-    """Docstring required by publisher."""
-    tries = 0
-
-    def __call__(self, REQUEST):
-        return 'URL invoked: %s' % REQUEST.URL
-
-    def redirect_method(self, REQUEST):
-        "Generates a redirect using the redirect() method."
-        REQUEST.response.redirect("http://somewhere.com/redirect")
-
-    def redirect_exception(self):
-        "Generates a redirect using an exception."
-        raise Redirect("http://somewhere.com/exception")
-
-    def conflict(self, REQUEST, wait_tries):
-        """
-        Returns 202 status only after (wait_tries) tries.
-        """
-        if self.tries >= int(wait_tries):
-            raise Accepted
-        else:
-            self.tries += 1
-            raise Conflict
-
-
-class Tests(PlacelessSetup, unittest.TestCase):
-
-    def setUp(self):
-        super(Tests, self).setUp()
-        zope.component.provideAdapter(HTTPCharsets, [IHTTPRequest],
-                                      IUserPreferredCharsets, '')
-        obj = tested_object()
-        obj.folder = tested_object()
-        obj.folder.item = tested_object()
-
-        obj._protected = tested_object()
-
-        pub = PublicationWithConflict(obj)
-
-        def request_factory(input_stream, output_steam, env):
-            request = BrowserRequest(input_stream, output_steam, env)
-            request.setPublication(pub)
-            return request
-
-        td.setThreadCount(4)
-        # Bind to any port on localhost.
-        self.server = PublisherHTTPServer(request_factory, 'Browser',
-                                          LOCALHOST, 0, task_dispatcher=td)
-        self.port = self.server.socket.getsockname()[1]
-        self.run_loop = 1
-        self.thread = Thread(target=self.loop)
-        self.thread.start()
-        sleep(0.1)  # Give the thread some time to start.
-
-    def tearDown(self):
-        self.run_loop = 0
-        self.thread.join()
-        td.shutdown()
-        self.server.close()
-
-    def loop(self):
-        while self.run_loop:
-            poll(0.1, socket_map)
-
-    def testResponse(self, path='/', status_expected=200,
-                     add_headers=None, request_body=''):
-        h = HTTPConnection(LOCALHOST, self.port)
-        h.putrequest('GET', path)
-        h.putheader('Accept', 'text/plain')
-        if add_headers:
-            for k, v in add_headers.items():
-                h.putheader(k, v)
-        if request_body:
-            h.putheader('Content-Length', str(int(len(request_body))))
-        h.endheaders()
-        if request_body:
-            h.send(request_body)
-        response = h.getresponse()
-        length = int(response.getheader('Content-Length', '0'))
-        if length:
-            response_body = response.read(length)
-        else:
-            response_body = ''
-
-        # Please do not disable the status code check.  It must work.
-        self.failUnlessEqual(int(response.status), status_expected)
-
-        self.failUnlessEqual(length, len(response_body))
-
-        if (status_expected == 200):
-            if path == '/': path = ''
-            expect_response = 'URL invoked: http://%s:%d%s' % (LOCALHOST,
-                self.port, path)
-            self.failUnlessEqual(response_body, expect_response)
-
-    def testDeeperPath(self):
-        self.testResponse(path='/folder/item')
-
-    def testNotFound(self):
-        self.testResponse(path='/foo/bar', status_expected=404)
-
-    def testUnauthorized(self):
-        self.testResponse(path='/_protected', status_expected=401)
-
-    def testRedirectMethod(self):
-        self.testResponse(path='/redirect_method', status_expected=303)
-
-    def testRedirectException(self):
-        self.testResponse(path='/redirect_exception', status_expected=303)
-        self.testResponse(path='/folder/redirect_exception',
-                          status_expected=303)
-
-    def testConflictRetry(self):
-        # Expect the "Accepted" response since the retries will succeed.
-        self.testResponse(path='/conflict?wait_tries=2', status_expected=202)
-
-    def testFailedConflictRetry(self):
-        # Expect a "Conflict" response since there will be too many
-        # conflicts.
-        self.testResponse(path='/conflict?wait_tries=10', status_expected=409)
-
-
-
-def test_suite():
-    loader = unittest.TestLoader()
-    return loader.loadTestsFromTestCase(Tests)
-
-if __name__=='__main__':
-    unittest.TextTestRunner().run(test_suite())

Copied: Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_wsgiserver.py (from rev 38226, Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_publisherserver.py)
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_publisherserver.py	2005-09-01 18:41:05 UTC (rev 38226)
+++ Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/tests/test_wsgiserver.py	2005-09-02 23:12:37 UTC (rev 38273)
@@ -0,0 +1,197 @@
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors.  All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 1.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+##############################################################################
+"""Test Puvlisher-based HTTP Server
+
+$Id$
+"""
+import unittest
+from asyncore import socket_map, poll
+from threading import Thread
+from time import sleep
+from httplib import HTTPConnection
+
+from zope.server.taskthreads import ThreadedTaskDispatcher
+from zope.server.http.wsgihttpserver import WSGIHTTPServer
+
+from zope.component.testing import PlacelessSetup
+import zope.component
+
+from zope.i18n.interfaces import IUserPreferredCharsets
+
+from zope.publisher.publish import publish
+from zope.publisher.http import IHTTPRequest
+from zope.publisher.http import HTTPCharsets
+from zope.publisher.browser import BrowserRequest
+from zope.publisher.base import DefaultPublication
+from zope.publisher.interfaces import Redirect, Retry
+from zope.publisher.http import HTTPRequest
+
+td = ThreadedTaskDispatcher()
+
+LOCALHOST = '127.0.0.1'
+
+HTTPRequest.STAGGER_RETRIES = 0  # Don't pause.
+
+
+class Conflict(Exception):
+    """
+    Pseudo ZODB conflict error.
+    """
+
+
+class PublicationWithConflict(DefaultPublication):
+
+    def handleException(self, object, request, exc_info, retry_allowed=1):
+        if exc_info[0] is Conflict and retry_allowed:
+            # This simulates a ZODB retry.
+            raise Retry(exc_info)
+        else:
+            DefaultPublication.handleException(self, object, request, exc_info,
+                                               retry_allowed)
+
+class Accepted(Exception):
+    pass
+
+class tested_object(object):
+    """Docstring required by publisher."""
+    tries = 0
+
+    def __call__(self, REQUEST):
+        return 'URL invoked: %s' % REQUEST.URL
+
+    def redirect_method(self, REQUEST):
+        "Generates a redirect using the redirect() method."
+        REQUEST.response.redirect("http://somewhere.com/redirect")
+
+    def redirect_exception(self):
+        "Generates a redirect using an exception."
+        raise Redirect("http://somewhere.com/exception")
+
+    def conflict(self, REQUEST, wait_tries):
+        """
+        Returns 202 status only after (wait_tries) tries.
+        """
+        if self.tries >= int(wait_tries):
+            raise Accepted
+        else:
+            self.tries += 1
+            raise Conflict
+
+
+class Tests(PlacelessSetup, unittest.TestCase):
+
+    def setUp(self):
+        super(Tests, self).setUp()
+        zope.component.provideAdapter(HTTPCharsets, [IHTTPRequest],
+                                      IUserPreferredCharsets, '')
+        obj = tested_object()
+        obj.folder = tested_object()
+        obj.folder.item = tested_object()
+
+        obj._protected = tested_object()
+
+        pub = PublicationWithConflict(obj)
+
+        def application(environ, start_response):
+            request = BrowserRequest(environ['wsgi.input'], environ)
+            request.setPublication(pub)
+            response = request.response
+            publish(request)
+            start_response(response.getStatusString(), response.getHeaders())
+            return response.result.body
+
+        td.setThreadCount(4)
+        # Bind to any port on localhost.
+        self.server = WSGIHTTPServer(application, 'Browser',
+                                     LOCALHOST, 0, task_dispatcher=td)
+
+        self.port = self.server.socket.getsockname()[1]
+        self.run_loop = 1
+        self.thread = Thread(target=self.loop)
+        self.thread.start()
+        sleep(0.1)  # Give the thread some time to start.
+
+    def tearDown(self):
+        self.run_loop = 0
+        self.thread.join()
+        td.shutdown()
+        self.server.close()
+
+    def loop(self):
+        while self.run_loop:
+            poll(0.1, socket_map)
+
+    def testResponse(self, path='/', status_expected=200,
+                     add_headers=None, request_body=''):
+        h = HTTPConnection(LOCALHOST, self.port)
+        h.putrequest('GET', path)
+        h.putheader('Accept', 'text/plain')
+        if add_headers:
+            for k, v in add_headers.items():
+                h.putheader(k, v)
+        if request_body:
+            h.putheader('Content-Length', str(int(len(request_body))))
+        h.endheaders()
+        if request_body:
+            h.send(request_body)
+        response = h.getresponse()
+        length = int(response.getheader('Content-Length', '0'))
+        if length:
+            response_body = response.read(length)
+        else:
+            response_body = ''
+
+        # Please do not disable the status code check.  It must work.
+        self.failUnlessEqual(int(response.status), status_expected)
+
+        self.failUnlessEqual(length, len(response_body))
+
+        if (status_expected == 200):
+            if path == '/': path = ''
+            expect_response = 'URL invoked: http://%s:%d%s' % (LOCALHOST,
+                self.port, path)
+            self.failUnlessEqual(response_body, expect_response)
+
+    def testDeeperPath(self):
+        self.testResponse(path='/folder/item')
+
+    def testNotFound(self):
+        self.testResponse(path='/foo/bar', status_expected=404)
+
+    def testUnauthorized(self):
+        self.testResponse(path='/_protected', status_expected=401)
+
+    def testRedirectMethod(self):
+        self.testResponse(path='/redirect_method', status_expected=303)
+
+    def testRedirectException(self):
+        self.testResponse(path='/redirect_exception', status_expected=303)
+        self.testResponse(path='/folder/redirect_exception',
+                          status_expected=303)
+
+    def testConflictRetry(self):
+        # Expect the "Accepted" response since the retries will succeed.
+        self.testResponse(path='/conflict?wait_tries=2', status_expected=202)
+
+    def testFailedConflictRetry(self):
+        # Expect a "Conflict" response since there will be too many
+        # conflicts.
+        self.testResponse(path='/conflict?wait_tries=10', status_expected=409)
+
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromTestCase(Tests)
+
+if __name__=='__main__':
+    unittest.TextTestRunner().run(test_suite())

Modified: Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/wsgihttpserver.py
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/wsgihttpserver.py	2005-09-02 23:08:40 UTC (rev 38272)
+++ Zope3/branches/stephan_and_jim-response-refactor/src/zope/server/http/wsgihttpserver.py	2005-09-02 23:12:37 UTC (rev 38273)
@@ -19,8 +19,14 @@
 import sys
 from zope.server.http.httpserver import HTTPServer
 from zope.publisher.publish import publish
+import zope.security.management
 
 
+def fakeWrite(body):
+    raise NotImplementedError(
+        "Zope 3's HTTP Server does not support the WSGI write() function.")
+
+
 class WSGIHTTPServer(HTTPServer):
     """Zope Publisher-specific WSGI-compliant HTTP Server"""
 
@@ -49,7 +55,41 @@
             task.appendResponseHeaders(['%s: %s' % i for i in headers])
 
             # Return the write method used to write the response data.
-            return task.write
+            return fakeWrite
 
         # Call the application to handle the request and write a response
-        self.application(env, start_response)
+        task.write(''.join(self.application(env, start_response)))
+
+
+class PMDBWSGIHTTPServer(WSGIHTTPServer):
+    """Enter the post-mortem debugger when there's an error"""
+
+    def executeRequest(self, task):
+        """Overrides HTTPServer.executeRequest()."""
+        env = task.getCGIEnvironment()
+        env['wsgi.input'] = task.request_data.getBodyStream()
+        env['wsgi.handleErrors'] = False
+
+        def start_response(status, headers):
+            # Prepare the headers for output
+            status, reason = re.match('([0-9]*) (.*)', status).groups()
+            task.setResponseStatus(status, reason)
+            task.appendResponseHeaders(['%s: %s' % i for i in headers])
+
+            # Return the write method used to write the response data.
+            return fakeWrite
+
+        # Call the application to handle the request and write a response
+        try:
+            task.write(''.join(self.application(env, start_response)))
+        except:
+            import sys, pdb
+            print "%s:" % sys.exc_info()[0]
+            print sys.exc_info()[1]
+            zope.security.management.restoreInteraction()
+            try:
+                pdb.post_mortem(sys.exc_info()[2])
+                raise
+            finally:
+                zope.security.management.endInteraction()
+

Modified: Zope3/branches/stephan_and_jim-response-refactor/zope.conf.in
===================================================================
--- Zope3/branches/stephan_and_jim-response-refactor/zope.conf.in	2005-09-02 23:08:40 UTC (rev 38272)
+++ Zope3/branches/stephan_and_jim-response-refactor/zope.conf.in	2005-09-02 23:12:37 UTC (rev 38273)
@@ -6,7 +6,7 @@
 interrupt-check-interval 200
 
 <server http>
-  type HTTP
+  type WSGI-HTTP
   address 8080
 </server>
 



More information about the Zope3-Checkins mailing list