Log message for revision 67794:
  Experimental WSGI + Twisted support. 
  Merge from svn+ssh://[EMAIL 
PROTECTED]/repos/main/Zope/branches/regebro-wsgi_support2
  

Changed:
  U   Zope/trunk/doc/CHANGES.txt
  U   Zope/trunk/lib/python/Lifetime/__init__.py
  U   Zope/trunk/lib/python/Products/PythonScripts/tests/testPythonScript.py
  U   Zope/trunk/lib/python/ZPublisher/Publish.py
  A   Zope/trunk/lib/python/ZPublisher/WSGIPublisher.py
  U   Zope/trunk/lib/python/ZServer/HTTPResponse.py
  U   Zope/trunk/lib/python/ZServer/HTTPServer.py
  U   Zope/trunk/lib/python/ZServer/PubCore/ZServerPublisher.py
  U   Zope/trunk/lib/python/ZServer/component.xml
  U   Zope/trunk/lib/python/ZServer/datatypes.py
  U   Zope/trunk/lib/python/Zope2/Startup/__init__.py
  U   Zope/trunk/lib/python/Zope2/Startup/datatypes.py
  U   Zope/trunk/lib/python/Zope2/Startup/handlers.py
  U   Zope/trunk/lib/python/Zope2/Startup/zopeschema.xml
  U   Zope/trunk/skel/etc/zope.conf.in

-=-
Modified: Zope/trunk/doc/CHANGES.txt
===================================================================
--- Zope/trunk/doc/CHANGES.txt  2006-05-01 12:59:29 UTC (rev 67793)
+++ Zope/trunk/doc/CHANGES.txt  2006-05-01 13:17:32 UTC (rev 67794)
@@ -48,6 +48,27 @@
       - Using FastCGI is offically deprecated.
 
     Features added
+    
+      - Experimental WSGI and Twisted support for http.
+        Zope now has a WSGI interface for integration with other
+        web-servers than ZServer. Most notably Twisted is supported.
+        The WSGI application is ZPublisher.WSGIPublisher.publish_module
+        
+        You can make ZServer use the twisted interface with the 
+        "use-wsgi on" keyword in the http-server section in zope.conf.
+        
+        You can run Twisted by installing Twisted (2.1 recommended) and
+        replacing the http-server section with a server section in 
+        zope.conf. It is not possible to run a Twisted server together with
+        a ZServer at the same time.
+        
+          <server>
+            address 8080
+            type Zope2-HTTP
+          </server>
+        
+        WSGI: http://www.python.org/dev/peps/pep-0333/
+        Twisted: http://twistedmatrix.com/
 
       - The traversal has been refactored to take heed of Zope3s 
         IPublishTraverse adapter interfaces. The ZCML directives 

Modified: Zope/trunk/lib/python/Lifetime/__init__.py
===================================================================
--- Zope/trunk/lib/python/Lifetime/__init__.py  2006-05-01 12:59:29 UTC (rev 
67793)
+++ Zope/trunk/lib/python/Lifetime/__init__.py  2006-05-01 13:17:32 UTC (rev 
67794)
@@ -31,6 +31,11 @@
         import ZServer
         ZServer.exit_code = exit_code
         _shutdown_phase = 1
+        try:
+            from twisted.internet import reactor
+            reactor.callLater(0.1, reactor.stop)
+        except ImportError:
+            pass
     if fast:
         # Someone wants us to shutdown fast. This is hooked into SIGTERM - so
         # possibly the system is going down and we can expect a SIGKILL within

Modified: Zope/trunk/lib/python/Products/PythonScripts/tests/testPythonScript.py
===================================================================
--- Zope/trunk/lib/python/Products/PythonScripts/tests/testPythonScript.py      
2006-05-01 12:59:29 UTC (rev 67793)
+++ Zope/trunk/lib/python/Products/PythonScripts/tests/testPythonScript.py      
2006-05-01 13:17:32 UTC (rev 67794)
@@ -223,7 +223,8 @@
 
     def testBadImports(self):
         self.assertPSRaises(ImportError, body="from string import *")
