Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cherrypy-2.x/cherrypy/_cpwsgi.py

Revision 1853 (checked in by fumanchu, 11 months ago)

Fix to wsgi parsing of the URI "*".

  • Property svn:eol-style set to native
Line 
1 """A WSGI application interface (see PEP 333)."""
2
3 try:
4     GeneratorExit
5 except NameError:
6     GeneratorExit = None
7
8 import sys
9 import cherrypy
10 from cherrypy import _cputil, _cpwsgiserver, _cpwsgiserver3
11 from cherrypy.lib import httptools
12
13
14 def requestLine(environ):
15     """Rebuild first line of the request (e.g. "GET /path HTTP/1.0")."""
16    
17     resource = httptools.urljoin(environ.get('SCRIPT_NAME', ''),
18                                  environ.get('PATH_INFO', ''))
19     if resource == "/*":
20         resource = "*"
21     elif not resource.startswith("/"):
22         resource = "/" + resource
23    
24     qString = environ.get('QUERY_STRING')
25     if qString:
26         resource += '?' + qString
27    
28     resource = resource.replace(" ", "%20")
29    
30     return ('%s %s %s' % (environ['REQUEST_METHOD'],
31                           resource or '/',
32                           environ['SERVER_PROTOCOL']
33                           )
34             )
35
36 headerNames = {'HTTP_CGI_AUTHORIZATION': 'Authorization',
37                'CONTENT_LENGTH': 'Content-Length',
38                'CONTENT_TYPE': 'Content-Type',
39                'REMOTE_HOST': 'Remote-Host',
40                'REMOTE_ADDR': 'Remote-Addr',
41                }
42
43 def translate_headers(environ):
44     """Translate CGI-environ header names to HTTP header names."""
45     for cgiName in environ:
46         translatedHeader = headerNames.get(cgiName.upper())
47         if translatedHeader:
48             yield translatedHeader, environ[cgiName]
49         elif cgiName.upper().startswith("HTTP_"):
50             # Hackish attempt at recovering original header names.
51             translatedHeader = cgiName[5:].replace("_", "-")
52             yield translatedHeader, environ[cgiName]
53
54
55 class NullWriter(object):
56    
57     def write(self, data):
58         pass
59
60 class ResponseIter(object):
61     def __init__(self, request, body):
62         self.body = body
63         self.request = request
64        
65     def __iter__(self):
66         if not self.body:
67             raise StopIteration
68         try:
69             for chunk in self.body:
70                 # WSGI requires all data to be of type "str". This coercion should
71                 # not take any time at all if chunk is already of type "str".
72                 # If it's unicode, it could be a big performance hit (x ~500).
73                 if not isinstance(chunk, str):
74                     chunk = chunk.encode("ISO-8859-1")
75                 yield chunk
76         except (KeyboardInterrupt, SystemExit), ex:
77             raise ex
78         except GeneratorExit:
79             raise
80         except:
81             cherrypy.log(traceback=True)
82             s, h, b = _cputil.bareError()
83             # CherryPy test suite expects bareError body to be output,
84             # so don't call start_response (which, according to PEP 333,
85             # may raise its own error at that point).
86             for chunk in b:
87                 # WSGI requires all data to be of type "str". This coercion should
88                 # not take any time at all if chunk is already of type "str".
89                 # If it's unicode, it could be a big performance hit (x ~500).
90                 if not isinstance(chunk, str):
91                     chunk = chunk.encode("ISO-8859-1")
92                 yield chunk
93    
94     def close(self):
95         try:
96             if self.request:
97                 self.request.close()
98         except:
99             cherrypy.log(traceback=True)
100         self.request = None
101
102 def wsgiApp(environ, start_response):
103     """The WSGI 'application object' for CherryPy."""
104    
105     # Trap screen output from BaseHTTPRequestHandler.log_message()
106     if not cherrypy.config.get('server.log_to_screen'):
107         sys.stderr = NullWriter()
108    
109     request = None
110     try:
111         env = environ.get
112         clientAddr = (env('REMOTE_ADDR', ''), int(env('REMOTE_PORT', -1)))
113         request = cherrypy.server.request(clientAddr, '', environ['wsgi.url_scheme'])
114        
115         # LOGON_USER is served by IIS, and is the name of the
116         # user after having been mapped to a local account.
117         # Both IIS and Apache set REMOTE_USER, when possible.
118         request.login = (env('LOGON_USER') or env('REMOTE_USER') or None)
119        
120         request.multithread = environ['wsgi.multithread']
121         request.multiprocess = environ['wsgi.multiprocess']
122         request.wsgi_environ = environ
123         response = request.run(requestLine(environ),
124                                translate_headers(environ),
125                                environ['wsgi.input'])
126         s, h, b = response.status, response.header_list, response.body
127         exc = None
128     except (KeyboardInterrupt, SystemExit), ex:
129         try:
130             if request:
131                 request.close()
132         except:
133             cherrypy.log(traceback=True)
134         request = None
135         raise ex
136     except:
137         if cherrypy.config.get("server.throw_errors", False):
138             if request:
139                 request.close()
140                 request = None
141             raise
142         tb = _cputil.formatExc()
143         cherrypy.log(tb)
144         if not cherrypy.config.get("server.show_tracebacks", False):
145             tb = ""
146         s, h, b = _cputil.bareError(tb)
147         exc = sys.exc_info()
148    
149     start_response(s, h, exc)
150     return ResponseIter(request, b)
151
152
153
154
155 # Server components.
156
157
158 class CPHTTPRequest(_cpwsgiserver.HTTPRequest):
159    
160     def __init__(self, socket, addr, server):
161         _cpwsgiserver.HTTPRequest.__init__(self, socket, addr, server)
162         mhs = int(cherrypy.config.get('server.max_request_header_size',
163                                       500 * 1024))
164         if mhs > 0:
165             self.rfile = httptools.SizeCheckWrapper(self.rfile, mhs)
166    
167     def parse_request(self):
168         try:
169             _cpwsgiserver.HTTPRequest.parse_request(self)
170         except httptools.MaxSizeExceeded:
171             msg = "Request Entity Too Large"
172             proto = self.environ.get("SERVER_PROTOCOL", "HTTP/1.0")
173             self.wfile.write("%s 413 %s\r\n" % (proto, msg))
174             self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg))
175             self.wfile.write(msg)
176             self.wfile.flush()
177             self.ready = False
178            
179             cherrypy.log(traceback=True)
180         else:
181             if self.ready:
182                 if isinstance(self.rfile, httptools.SizeCheckWrapper):
183                     # Unwrap the rfile
184                     self.rfile = self.rfile.rfile
185                 self.environ["wsgi.input"] = self.rfile
186
187
188 class WSGIServer(_cpwsgiserver.CherryPyWSGIServer):
189    
190     """Wrapper for _cpwsgiserver.CherryPyWSGIServer.
191     
192     _cpwsgiserver has been designed to not reference CherryPy in any way,
193     so that it can be used in other frameworks and applications. Therefore,
194     we wrap it here, so we can set our own mount points from cherrypy.tree.
195     
196     """
197    
198     RequestHandlerClass = CPHTTPRequest
199    
200     def __init__(self):
201         conf = cherrypy.config.get
202        
203         sockFile = cherrypy.config.get('server.socket_file')
204         if sockFile:
205             bind_addr = sockFile
206         else:
207             bind_addr = (conf("server.socket_host"), conf("server.socket_port"))
208        
209         pts = cherrypy.tree.mount_points
210         if pts:
211             apps = [(base, wsgiApp) for base in pts.keys()]
212         else:
213             apps = [("", wsgiApp)]
214        
215         s = _cpwsgiserver.CherryPyWSGIServer
216         s.__init__(self, bind_addr, apps,
217                    conf("server.thread_pool"),
218                    conf("server.socket_host"),
219                    request_queue_size = conf('server.socket_queue_size'),
220                    timeout = conf('server.socket_timeout'),
221                    )
222
223
224 #                            Server3 components                            #
225
226
227 class CPHTTPRequest3(_cpwsgiserver3.HTTPRequest):
228    
229     def parse_request(self):
230         mhs = int(cherrypy.config.get('server.max_request_header_size',
231                                       500 * 1024))
232         if mhs > 0:
233             self.rfile = httptools.SizeCheckWrapper(self.rfile, mhs)
234        
235         try:
236             _cpwsgiserver3.HTTPRequest.parse_request(self)
237         except httptools.MaxSizeExceeded:
238             self.simple_response("413 Request Entity Too Large")
239             cherrypy.log(traceback=True)
240         else:
241             if self.ready:
242                 if isinstance(self.rfile, httptools.SizeCheckWrapper):
243                     # Unwrap the rfile
244                     self.rfile = self.rfile.rfile
245                 self.environ["wsgi.input"] = self.rfile
246    
247     def decode_chunked(self):
248         """Decode the 'chunked' transfer coding."""
249         if isinstance(self.rfile, httptools.SizeCheckWrapper):
250             self.rfile = self.rfile.rfile
251         mbs = int(cherrypy.config.get('server.max_request_body_size',
252                                       100 * 1024 * 1024))
253         if mbs > 0:
254             self.rfile = httptools.SizeCheckWrapper(self.rfile, mbs)
255         try:
256             return _cpwsgiserver3.HTTPRequest.decode_chunked(self)
257         except httptools.MaxSizeExceeded:
258             self.simple_response("413 Request Entity Too Large")
259             cherrypy.log(traceback=True)
260             return False
261
262
263 class CPHTTPConnection3(_cpwsgiserver3.HTTPConnection):
264    
265     RequestHandlerClass = CPHTTPRequest3
266
267
268 class CPWSGIServer3(_cpwsgiserver3.CherryPyWSGIServer):
269     """Wrapper for _cpwsgiserver3.CherryPyWSGIServer.
270     
271     wsgiserver has been designed to not reference CherryPy in any way,
272     so that it can be used in other frameworks and applications. Therefore,
273     we wrap it here, so we can set our own mount points from cherrypy.tree.
274     """
275    
276     ConnectionClass = CPHTTPConnection3
277    
278     def __init__(self):
279         conf = cherrypy.config.get
280        
281         sockFile = cherrypy.config.get('server.socket_file')
282         if sockFile:
283             bind_addr = sockFile
284         else:
285             bind_addr = (conf("server.socket_host"), conf("server.socket_port"))
286        
287         pts = cherrypy.tree.mount_points
288         if pts:
289             apps = [(base, wsgiApp) for base in pts.keys()]
290         else:
291             apps = [("", wsgiApp)]
292        
293         s = _cpwsgiserver3.CherryPyWSGIServer
294         s.__init__(self, bind_addr, apps,
295                    conf("server.thread_pool"),
296                    conf("server.socket_host"),
297                    request_queue_size = conf('server.socket_queue_size'),
298                    timeout = conf('server.socket_timeout'),
299                    )
300        
301         self.protocol = conf("server.protocol_version")
302         self.ssl_certificate = conf("server.ssl_certificate")
303         self.ssl_private_key = conf("server.ssl_private_key")
304
305
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets