1 """WSGI interface (see PEP 333)."""
2
3 import StringIO as _StringIO
4 import sys as _sys
5
6 import cherrypy as _cherrypy
7 from cherrypy import _cperror, wsgiserver
8 from cherrypy.lib import http as _http
9
10
11
12
13
15 """WSGI middleware which handles cherrypy.InternalRedirect.
16
17 When cherrypy.InternalRedirect is raised, this middleware traps it,
18 rewrites the WSGI environ using the new path and query_string,
19 and calls the next application again. Because the wsgi.input stream
20 may have already been consumed by the next application, the redirected
21 call will always be of HTTP method "GET", and therefore any params must
22 be passed in the InternalRedirect object's query_string attribute.
23 If you need something more complicated, make and raise your own
24 exception and your own WSGI middleware to trap it. ;)
25
26 It would be a bad idea to raise InternalRedirect after you've already
27 yielded response content, although an enterprising soul could choose
28 to abuse this.
29
30 nextapp: the next application callable in the WSGI chain.
31
32 recursive: if False (the default), each URL (path + qs) will be
33 stored, and, if the same URL is requested again, RuntimeError will
34 be raised. If 'recursive' is True, no such error will be raised.
35 """
36
37 - def __init__(self, nextapp, recursive=False):
40
41 - def __call__(self, environ, start_response):
43
44
46
47 - def __init__(self, nextapp, environ, start_response, recursive):
56
66
68 env = self.environ
69 if not self.recursive:
70 if ir.path in self.redirections:
71 raise RuntimeError("InternalRedirector visited the "
72 "same URL twice: %r" % ir.path)
73 else:
74
75 sn = env.get('SCRIPT_NAME', '')
76 path = env.get('PATH_INFO', '')
77 qs = env.get('QUERY_STRING', '')
78 if qs:
79 qs = "?" + qs
80 self.redirections.append(sn + path + qs)
81
82
83 env['REQUEST_METHOD'] = "GET"
84 env['PATH_INFO'] = ir.path
85 env['QUERY_STRING'] = ir.query_string
86 env['wsgi.input'] = _StringIO.StringIO()
87 env['CONTENT_LENGTH'] = "0"
88
92
95
104
105
106
107
108
109
111
112 throws = (KeyboardInterrupt, SystemExit, _cherrypy.InternalRedirect)
113 request = None
114
115 - def __init__(self, environ, start_response, cpapp):
116 try:
117 self.request = self.get_engine_request(environ, cpapp)
118
119 meth = environ['REQUEST_METHOD']
120 path = environ.get('SCRIPT_NAME', '') + environ.get('PATH_INFO', '')
121 qs = environ.get('QUERY_STRING', '')
122 rproto = environ.get('SERVER_PROTOCOL')
123 headers = self.translate_headers(environ)
124 rfile = environ['wsgi.input']
125
126 response = self.request.run(meth, path, qs, rproto, headers, rfile)
127 s, h, b = response.status, response.header_list, response.body
128 exc = None
129 except self.throws:
130 self.close()
131 raise
132 except:
133 if getattr(self.request, "throw_errors", False):
134 self.close()
135 raise
136
137 tb = _cperror.format_exc()
138 _cherrypy.log(tb)
139 if not getattr(self.request, "show_tracebacks", True):
140 tb = ""
141 s, h, b = _cperror.bare_error(tb)
142 exc = _sys.exc_info()
143
144 self.iter_response = iter(b)
145
146 try:
147 start_response(s, h, exc)
148 except self.throws:
149 self.close()
150 raise
151 except:
152 if getattr(self.request, "throw_errors", False):
153 self.close()
154 raise
155
156 _cherrypy.log(traceback=True)
157 self.close()
158
159
160
161
162 s, h, b = _cperror.bare_error()
163 self.iter_response = iter(b)
164
167
169 try:
170 chunk = self.iter_response.next()
171
172
173
174 if not isinstance(chunk, str):
175 chunk = chunk.encode("ISO-8859-1")
176 return chunk
177 except self.throws:
178 raise
179 except StopIteration:
180 raise
181 except:
182 if getattr(self.request, "throw_errors", False):
183 raise
184
185 _cherrypy.log(traceback=True)
186
187
188
189
190 s, h, b = _cperror.bare_error()
191 self.iter_response = iter([])
192 return "".join(b)
193
196
198 """Return a Request object from the CherryPy Engine using environ."""
199 env = environ.get
200
201 local = _http.Host('', int(env('SERVER_PORT', 80)),
202 env('SERVER_NAME', ''))
203 remote = _http.Host(env('REMOTE_ADDR', ''),
204 int(env('REMOTE_PORT', -1)),
205 env('REMOTE_HOST', ''))
206 scheme = env('wsgi.url_scheme')
207 sproto = env('ACTUAL_SERVER_PROTOCOL', "HTTP/1.1")
208 request = _cherrypy.engine.request(local, remote, scheme, sproto)
209
210
211
212
213 request.login = env('LOGON_USER') or env('REMOTE_USER') or None
214 request.multithread = environ['wsgi.multithread']
215 request.multiprocess = environ['wsgi.multiprocess']
216 request.wsgi_environ = environ
217 request.app = cpapp
218 request.prev = env('cherrypy.request')
219 environ['cherrypy.request'] = request
220 return request
221
222 headerNames = {'HTTP_CGI_AUTHORIZATION': 'Authorization',
223 'CONTENT_LENGTH': 'Content-Length',
224 'CONTENT_TYPE': 'Content-Type',
225 'REMOTE_HOST': 'Remote-Host',
226 'REMOTE_ADDR': 'Remote-Addr',
227 }
228
230 """Translate CGI-environ header names to HTTP header names."""
231 for cgiName in environ:
232
233 if cgiName in self.headerNames:
234 yield self.headerNames[cgiName], environ[cgiName]
235 elif cgiName[:5] == "HTTP_":
236
237 translatedHeader = cgiName[5:].replace("_", "-")
238 yield translatedHeader, environ[cgiName]
239
240
242 """A WSGI application object for a CherryPy Application.
243
244 pipeline: a list of (name, wsgiapp) pairs. Each 'wsgiapp' MUST be a
245 constructor that takes an initial, positional 'nextapp' argument,
246 plus optional keyword arguments, and returns a WSGI application
247 (that takes environ and start_response arguments). The 'name' can
248 be any you choose, and will correspond to keys in self.config.
249
250 head: rather than nest all apps in the pipeline on each call, it's only
251 done the first time, and the result is memoized into self.head. Set
252 this to None again if you change self.pipeline after calling self.
253
254 config: a dict whose keys match names listed in the pipeline. Each
255 value is a further dict which will be passed to the corresponding
256 named WSGI callable (from the pipeline) as keyword arguments.
257 """
258
259 pipeline = [('iredir', InternalRedirector)]
260 head = None
261 config = {}
262
263 response_class = AppResponse
264
265 - def __init__(self, cpapp, pipeline=None):
271
272 - def tail(self, environ, start_response):
273 """WSGI application callable for the actual CherryPy application.
274
275 You probably shouldn't call this; call self.__call__ instead,
276 so that any WSGI middleware in self.pipeline can run first.
277 """
278 return self.response_class(environ, start_response, self.cpapp)
279
280 - def __call__(self, environ, start_response):
291
293 """Config handler for the 'wsgi' namespace."""
294 if k == "pipeline":
295
296
297
298
299
300 self.pipeline.extend(v)
301 else:
302 name, arg = k.split(".", 1)
303 bucket = self.config.setdefault(name, {})
304 bucket[arg] = v
305
306
307
308
309
310
337
338
342
343
345
346 """Wrapper for wsgiserver.CherryPyWSGIServer.
347
348 wsgiserver has been designed to not reference CherryPy in any way,
349 so that it can be used in other frameworks and applications. Therefore,
350 we wrap it here, so we can set our own mount points from cherrypy.tree.
351
352 """
353
354 ConnectionClass = CPHTTPConnection
355
376