Back to index

moin  1.9.0~rc2
flup_frontend.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - Flup based WSGI adapters
00004 
00005     This module provides adapters between popular gateway interfaces
00006     like CGI, FastCGI, SCGI and AJP to the MoinMoin WSGI application.
00007     They are based on the adapters in the flup package and upon the
00008     MoinMoin.frontend.ServerFrontEnd to provide configuration via
00009     command line switches.
00010 
00011     Typically they are simply run from the CGI-scripts like this:
00012 
00013     > from MoinMoin.web.flup_frontend import CGIFrontEnd
00014     > CGIFrontEnd().run()
00015 
00016     They automatically parse the options given on the commandline and
00017     behave accordingly. Flup makes it possible to serve FCGI, SCGI and
00018     AJP from a bound network or unix socket, in different flavours of
00019     multiprocessing/multithreading.
00020 
00021     @copyright: 2008 MoinMoin:FlorianKrupicka,
00022                 2009 MoinMoin:ThomasWaldmann
00023     @license: GNU GPL, see COPYING for details.
00024 """
00025 
00026 import os, sys
00027 
00028 try:
00029     import flup.server.fcgi
00030     have_flup = True
00031     try:
00032         import flup.server.fcgi_single
00033         have_singlepatch = True
00034     except ImportError:
00035         have_singlepatch = False
00036 except ImportError:
00037     have_flup = False
00038 
00039 from MoinMoin.web.frontend import ServerFrontEnd, FrontEnd, FrontEndNotAvailable
00040 
00041 from MoinMoin import log
00042 logging = log.getLogger(__name__)
00043 
00044 if have_flup:
00045     from flup.server.fcgi_base import FCGI_RESPONDER
00046     class FlupFrontEnd(ServerFrontEnd):
00047         def add_options(self):
00048             super(FlupFrontEnd, self).add_options()
00049             parser = self.parser
00050             parser.add_option("--min-spare", dest="min_spare", type="int", metavar='MIN',
00051                               help=("Minimum spare threads/processes (when "
00052                                     "using threaded or forking servers)."))
00053             parser.add_option("--max-spare", dest="max_spare", type="int", metavar='MAX',
00054                               help=("Maximum spare threads/processes (when "
00055                                     "using threaded or forking servers)."))
00056             parser.add_option("--max-childs", dest="max_childs", type="int", metavar='CHILDS',
00057                               help=("Hard upper limit on threads/processes "
00058                                     "(when using threaded or forking servers)."))
00059             parser.add_option("-t", "--type", dest="server_type", metavar='TYPE',
00060                               help=("Type of server to use, e.g. single/threaded"
00061                                     "/forking. Defaults to 'single' when not "
00062                                     "bound to a socket and to 'threaded' when it is"))
00063 
00064         def run_server(self, application, options):
00065             server_type = options.server_type
00066 
00067             if not server_type:
00068                 if 'single' in self.server_types:
00069                     server_type = (options.port and 'threaded') or 'single'
00070                 else:
00071                     server_type = 'threaded'
00072 
00073             if server_type not in self.server_types:
00074                 raise TypeError("Unknown server type '%s'" % options.server_type)
00075 
00076             multi = server_type in ('threaded', 'forking')
00077 
00078             mod = self.server_types[server_type]
00079             mod = __import__(mod, globals(), locals(), ['WSGIServer'])
00080             WSGIServerWrapped = mod.WSGIServer
00081 
00082             class WSGIServer(WSGIServerWrapped):
00083                 def __init__(self, application, environ=None,
00084                              multithreaded=True, multiprocess=False,
00085                              bindAddress=None, umask=None, multiplexed=False,
00086                              debug="off", roles=(FCGI_RESPONDER, )):
00087                     WSGIServerWrapped.__init__(self, application, environ=environ,
00088                                                multithreaded=multithreaded, multiprocess=multiprocess,
00089                                                bindAddress=bindAddress, umask=umask, multiplexed=multiplexed,
00090                                                debug=debug, roles=roles)
00091 
00092                 def error(self, req):
00093                     """ Override the default handler, so it implements debug=web/external/off. """
00094                     if self.debug == 'external':
00095                         raise
00096                     elif self.debug == 'web':
00097                         import cgitb
00098                         req.stdout.write('Content-Type: text/html\r\n\r\n' +
00099                                          cgitb.html(sys.exc_info()))
00100                     else: # 'off'
00101                         errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
00102 <html><head>
00103 <title>Unhandled Exception</title>
00104 </head><body>
00105 <h1>Unhandled Exception</h1>
00106 <p>An unhandled exception was thrown by the application.</p>
00107 </body></html>
00108 """
00109                         req.stdout.write('Content-Type: text/html\r\n\r\n' + errorpage)
00110 
00111 
00112             kwargs = {}
00113 
00114             kwargs['debug'] = options.debug or os.environ.get('MOIN_DEBUGGER', 'off')
00115 
00116             if options.port:
00117                 kwargs['bindAddress'] = (options.interface, options.port)
00118             elif options.interface and (
00119                  options.interface.startswith('/') or options.interface.startswith('./')):
00120                 kwargs['bindAddress'] = options.interface
00121 
00122             if options.min_spare and multi:
00123                 kwargs['minSpare'] = options.min_spare
00124             if options.max_spare and multi:
00125                 kwargs['maxSpare'] = options.max_spare
00126             if options.max_childs and multi:
00127                 if server_type == 'threaded':
00128                     kwargs['maxThreads'] = options.max_childs
00129                 else:
00130                     kwargs['maxChildren'] = options.max_childs
00131             logging.debug("WSGIServer(%r, %r)" % (application, kwargs))
00132             return WSGIServer(application, **kwargs).run()
00133 
00134     class CGIFrontEnd(FlupFrontEnd):
00135         server_types = {'threaded': 'flup.server.fcgi',
00136                         'forking': 'flup.server.fcgi_fork'}
00137         if have_singlepatch:
00138             server_types['single'] = 'flup.server.fcgi_single'
00139 
00140     class SCGIFrontEnd(FlupFrontEnd):
00141         server_types = {'threaded': 'flup.server.scgi',
00142                         'forking': 'flup.server.scgi_fork'}
00143 
00144     class AJPFrontEnd(FlupFrontEnd):
00145         server_types = {'threaded': 'flup.server.ajp',
00146                         'forking': 'flup.server.ajp_fork'}
00147 else:
00148     class CGIFrontEnd(FrontEnd):
00149         """ Simple WSGI CGI Adapter for fallback if flup is not installed. """
00150         def __init__(self):
00151             logging.warning("No flup-package installed, only basic CGI "
00152                             "support is available.")
00153             super(CGIFrontEnd, self).__init__()
00154 
00155         def run_server(self, application, options):
00156             from MoinMoin.web._fallback_cgi import WSGIServer
00157             return WSGIServer(application).run()
00158 
00159     _ERROR = """
00160 The flup package is not installed on your system. To make use of FCGI,
00161 SCGI or AJP adapters, you have to install it first. The MoinMoin source
00162 distribution provides a flup package in the contrib/flup-server
00163 directory. It is also patched to support non-threaded & non-forking
00164 behaviour. See contrib/flup-server/NOTES.moin for more information.
00165 """
00166     def SCGIFrontEnd():
00167         raise FrontEndNotAvailable(_ERROR)
00168     def AJPFrontEnd():
00169         raise FrontEndNotAvailable(_ERROR)