Skip to content
Newer
Older
100755 227 lines (157 sloc) 5.9 KB
273883c Added web2py 1.98.2
spiffy authored
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 This file is part of the web2py Web Framework
6 Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
7 License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
8
9 WSGI wrapper for mod_python. Requires Python 2.2 or greater.
10 Part of CherryPy mut modified by Massimo Di Pierro (2008) for web2py
11
12 <Location /myapp>
13 SetHandler python-program
14 PythonHandler modpythonhandler
15 PythonPath \"['/path/to/web2py/'] + sys.path\"
16 PythonOption SCRIPT_NAME /myapp
17 </Location>
18
19 Some WSGI implementations assume that the SCRIPT_NAME environ variable will
20 always be equal to 'the root URL of the app'; Apache probably won't act as
21 you expect in that case. You can add another PythonOption directive to tell
22 modpython_gateway to force that behavior:
23
24 PythonOption SCRIPT_NAME /mcontrol
25
26 The module.function will be called with no arguments on server shutdown,
27 once for each child process or thread.
28 """
29
30 import traceback
31 import sys
32 import os
33 from mod_python import apache
34
35 path = os.path.dirname(os.path.abspath(__file__))
36 os.chdir(path)
37 sys.path = [path]+[p for p in sys.path if not p==path]
38
39 import gluon.main
40
41
42 class InputWrapper(object):
43 """ Input wrapper for the wsgi handler """
44
45 def __init__(self, req):
46 """ InputWrapper constructor """
47
48 self.req = req
49
50 def close(self):
51 """ """
52
53 pass
54
55 def read(self, size=-1):
56 """ Wrapper for req.read """
57
58 return self.req.read(size)
59
60 def readline(self, size=-1):
61 """ Wrapper for req.readline """
62
63 return self.req.readline(size)
64
65 def readlines(self, hint=-1):
66 """ Wrapper for req.readlines """
67
68 return self.req.readlines(hint)
69
70 def __iter__(self):
71 """ Defines a generator with the req data """
72
73 line = self.readline()
74 while line:
75 yield line
76
77 # Notice this won't prefetch the next line; it only
78 # gets called if the generator is resumed.
79 line = self.readline()
80
81
82 class ErrorWrapper(object):
83 """ Error wrapper for the wsgi handler """
84
85 def __init__(self, req):
86 """ ErrorWrapper constructor """
87
88 self.req = req
89
90 def flush(self):
91 """ """
92
93 pass
94
95 def write(self, msg):
96 """ Logs the given msg in the log file """
97
98 self.req.log_error(msg)
99
100 def writelines(self, seq):
101 """ Writes various lines in the log file """
102
103 self.write(''.join(seq))
104
105
106 bad_value = "You must provide a PythonOption '%s', either 'on' or 'off', when running a version of mod_python < 3.1"
107
108
109 class Handler:
110 """ Defines the handler """
111
112 def __init__(self, req):
113 """ Handler constructor """
114
115 self.started = False
116 options = req.get_options()
117
118 # Threading and forking
119 try:
120 q = apache.mpm_query
121 threaded = q(apache.AP_MPMQ_IS_THREADED)
122 forked = q(apache.AP_MPMQ_IS_FORKED)
123 except AttributeError:
124 threaded = options.get('multithread', '').lower()
125
126 if threaded == 'on':
127 threaded = True
128 elif threaded == 'off':
129 threaded = False
130 else:
131 raise ValueError(bad_value % 'multithread')
132
133 forked = options.get('multiprocess', '').lower()
134
135 if forked == 'on':
136 forked = True
137 elif forked == 'off':
138 forked = False
139 else:
140 raise ValueError(bad_value % 'multiprocess')
141
142 env = self.environ = dict(apache.build_cgi_env(req))
143
144 if 'SCRIPT_NAME' in options:
145 # Override SCRIPT_NAME and PATH_INFO if requested.
146 env['SCRIPT_NAME'] = options['SCRIPT_NAME']
147 env['PATH_INFO'] = req.uri[len(options['SCRIPT_NAME']):]
148
149 env['wsgi.input'] = InputWrapper(req)
150 env['wsgi.errors'] = ErrorWrapper(req)
151 env['wsgi.version'] = (1, 0)
152 env['wsgi.run_once'] = False
153
154 if env.get('HTTPS') in ('yes', 'on', '1'):
155 env['wsgi.url_scheme'] = 'https'
156 else:
157 env['wsgi.url_scheme'] = 'http'
158
159 env['wsgi.multithread'] = threaded
160 env['wsgi.multiprocess'] = forked
161
162 self.request = req
163
164 def run(self, application):
165 """ Run the application """
166
167 try:
168 result = application(self.environ, self.start_response)
169
170 for data in result:
171 self.write(data)
172
173 if not self.started:
174 self.request.set_content_length(0)
175
176 if hasattr(result, 'close'):
177 result.close()
178 except:
179 traceback.print_exc(None, self.environ['wsgi.errors'])
180
181 if not self.started:
182 self.request.status = 500
183 self.request.content_type = 'text/plain'
184 data = 'A server error occurred. Please contact the ' + \
185 'administrator.'
186 self.request.set_content_length(len(data))
187 self.request.write(data)
188
189 def start_response(self, status, headers, exc_info=None):
190 """ Defines the request data """
191
192 if exc_info:
193 try:
194 if self.started:
195 raise exc_info[0], exc_info[1], exc_info[2]
196 finally:
197 exc_info = None
198
199 self.request.status = int(status[:3])
200
201 for (key, val) in headers:
202 if key.lower() == 'content-length':
203 self.request.set_content_length(int(val))
204 elif key.lower() == 'content-type':
205 self.request.content_type = val
206 else:
207 self.request.headers_out.add(key, val)
208
209 return self.write
210
211 def write(self, data):
212 """ Write the request data """
213
214 if not self.started:
215 self.started = True
216
217 self.request.write(data)
218
219
220 def handler(req):
221 """ Execute the gluon app """
222
223 Handler(req).run(gluon.main.wsgibase)
224 return apache.OK
f3422c1 Upgraded to web2py 1.99.2
spiffy authored
225
7a75fac @spiffytech Updated to web2py version 1.99.4
authored
226
Something went wrong with that request. Please try again.