Back to index

moin  1.9.0~rc2
fcgi_fork.py
Go to the documentation of this file.
00001 """
00002 .. highlight:: python
00003    :linenothreshold: 5
00004 
00005 .. highlight:: bash
00006    :linenothreshold: 5
00007 
00008 fcgi - a FastCGI/WSGI gateway.
00009 
00010 :copyright: Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
00011   All rights reserved.
00012 :license:
00013 
00014  Redistribution and use in source and binary forms, with or without
00015  modification, are permitted provided that the following conditions
00016  are met:
00017 
00018  1. Redistributions of source code must retain the above copyright
00019     notice, this list of conditions and the following disclaimer.
00020  2. Redistributions in binary form must reproduce the above copyright
00021     notice, this list of conditions and the following disclaimer in the
00022     documentation and/or other materials provided with the distribution.
00023 
00024  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS **AS IS** AND
00025  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00026  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00027  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00028  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00029  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00030  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00031  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00033  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00034  SUCH DAMAGE.
00035 
00036 For more information about FastCGI, see http://www.fastcgi.com/.
00037 
00038 For more information about the Web Server Gateway Interface, see
00039 http://www.python.org/peps/pep-0333.html.
00040 
00041 Example usage::
00042 
00043   #!/usr/bin/env python
00044   from myapplication import app # Assume app is your WSGI application object
00045   from fcgi import WSGIServer
00046   WSGIServer(app).run()
00047 
00048 See the documentation for WSGIServer for more information.
00049 
00050 On most platforms, fcgi will fallback to regular CGI behavior if run in a
00051 non-FastCGI context. If you want to force CGI behavior, set the environment
00052 variable FCGI_FORCE_CGI to "Y" or "y".
00053 """
00054 
00055 __author__ = 'Allan Saddi <allan@saddi.com>'
00056 __version__ = '$Revision$'
00057 
00058 import os
00059 
00060 from flup.server.fcgi_base import BaseFCGIServer, FCGI_RESPONDER, \
00061      FCGI_MAX_CONNS, FCGI_MAX_REQS, FCGI_MPXS_CONNS
00062 from flup.server.preforkserver import PreforkServer
00063 
00064 __all__ = ['WSGIServer']
00065 
00066 class WSGIServer(BaseFCGIServer, PreforkServer):
00067     """
00068     FastCGI server that supports the Web Server Gateway Interface. See
00069     http://www.python.org/peps/pep-0333.html.
00070     """
00071     def __init__(self, application, environ=None,
00072                  bindAddress=None, umask=None, multiplexed=False,
00073                  debug=False, roles=(FCGI_RESPONDER,), forceCGI=False, **kw):
00074         """
00075         environ, if present, must be a dictionary-like object. Its
00076         contents will be copied into application's environ. Useful
00077         for passing application-specific variables.
00078 
00079         bindAddress, if present, must either be a string or a 2-tuple. If
00080         present, run() will open its own listening socket. You would use
00081         this if you wanted to run your application as an 'external' FastCGI
00082         app. (i.e. the webserver would no longer be responsible for starting
00083         your app) If a string, it will be interpreted as a filename and a UNIX
00084         socket will be opened. If a tuple, the first element, a string,
00085         is the interface name/IP to bind to, and the second element (an int)
00086         is the port number.
00087         """
00088         BaseFCGIServer.__init__(self, application,
00089                                 environ=environ,
00090                                 multithreaded=False,
00091                                 multiprocess=True,
00092                                 bindAddress=bindAddress,
00093                                 umask=umask,
00094                                 multiplexed=multiplexed,
00095                                 debug=debug,
00096                                 roles=roles,
00097                                 forceCGI=forceCGI)
00098         for key in ('multithreaded', 'multiprocess', 'jobClass', 'jobArgs'):
00099             if kw.has_key(key):
00100                 del kw[key]
00101         PreforkServer.__init__(self, jobClass=self._connectionClass,
00102                                jobArgs=(self,), **kw)
00103 
00104         try:
00105             import resource
00106             # Attempt to glean the maximum number of connections
00107             # from the OS.
00108             try:
00109                 maxProcs = resource.getrlimit(resource.RLIMIT_NPROC)[0]
00110                 maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
00111                 maxConns = min(maxConns, maxProcs)
00112             except AttributeError:
00113                 maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
00114         except ImportError:
00115             maxConns = 100 # Just some made up number.
00116         maxReqs = maxConns
00117         self.capability = {
00118             FCGI_MAX_CONNS: maxConns,
00119             FCGI_MAX_REQS: maxReqs,
00120             FCGI_MPXS_CONNS: 0
00121             }
00122 
00123     def _isClientAllowed(self, addr):
00124         return self._web_server_addrs is None or \
00125                (len(addr) == 2 and addr[0] in self._web_server_addrs)
00126 
00127     def run(self):
00128         """
00129         The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if
00130         SIGHUP was received, False otherwise.
00131         """
00132         self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS')
00133         if self._web_server_addrs is not None:
00134             self._web_server_addrs = map(lambda x: x.strip(),
00135                                          self._web_server_addrs.split(','))
00136 
00137         sock = self._setupSocket()
00138 
00139         ret = PreforkServer.run(self, sock)
00140 
00141         self._cleanupSocket(sock)
00142 
00143         return ret
00144 
00145 if __name__ == '__main__':
00146     def test_app(environ, start_response):
00147         """Probably not the most efficient example."""
00148         import cgi
00149         start_response('200 OK', [('Content-Type', 'text/html')])
00150         yield '<html><head><title>Hello World!</title></head>\n' \
00151               '<body>\n' \
00152               '<p>Hello World!</p>\n' \
00153               '<table border="1">'
00154         names = environ.keys()
00155         names.sort()
00156         for name in names:
00157             yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
00158                 name, cgi.escape(`environ[name]`))
00159 
00160         form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
00161                                 keep_blank_values=1)
00162         if form.list:
00163             yield '<tr><th colspan="2">Form data</th></tr>'
00164 
00165         for field in form.list:
00166             yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
00167                 field.name, field.value)
00168 
00169         yield '</table>\n' \
00170               '</body></html>\n'
00171 
00172     from wsgiref import validate
00173     test_app = validate.validator(test_app)
00174     WSGIServer(test_app).run()