Robert Brewer avatar Robert Brewer committed 42eacdb

New test/modfastcgi.py (I couldn't get mod_fcgid working well with our test suite on nix).

Comments (0)

Files changed (3)

cherrypy/test/fastcgi.conf

+
+# Apache2 server conf file for testing CherryPy with mod_fastcgi.
+# fumanchu: I had to hard-code paths due to crazy Debian layouts :(
+ServerRoot /usr/lib/apache2
+User #1000
+ErrorLog /usr/lib/python2.5/site-packages/cproot/trunk/cherrypy/test/mod_fastcgi.error.log
+
+DocumentRoot "/usr/lib/python2.5/site-packages/cproot/trunk/cherrypy/test"
+ServerName 127.0.0.1
+Listen 8080
+LoadModule fastcgi_module modules/mod_fastcgi.so
+LoadModule rewrite_module modules/mod_rewrite.so
+
+Options +ExecCGI
+SetHandler fastcgi-script
+RewriteEngine On
+RewriteRule ^(.*)$ /fastcgi.pyc [L]
+FastCgiExternalServer "/usr/lib/python2.5/site-packages/cproot/trunk/cherrypy/test/fastcgi.pyc" -host 127.0.0.1:4000

cherrypy/test/helper.py

             'host': '127.0.0.1',
             'validate': False,
             'conquer': False,
+            'server': 'wsgi',
         }
         try:
             import testconfig
         """Hook a new WSGI app into the origin server."""
         cherrypy.server.httpserver.wsgi_app = self.get_app()
 
-    def get_app(self):
+    def get_app(self, app=None):
         """Obtain a new (decorated) WSGI app to hook into the origin server."""
-        app = cherrypy.tree
+        if app is None:
+            app = cherrypy.tree
+        
         if self.conquer:
             try:
                 import wsgiconq
                 warnings.warn("Error importing wsgiconq. pyconquer will not run.")
             else:
                 app = wsgiconq.WSGILogger(app, c_calls=True)
+        
         if self.validate:
             try:
                 from wsgiref import validate
             else:
                 #wraps the app in the validator
                 app = validate.validator(app)
-
+        
         return app
 
 
     from cherrypy.test import modfcgid
     return modfcgid.ModFCGISupervisor(**options)
 
+def get_modfastcgi_supervisor(**options):
+    from cherrypy.test import modfastcgi
+    return modfastcgi.ModFCGISupervisor(**options)
+
 def get_wsgi_u_supervisor(**options):
     cherrypy.server.wsgi_version = ('u', 0)
     return LocalWSGISupervisor(**options)
                          'modpygw': get_modpygw_supervisor,
                          'modwsgi': get_modwsgi_supervisor,
                          'modfcgid': get_modfcgid_supervisor,
+                         'modfastcgi': get_modfastcgi_supervisor,
                          }
     default_server = "wsgi"
-    supervisor_factory = None
-
+    
     @classmethod
     def _setup_server(cls, supervisor, conf):
         v = sys.version.split()[0]
         ''
         #Creates a server
         conf = get_tst_config()
-        if not cls.supervisor_factory:
-            cls.supervisor_factory = cls.available_servers.get(conf.get('server', 'wsgi'))
-            if cls.supervisor_factory is None:
-                raise RuntimeError('Unknown server in config: %s' % conf['server'])
-        supervisor = cls.supervisor_factory(**conf)
+        supervisor_factory = cls.available_servers.get(conf.get('server', 'wsgi'))
+        if supervisor_factory is None:
+            raise RuntimeError('Unknown server in config: %s' % conf['server'])
+        supervisor = supervisor_factory(**conf)
 
         #Copied from "run_test_suite"
         cherrypy.config.reset()

cherrypy/test/modfastcgi.py

+"""Wrapper for mod_fastcgi, for use as a CherryPy HTTP server when testing.
+
+To autostart fastcgi, the "apache" executable or script must be
+on your system path, or you must override the global APACHE_PATH.
+On some platforms, "apache" may be called "apachectl", "apache2ctl",
+or "httpd"--create a symlink to them if needed.
+
+You'll also need the WSGIServer from flup.servers.
+See http://projects.amor.org/misc/wiki/ModPythonGateway
+
+
+KNOWN BUGS
+==========
+
+1. Apache processes Range headers automatically; CherryPy's truncated
+    output is then truncated again by Apache. See test_core.testRanges.
+    This was worked around in http://www.cherrypy.org/changeset/1319.
+2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
+    See test_core.testHTTPMethods.
+3. Max request header and body settings do not work with Apache.
+4. Apache replaces status "reason phrases" automatically. For example,
+    CherryPy may set "304 Not modified" but Apache will write out
+    "304 Not Modified" (capital "M").
+5. Apache does not allow custom error codes as per the spec.
+6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
+    Request-URI too early.
+7. mod_python will not read request bodies which use the "chunked"
+    transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
+    instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
+    mod_python's requestobject.c).
+8. Apache will output a "Content-Length: 0" response header even if there's
+    no response entity body. This isn't really a bug; it just differs from
+    the CherryPy default.
+"""
+
+import os
+curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
+import re
+import sys
+import time
+
+import cherrypy
+from cherrypy.process import plugins, servers
+from cherrypy.test import helper
+
+
+def read_process(cmd, args=""):
+    pipein, pipeout = os.popen4("%s %s" % (cmd, args))
+    try:
+        firstline = pipeout.readline()
+        if (re.search(r"(not recognized|No such file|not found)", firstline,
+                      re.IGNORECASE)):
+            raise IOError('%s must be on your system path.' % cmd)
+        output = firstline + pipeout.read()
+    finally:
+        pipeout.close()
+    return output
+
+
+APACHE_PATH = "apache2ctl"
+CONF_PATH = "fastcgi.conf"
+
+conf_fastcgi = """
+# Apache2 server conf file for testing CherryPy with mod_fastcgi.
+# fumanchu: I had to hard-code paths due to crazy Debian layouts :(
+ServerRoot /usr/lib/apache2
+User #1000
+ErrorLog %(root)s/mod_fastcgi.error.log
+
+DocumentRoot "%(root)s"
+ServerName 127.0.0.1
+Listen %(port)s
+LoadModule fastcgi_module modules/mod_fastcgi.so
+LoadModule rewrite_module modules/mod_rewrite.so
+
+Options +ExecCGI
+SetHandler fastcgi-script
+RewriteEngine On
+RewriteRule ^(.*)$ /fastcgi.pyc [L]
+FastCgiExternalServer "%(server)s" -host 127.0.0.1:4000
+"""
+
+def erase_script_name(environ, start_response):
+    environ['SCRIPT_NAME'] = ''
+    return cherrypy.tree(environ, start_response)
+
+class ModFCGISupervisor(helper.LocalWSGISupervisor):
+    
+    httpserver_class = "cherrypy.process.servers.FlupFCGIServer"
+    using_apache = True
+    using_wsgi = True
+    template = conf_fastcgi
+    
+    def __str__(self):
+        return "FCGI Server on %s:%s" % (self.host, self.port)
+    
+    def start(self, modulename):
+        cherrypy.server.httpserver = servers.FlupFCGIServer(
+            application=erase_script_name, bindAddress=('127.0.0.1', 4000))
+        cherrypy.server.httpserver.bind_addr = ('127.0.0.1', 4000)
+        cherrypy.server.socket_port = 4000
+        # For FCGI, we both start apache...
+        self.start_apache()
+        # ...and our local server
+        cherrypy.engine.start()
+        self.sync_apps()
+    
+    def start_apache(self):
+        fcgiconf = CONF_PATH
+        if not os.path.isabs(fcgiconf):
+            fcgiconf = os.path.join(curdir, fcgiconf)
+        
+        # Write the Apache conf file.
+        f = open(fcgiconf, 'wb')
+        try:
+            server = repr(os.path.join(curdir, 'fastcgi.pyc'))[1:-1]
+            output = self.template % {'port': self.port, 'root': curdir,
+                                      'server': server}
+            output = output.replace('\r\n', '\n')
+            f.write(output)
+        finally:
+            f.close()
+        
+        result = read_process(APACHE_PATH, "-k start -f %s" % fcgiconf)
+        if result:
+            print(result)
+    
+    def stop(self):
+        """Gracefully shutdown a server that is serving forever."""
+        read_process(APACHE_PATH, "-k stop")
+        helper.LocalWSGISupervisor.stop(self)
+    
+    def sync_apps(self):
+        cherrypy.server.httpserver.fcgiserver.application = self.get_app(erase_script_name)
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.