-        self.assertPSRaises(ImportError, body="import mmap")
+        self.assertPSRaises(ImportError, body="from datetime import datetime")
+        #self.assertPSRaises(ImportError, body="import mmap")
 
     def testAttributeAssignment(self):
         # It's illegal to assign to attributes of anything that

Modified: Zope/trunk/lib/python/ZPublisher/Publish.py
===================================================================
--- Zope/trunk/lib/python/ZPublisher/Publish.py 2006-05-01 12:59:29 UTC (rev 
67793)
+++ Zope/trunk/lib/python/ZPublisher/Publish.py 2006-05-01 13:17:32 UTC (rev 
67794)
@@ -122,7 +122,6 @@
 
         return response
     except:
-
         # DM: provide nicer error message for FTP
         sm = None
         if response is not None:

Copied: Zope/trunk/lib/python/ZPublisher/WSGIPublisher.py (from rev 67793, 
Zope/branches/regebro-wsgi_support2/lib/python/ZPublisher/WSGIPublisher.py)

Modified: Zope/trunk/lib/python/ZServer/HTTPResponse.py
===================================================================
--- Zope/trunk/lib/python/ZServer/HTTPResponse.py       2006-05-01 12:59:29 UTC 
(rev 67793)
+++ Zope/trunk/lib/python/ZServer/HTTPResponse.py       2006-05-01 13:17:32 UTC 
(rev 67794)
@@ -311,6 +311,16 @@
             self._close=1
         self._request.reply_code=response.status
 
+    def start_response(self, status, headers, exc_info=None):
+        # Used for WSGI
+        self._request.reply_code = int(status.split(' ')[0])
+        status = 'HTTP/%s %s\r\n' % (self._request.version, status)
+        self.write(status)
+        headers = '\r\n'.join([': '.join(x) for x in headers])
+        self.write(headers)
+        self.write('\r\n\r\n')
+        return self.write
+        
 
 is_proxying_match = re.compile(r'[^ ]* [^ \\]*:').match
 proxying_connection_re = re.compile ('Proxy-Connection: (.*)', re.IGNORECASE)

