|
florian@4287
|
1 |
# -*- coding: iso-8859-1 -*-
|
|
florian@4287
|
2 |
"""
|
|
florian@4287
|
3 |
MoinMoin - Flup based WSGI adapters
|
|
florian@4287
|
4 |
|
|
florian@4322
|
5 |
This module provides adapters between popular gateway interfaces
|
|
florian@4322
|
6 |
like CGI, FastCGI, SCGI and AJP to the MoinMoin WSGI application.
|
|
florian@4322
|
7 |
They are based on the adapters in the flup package and upon the
|
|
florian@4322
|
8 |
MoinMoin.frontend.ServerFrontEnd to provide configuration via
|
|
florian@4322
|
9 |
command line switches.
|
|
florian@4322
|
10 |
|
|
florian@4322
|
11 |
Typically they are simply run from the CGI-scripts like this:
|
|
florian@4322
|
12 |
|
|
florian@4322
|
13 |
> from MoinMoin.web.flup_frontend import CGIFrontEnd
|
|
florian@4322
|
14 |
> CGIFrontEnd().run()
|
|
florian@4322
|
15 |
|
|
florian@4322
|
16 |
They automatically parse the options given on the commandline and
|
|
florian@4322
|
17 |
behave accordingly. Flup makes it possible to serve FCGI, SCGI and
|
|
florian@4322
|
18 |
AJP from a bound network or unix socket, in different flavours of
|
|
florian@4322
|
19 |
multiprocessing/multithreading.
|
|
florian@4322
|
20 |
|
|
tw@4653
|
21 |
@copyright: 2008 MoinMoin:FlorianKrupicka,
|
|
tw@4653
|
22 |
2009 MoinMoin:ThomasWaldmann
|
|
florian@4287
|
23 |
@license: GNU GPL, see COPYING for details.
|
|
florian@4287
|
24 |
"""
|
|
tw@4653
|
25 |
|
|
tw@4653
|
26 |
import os, sys
|
|
tw@4653
|
27 |
|
|
florian@4289
|
28 |
try:
|
|
florian@4289
|
29 |
import flup.server.fcgi
|
|
florian@4289
|
30 |
have_flup = True
|
|
florian@4289
|
31 |
try:
|
|
florian@4289
|
32 |
import flup.server.fcgi_single
|
|
florian@4289
|
33 |
have_singlepatch = True
|
|
florian@4289
|
34 |
except ImportError:
|
|
florian@4289
|
35 |
have_singlepatch = False
|
|
florian@4289
|
36 |
except ImportError:
|
|
florian@4289
|
37 |
have_flup = False
|
|
florian@4289
|
38 |
|
|
florian@4289
|
39 |
from MoinMoin.web.frontend import ServerFrontEnd, FrontEnd, FrontEndNotAvailable
|
|
florian@4287
|
40 |
|
|
florian@4287
|
41 |
from MoinMoin import log
|
|
florian@4287
|
42 |
logging = log.getLogger(__name__)
|
|
florian@4287
|
43 |
|
|
florian@4289
|
44 |
if have_flup:
|
|
florian@4289
|
45 |
class FlupFrontEnd(ServerFrontEnd):
|
|
florian@4289
|
46 |
def add_options(self):
|
|
florian@4289
|
47 |
super(FlupFrontEnd, self).add_options()
|
|
florian@4289
|
48 |
parser = self.parser
|
|
florian@4289
|
49 |
parser.add_option("--min-spare", dest="min_spare", type="int", metavar='MIN',
|
|
florian@4289
|
50 |
help=("Minimum spare threads/processes (when "
|
|
florian@4289
|
51 |
"using threaded or forking servers)."))
|
|
florian@4289
|
52 |
parser.add_option("--max-spare", dest="max_spare", type="int", metavar='MAX',
|
|
florian@4289
|
53 |
help=("Maximum spare threads/processes (when "
|
|
florian@4289
|
54 |
"using threaded or forking servers)."))
|
|
florian@4289
|
55 |
parser.add_option("--max-childs", dest="max_childs", type="int", metavar='CHILDS',
|
|
florian@4289
|
56 |
help=("Hard upper limit on threads/processes "
|
|
florian@4289
|
57 |
"(when using threaded or forking servers)."))
|
|
florian@4289
|
58 |
parser.add_option("-t", "--type", dest="server_type", metavar='TYPE',
|
|
florian@4289
|
59 |
help=("Type of server to use, e.g. single/threaded"
|
|
florian@4289
|
60 |
"/forking. Defaults to 'single' when not "
|
|
florian@4289
|
61 |
"bound to a socket and to 'threaded' when it is"))
|
|
florian@4287
|
62 |
|
|
florian@4289
|
63 |
def run_server(self, application, options):
|
|
florian@4289
|
64 |
server_type = options.server_type
|
|
florian@4287
|
65 |
|
|
florian@4289
|
66 |
if not server_type:
|
|
tw@4525
|
67 |
if 'single' in self.server_types:
|
|
florian@4289
|
68 |
server_type = (options.port and 'threaded') or 'single'
|
|
florian@4289
|
69 |
else:
|
|
florian@4289
|
70 |
server_type = 'threaded'
|
|
florian@4287
|
71 |
|
|
tw@4525
|
72 |
if server_type not in self.server_types:
|
|
florian@4289
|
73 |
raise TypeError("Unknown server type '%s'" % options.server_type)
|
|
florian@4287
|
74 |
|
|
florian@4289
|
75 |
multi = server_type in ('threaded', 'forking')
|
|
tw@4525
|
76 |
|
|
tw@4525
|
77 |
mod = self.server_types[server_type]
|
|
tw@4925
|
78 |
mod = __import__(mod, globals(), locals(), ['WSGIServer'])
|
|
tw@4653
|
79 |
WSGIServerWrapped = mod.WSGIServer
|
|
tw@4653
|
80 |
|
|
tw@4653
|
81 |
class WSGIServer(WSGIServerWrapped):
|
|
tw@5378
|
82 |
# note: base class uses debug=False as default. as we use string values,
|
|
tw@5378
|
83 |
# better explicitely pass in "off", "web" or "external".
|
|
tw@4653
|
84 |
def error(self, req):
|
|
tw@4653
|
85 |
""" Override the default handler, so it implements debug=web/external/off. """
|
|
tw@4653
|
86 |
if self.debug == 'external':
|
|
tw@4653
|
87 |
raise
|
|
tw@4653
|
88 |
elif self.debug == 'web':
|
|
tw@4653
|
89 |
import cgitb
|
|
tw@4653
|
90 |
req.stdout.write('Content-Type: text/html\r\n\r\n' +
|
|
tw@4653
|
91 |
cgitb.html(sys.exc_info()))
|
|
tw@4653
|
92 |
else: # 'off'
|
|
tw@4653
|
93 |
errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
|
|
tw@4653
|
94 |
<html><head>
|
|
tw@4653
|
95 |
<title>Unhandled Exception</title>
|
|
tw@4653
|
96 |
</head><body>
|
|
tw@4653
|
97 |
<h1>Unhandled Exception</h1>
|
|
tw@4653
|
98 |
<p>An unhandled exception was thrown by the application.</p>
|
|
tw@4653
|
99 |
</body></html>
|
|
tw@4653
|
100 |
"""
|
|
tw@4653
|
101 |
req.stdout.write('Content-Type: text/html\r\n\r\n' + errorpage)
|
|
tw@4653
|
102 |
|
|
florian@4287
|
103 |
|
|
florian@4289
|
104 |
kwargs = {}
|
|
florian@4287
|
105 |
|
|
tw@4653
|
106 |
kwargs['debug'] = options.debug or os.environ.get('MOIN_DEBUGGER', 'off')
|
|
tw@4653
|
107 |
|
|
florian@4289
|
108 |
if options.port:
|
|
florian@4289
|
109 |
kwargs['bindAddress'] = (options.interface, options.port)
|
|
tw@4532
|
110 |
elif options.interface and (
|
|
tw@4532
|
111 |
options.interface.startswith('/') or options.interface.startswith('./')):
|
|
florian@4289
|
112 |
kwargs['bindAddress'] = options.interface
|
|
florian@4287
|
113 |
|
|
florian@4289
|
114 |
if options.min_spare and multi:
|
|
florian@4289
|
115 |
kwargs['minSpare'] = options.min_spare
|
|
florian@4289
|
116 |
if options.max_spare and multi:
|
|
florian@4289
|
117 |
kwargs['maxSpare'] = options.max_spare
|
|
florian@4289
|
118 |
if options.max_childs and multi:
|
|
florian@4289
|
119 |
if server_type == 'threaded':
|
|
florian@4289
|
120 |
kwargs['maxThreads'] = options.max_childs
|
|
florian@4289
|
121 |
else:
|
|
florian@4289
|
122 |
kwargs['maxChildren'] = options.max_childs
|
|
tw@4532
|
123 |
logging.debug("WSGIServer(%r, %r)" % (application, kwargs))
|
|
florian@4287
|
124 |
return WSGIServer(application, **kwargs).run()
|
|
florian@4289
|
125 |
|
|
florian@4289
|
126 |
class CGIFrontEnd(FlupFrontEnd):
|
|
florian@4289
|
127 |
server_types = {'threaded': 'flup.server.fcgi',
|
|
florian@4289
|
128 |
'forking': 'flup.server.fcgi_fork'}
|
|
florian@4289
|
129 |
if have_singlepatch:
|
|
florian@4289
|
130 |
server_types['single'] = 'flup.server.fcgi_single'
|
|
florian@4289
|
131 |
|
|
tw@5461
|
132 |
def run(self, args=None):
|
|
tw@5461
|
133 |
if 'GATEWAY_INTERFACE' in os.environ:
|
|
tw@5461
|
134 |
sys.argv = []
|
|
tw@5461
|
135 |
super(CGIFrontEnd, self).run(args)
|
|
tw@5461
|
136 |
|
|
florian@4289
|
137 |
class SCGIFrontEnd(FlupFrontEnd):
|
|
florian@4289
|
138 |
server_types = {'threaded': 'flup.server.scgi',
|
|
florian@4289
|
139 |
'forking': 'flup.server.scgi_fork'}
|
|
florian@4289
|
140 |
|
|
florian@4289
|
141 |
class AJPFrontEnd(FlupFrontEnd):
|
|
florian@4289
|
142 |
server_types = {'threaded': 'flup.server.ajp',
|
|
florian@4289
|
143 |
'forking': 'flup.server.ajp_fork'}
|
|
florian@4289
|
144 |
else:
|
|
florian@4289
|
145 |
class CGIFrontEnd(FrontEnd):
|
|
florian@4289
|
146 |
""" Simple WSGI CGI Adapter for fallback if flup is not installed. """
|
|
florian@4289
|
147 |
def __init__(self):
|
|
florian@4289
|
148 |
logging.warning("No flup-package installed, only basic CGI "
|
|
florian@4289
|
149 |
"support is available.")
|
|
florian@4289
|
150 |
super(CGIFrontEnd, self).__init__()
|
|
florian@4289
|
151 |
|
|
tw@5461
|
152 |
def run(self, args=None):
|
|
tw@5461
|
153 |
if 'GATEWAY_INTERFACE' in os.environ:
|
|
tw@5461
|
154 |
sys.argv = []
|
|
tw@5461
|
155 |
super(CGIFrontEnd, self).run(args)
|
|
tw@5461
|
156 |
|
|
florian@4289
|
157 |
def run_server(self, application, options):
|
|
florian@4289
|
158 |
from MoinMoin.web._fallback_cgi import WSGIServer
|
|
florian@4287
|
159 |
return WSGIServer(application).run()
|
|
florian@4287
|
160 |
|
|
florian@4289
|
161 |
_ERROR = """
|
|
florian@4289
|
162 |
The flup package is not installed on your system. To make use of FCGI,
|
|
florian@4289
|
163 |
SCGI or AJP adapters, you have to install it first. The MoinMoin source
|
|
florian@4289
|
164 |
distribution provides a flup package in the contrib/flup-server
|
|
florian@4289
|
165 |
directory. It is also patched to support non-threaded & non-forking
|
|
florian@4289
|
166 |
behaviour. See contrib/flup-server/NOTES.moin for more information.
|
|
florian@4289
|
167 |
"""
|
|
florian@4289
|
168 |
def SCGIFrontEnd():
|
|
florian@4289
|
169 |
raise FrontEndNotAvailable(_ERROR)
|
|
florian@4289
|
170 |
def AJPFrontEnd():
|
|
florian@4289
|
171 |
raise FrontEndNotAvailable(_ERROR)
|