Modified: Zope/trunk/lib/python/ZServer/HTTPServer.py
===================================================================
--- Zope/trunk/lib/python/ZServer/HTTPServer.py 2006-05-01 12:59:29 UTC (rev 
67793)
+++ Zope/trunk/lib/python/ZServer/HTTPServer.py 2006-05-01 13:17:32 UTC (rev 
67794)
@@ -279,8 +279,50 @@
             </ul>""" %(self.module_name, self.hits)
             )
 
+from HTTPResponse import ChannelPipe
 
+class zwsgi_handler(zhttp_handler):
+    
+    def continue_request(self, sin, request):
+        "continue handling request now that we have the stdin"
 
+        s=get_header(CONTENT_LENGTH, request.header)
+        if s:
+            s=int(s)
+        else:
+            s=0
+        DebugLogger.log('I', id(request), s)
+
+        env=self.get_environment(request)
+        version = request.version
+        if version=='1.0' and is_proxying_match(request.request):
+            # a request that was made as if this zope was an http 1.0 proxy.
+            # that means we have to use some slightly different http
+            # headers to manage persistent connections.
+            connection_re = proxying_connection_re
+        else:
+            # a normal http request
+            connection_re = CONNECTION
+        
+        env['http_connection'] = get_header(connection_re,
+                                            request.header).lower()
+        env['server_version']=request.channel.server.SERVER_IDENT
+
+        env['wsgi.output'] = ChannelPipe(request)
+        env['wsgi.input'] = sin
+        env['wsgi.errors']       = sys.stderr
+        env['wsgi.version']      = (1,0)
+        env['wsgi.multithread']  = True
+        env['wsgi.multiprocess'] = True
+        env['wsgi.run_once']     = True
+        env['wsgi.url_scheme']   = env['SERVER_PROTOCOL'].split('/')[0]
+
+        request.channel.current_request=None
+        request.channel.queue.append(('Zope2WSGI', env, 
+                                      env['wsgi.output'].start_response))
+        request.channel.work()
+
+
 class zhttp_channel(http_channel):
     "http channel"
 

Modified: Zope/trunk/lib/python/ZServer/PubCore/ZServerPublisher.py
===================================================================
--- Zope/trunk/lib/python/ZServer/PubCore/ZServerPublisher.py   2006-05-01 
12:59:29 UTC (rev 67793)
+++ Zope/trunk/lib/python/ZServer/PubCore/ZServerPublisher.py   2006-05-01 
13:17:32 UTC (rev 67794)
@@ -14,13 +14,25 @@
 class ZServerPublisher:
     def __init__(self, accept):
         from ZPublisher import publish_module
+        from ZPublisher.WSGIPublisher import publish_module as publish_wsgi
         while 1:
-            try:
-                name, request, response=accept()
-                publish_module(
-                    name,
-                    request=request,
-                    response=response)
-            finally:
-                response._finish()
-                request=response=None
+            name, a, b=accept()
+            if name == "Zope2":
+                try:
+                    publish_module(
+                        name,
+                        request=a,
+                        response=b)
+                finally:
+                    b._finish()
+                    a=b=None
+
+            elif name == "Zope2WSGI":
+                try:
+                    res = publish_wsgi(a, b)
+                    for r in res:
+                        a['wsgi.output'].write(r)
+                finally:
+                    # TODO: Support keeping connections open.
+                    a['wsgi.output']._close = 1
+                    a['wsgi.output'].close()

Modified: Zope/trunk/lib/python/ZServer/component.xml
===================================================================
--- Zope/trunk/lib/python/ZServer/component.xml 2006-05-01 12:59:29 UTC (rev 
67793)
+++ Zope/trunk/lib/python/ZServer/component.xml 2006-05-01 13:17:32 UTC (rev 
67794)
@@ -19,6 +19,7 @@
          receive WebDAV source responses to GET requests.
        </description>
      </key>
+     <key name="use-wsgi" datatype="boolean" default="off" />
   </sectiontype>
 
   <sectiontype name="webdav-source-server"
@@ -26,6 +27,7 @@
                implements="ZServer.server">
      <key name="address" datatype="inet-binding-address"/>
      <key name="force-connection-close" datatype="boolean" default="off"/>
+     <key name="use-wsgi" datatype="boolean" default="off" />
   </sectiontype>
 
   <sectiontype name="persistent-cgi"

Modified: Zope/trunk/lib/python/ZServer/datatypes.py
===================================================================
--- Zope/trunk/lib/python/ZServer/datatypes.py  2006-05-01 12:59:29 UTC (rev 
67793)
+++ Zope/trunk/lib/python/ZServer/datatypes.py  2006-05-01 13:17:32 UTC (rev 
67794)
@@ -71,6 +71,7 @@
         # webdav-source-server sections won't have webdav_source_clients:
         webdav_clients = getattr(section, "webdav_source_clients", None)
         self.webdav_source_clients = webdav_clients
+        self.use_wsgi = section.use_wsgi
 
     def create(self):
         from ZServer.AccessLogger import access_logger
@@ -86,7 +87,10 @@
 
     def createHandler(self):
         from ZServer import HTTPServer
-        return HTTPServer.zhttp_handler(self.module, '', self.cgienv)
+        if self.use_wsgi:
+            return HTTPServer.zwsgi_handler(self.module, '', self.cgienv)
+        else:
+            return HTTPServer.zhttp_handler(self.module, '', self.cgienv)
 
 
 class WebDAVSourceServerFactory(HTTPServerFactory):

Modified: Zope/trunk/lib/python/Zope2/Startup/__init__.py
===================================================================
--- Zope/trunk/lib/python/Zope2/Startup/__init__.py     2006-05-01 12:59:29 UTC 
(rev 67793)
+++ Zope/trunk/lib/python/Zope2/Startup/__init__.py     2006-05-01 13:17:32 UTC 
(rev 67794)
@@ -20,12 +20,16 @@
 import socket
 from re import compile
 from socket import gethostbyaddr
+try:
+    import twisted.internet.reactor
+    _use_twisted = True
+except ImportError:
+    _use_twisted = True
+    
 
 import ZConfig
-
 from ZConfig.components.logger import loghandler
 
-
 logger = logging.getLogger("Zope")
 started = False
 
@@ -96,7 +100,10 @@
         self.makePidFile()
         self.setupInterpreter()
         self.startZope()
-        self.registerSignals()
+        from App.config import getConfiguration
+        config = getConfiguration()
+        if not config.twisted_servers:
+            self.registerSignals()
         # emit a "ready" message in order to prevent the kinds of emails
         # to the Zope maillist in which people claim that Zope has "frozen"
         # after it has emitted ZServer messages.
@@ -106,10 +113,24 @@
     def run(self):
         # the mainloop.
         try:
+            from App.config import getConfiguration
+            config = getConfiguration()
             import ZServer
-            import Lifetime
-            Lifetime.loop()
-            sys.exit(ZServer.exit_code)
+            if config.twisted_servers and config.servers:
+                raise ZConfig.ConfigurationError(
+                    "You can't run both ZServer servers and twisted servers.")
+            if config.twisted_servers:
+                if not _use_twisted:
+                    raise ZConfig.ConfigurationError(
+                        "You do not have twisted installed.")
+                twisted.internet.reactor.run()
+                # Storing the exit code in the ZServer even for twisted, 
+                # but hey, it works...
+                sys.exit(ZServer.exit_code)
+            else:
+                import Lifetime
+                Lifetime.loop()
+                sys.exit(ZServer.exit_code)
         finally:
             self.shutdown()
 

Modified: Zope/trunk/lib/python/Zope2/Startup/datatypes.py
===================================================================
--- Zope/trunk/lib/python/Zope2/Startup/datatypes.py    2006-05-01 12:59:29 UTC 
(rev 67793)
+++ Zope/trunk/lib/python/Zope2/Startup/datatypes.py    2006-05-01 13:17:32 UTC 
(rev 67794)
@@ -339,3 +339,11 @@
 # Zope class factory."  This no longer works with the implementation of
 # mounted databases, so we just use the zopeClassFactory as the default
 
+try:
+    from zope.app.twisted.server import ServerFactory
+    class TwistedServerFactory(ServerFactory):
+        pass
+except ImportError:
+    class TwistedServerFactory:
+        def __init__(self, section):
+            raise ImportError("You do not have twisted installed.")

Modified: Zope/trunk/lib/python/Zope2/Startup/handlers.py
===================================================================
--- Zope/trunk/lib/python/Zope2/Startup/handlers.py     2006-05-01 12:59:29 UTC 
(rev 67793)
+++ Zope/trunk/lib/python/Zope2/Startup/handlers.py     2006-05-01 13:17:32 UTC 
(rev 67794)
@@ -1,8 +1,34 @@
 import os
 import sys
+import time
+import logging
 from re import compile
 from socket import gethostbyaddr
 
+try:
+    import twisted.internet
+    from twisted.application.service import MultiService
+    import zope.app.appsetup.interfaces
+    import zope.app.twisted.main
+
+    import twisted.web2.wsgi
+    import twisted.web2.server
+    import twisted.web2.log
+    
+    try:
+        from twisted.web2.http import HTTPFactory
+    except ImportError:
+        from twisted.web2.channel.http import HTTPFactory
+    
+    from zope.component import provideUtility
+    from zope.app.twisted.server import ServerType, SSLServerType
+    from zope.app.twisted.interfaces import IServerType
+    from ZPublisher.WSGIPublisher import publish_module
+    
+    _use_twisted = True
+except ImportError:
+    _use_twisted = False
+
 # top-level key handlers
 
 
@@ -133,7 +159,7 @@
         "'catalog-getObject-raises' option will be removed in Zope 2.10:\n",
         DeprecationWarning)
 
-        from Products.ZCatalog import CatalogBrains 
+        from Products.ZCatalog import CatalogBrains
         CatalogBrains.GETOBJECT_RAISES = bool(value)
 
     return value
@@ -143,7 +169,8 @@
 def root_handler(config):
     """ Mutate the configuration with defaults and perform
     fixups of values that require knowledge about configuration
-    values outside of their context. """
+    values outside of their context.
+    """
 
     # Set environment variables
     for k,v in config.environment.items():
@@ -165,7 +192,7 @@
     instanceprod = os.path.join(config.instancehome, 'Products')
     if instanceprod not in config.products:
         config.products.append(instanceprod)
-    
+
     import Products
     L = []
     for d in config.products + Products.__path__:
@@ -190,6 +217,23 @@
                         config.cgi_environment,
                         config.port_base)
 
+    if not config.twisted_servers:
+        config.twisted_servers = []
+    else:
+        # Set number of threads (reuse zserver_threads variable)
+        twisted.internet.reactor.suggestThreadPoolSize(config.zserver_threads)
+
+        # Create a root service
+        rootService = MultiService()
+
+        for server in config.twisted_servers:
+            service = server.create(None)
+            service.setServiceParent(rootService)
+
+        rootService.startService()
+        twisted.internet.reactor.addSystemEventTrigger(
+            'before', 'shutdown', rootService.stopService)
+
     # set up trusted proxies
     if config.trusted_proxies:
         import ZPublisher.HTTPRequest
@@ -217,3 +261,15 @@
     if isIp_(host): return [host]
     return gethostbyaddr(host)[2]
 
+
+# Twisted support:
+
+def createHTTPFactory(ignored):
+    resource = twisted.web2.wsgi.WSGIResource(publish_module)
+    resource = twisted.web2.log.LogWrapperResource(resource)
+
+    return HTTPFactory(twisted.web2.server.Site(resource))
+
+if _use_twisted:
+    http = ServerType(createHTTPFactory, 8080)
+    provideUtility(http, IServerType, 'Zope2-HTTP')

Modified: Zope/trunk/lib/python/Zope2/Startup/zopeschema.xml
===================================================================
--- Zope/trunk/lib/python/Zope2/Startup/zopeschema.xml  2006-05-01 12:59:29 UTC 
(rev 67793)
+++ Zope/trunk/lib/python/Zope2/Startup/zopeschema.xml  2006-05-01 13:17:32 UTC 
(rev 67794)
@@ -11,6 +11,12 @@
   <import package="tempstorage"/>
   <import package="Zope2.Startup" file="warnfilter.xml"/>
 
+  <sectiontype name="server" 
datatype="Zope2.Startup.datatypes.TwistedServerFactory">
+    <key name="type" required="yes" />
+    <key name="address" datatype="inet-address" />
+    <key name="backlog" datatype="integer" default="50" />
+  </sectiontype>
+
   <sectiontype name="logger" datatype=".LoggerFactory">
     <description>
       This "logger" type only applies to access and request ("trace")
@@ -805,7 +811,9 @@
      <metadefault>on</metadefault>
   </key>
 
+  <multisection type="server" name="*" attribute="twisted_servers" />
   <multisection type="ZServer.server" name="*" attribute="servers"/>
+
   <key name="port-base" datatype="integer" default="0">
     <description>
       Base port number that gets added to the specific port numbers

Modified: Zope/trunk/skel/etc/zope.conf.in
===================================================================
--- Zope/trunk/skel/etc/zope.conf.in    2006-05-01 12:59:29 UTC (rev 67793)
+++ Zope/trunk/skel/etc/zope.conf.in    2006-05-01 13:17:32 UTC (rev 67794)
@@ -904,6 +904,8 @@
   # valid keys are "address" and "force-connection-close"
   address 8080
   # force-connection-close on
+  # You can also use the WSGI interface between ZServer and ZPublisher:
+  # use-wsgi on
 </http-server>
 
 # Examples:
@@ -947,6 +949,13 @@
 #    user admin
 #    password 123
 #  </clock-server>
+#
+#  <server>
+#    # This uses Twisted as the web-server. You must install Twisted 
+#    # separately. You can't run Twisted and ZServer at same time.
+#    address 8080
+#    type Zope2-HTTP
+#  </server>
 
 
 # Database (zodb_db) section

_______________________________________________
Zope-Checkins maillist  -  Zope-Checkins@zope.org
http://mail.zope.org/mailman/listinfo/zope-checkins

Reply via email to