Source

CherryPy / cherrypy / _cphttptools.py

Robert Brewer 5af23a8 
Remco Boerma e0ff4c0 
Robert Brewer 5cbd559 



Robert Brewer 7ea1f3e 
Robert Brewer 22c69e1 
Robert Brewer e7063eb 
Robert Brewer 124cbd3 
Robert Brewer a81f30c 
Robert Brewer c4ac97f 

Robert Brewer 8524902 
Robert Brewer 4679833 
Robert Brewer 8524902 
Robert Brewer 4679833 
Robert Brewer a3f6155 

Robert Brewer 4679833 

John P. Speno 7ea1760 
Robert Brewer 4679833 





Remi Delon 17abb16 
Robert Brewer f2f6cad 



Robert Brewer 7826244 
Robert Brewer 4588920 
Robert Brewer 4679833 



Robert Brewer a3f6155 




Robert Brewer 84f1c7a 
Robert Brewer a3f6155 







Robert Brewer 9c954e8 
Remi Delon e67ad7b 
Robert Brewer 9c954e8 

Robert Brewer 27bf27e 

Remi Delon 6231d1a 

Robert Brewer 27bf27e 
Robert Brewer 4679833 
Robert Brewer 9c954e8 
Robert Brewer 4679833 
Robert Brewer 9c954e8 
Robert Brewer 27bf27e 






Robert Brewer 4679833 

Robert Brewer 9c954e8 
Robert Brewer 8524902 
Robert Brewer 4679833 

Remi Delon 17abb16 
Robert Brewer 953b47a 
Robert Brewer 9c954e8 
Robert Brewer d829b57 
Robert Brewer 4679833 
Robert Brewer 4588920 
Robert Brewer 4679833 



Remi Delon 458c613 
Robert Brewer 4679833 


Robert Brewer e419d80 



Remi Delon 17abb16 
Robert Brewer e419d80 


Remi Delon 17abb16 
Robert Brewer 4679833 
Remi Delon 458c613 
Robert Brewer 4679833 







Remi Delon 458c613 
Robert Brewer 4679833 

Robert Brewer 4588920 
Robert Brewer 4679833 


Robert Brewer d4787cc 

Robert Brewer 4679833 
Robert Brewer 8524902 
Robert Brewer 9c954e8 

Robert Brewer a81f30c 
Robert Brewer ab400ec 

Robert Brewer 5f72b0b 
Robert Brewer ab400ec 

Robert Brewer 5f72b0b 
Robert Brewer ab400ec 
Remi Delon 3b3278f 

Robert Brewer ab400ec 
Robert Brewer 5f72b0b 
Remi Delon 17abb16 
Robert Brewer 5f72b0b 
Remi Delon 17abb16 
Robert Brewer d829b57 
Robert Brewer c4ac97f 



Robert Brewer 794330e 





Robert Brewer c4ac97f 


Robert Brewer 4679833 

Robert Brewer a81f30c 
Remi Delon e67ad7b 
Robert Brewer a81f30c 
Robert Brewer 4679833 
Robert Brewer 794330e 

Robert Brewer 4679833 
Robert Brewer d829b57 

Robert Brewer c4ac97f 
Remi Delon 3b3278f 
Remi Delon e67ad7b 
Robert Brewer 8524902 
Remi Delon e67ad7b 

Robert Brewer 8524902 

Remi Delon e67ad7b 
Remi Delon 6231d1a 
Remi Delon e67ad7b 
Robert Brewer 8524902 
Robert Brewer 6c1a7ac 

Robert Brewer 89eeeda 
Remi Delon 6231d1a 
Robert Brewer 8524902 

Robert Brewer ff93767 
Remi Delon e67ad7b 
Robert Brewer c4ac97f 
Robert Brewer 4679833 
Robert Brewer c4ac97f 


Remi Delon e67ad7b 
Robert Brewer 80ed69b 

Remi Delon e67ad7b 
Robert Brewer 0fa554c 
Remi Delon e67ad7b 
Robert Brewer ff93767 
Remi Delon e67ad7b 


Robert Brewer ff93767 
Remi Delon 15414d5 
Robert Brewer 0fa554c 
Remi Delon 3b3278f 

Robert Brewer 0fa554c 
Remi Delon 15414d5 
Robert Brewer 0fa554c 
Remi Delon 15414d5 
Robert Brewer 8524902 
Robert Brewer 4679833 
Remi Delon e67ad7b 
Robert Brewer 8524902 

Remi Delon e67ad7b 
Robert Brewer 8524902 
Robert Brewer f688251 
Robert Brewer 9fbd994 

Remi Delon 00f620b 
Robert Brewer 4679833 



Robert Brewer e7063eb 
Remi Delon 00f620b 
Robert Brewer 4679833 
Robert Brewer 8524902 
Robert Brewer 9fbd994 

Robert Brewer 4679833 
Robert Brewer 9fbd994 
Remi Delon e67ad7b 
Robert Brewer 4679833 



Remi Delon 17abb16 
Robert Brewer 8524902 
Robert Brewer e419d80 




Remi Delon 17abb16 

Robert Brewer e419d80 





Robert Brewer 4679833 
Robert Brewer 953b47a 
Robert Brewer 4679833 
Robert Brewer 8524902 
Robert Brewer 4679833 













Robert Brewer b0753c2 

Robert Brewer 4679833 
Robert Brewer 1dee819 

Robert Brewer b0753c2 

















Remi Delon 15414d5 
Robert Brewer b0753c2 




Robert Brewer 1dee819 


Robert Brewer b0753c2 
Remi Delon 6ad9d85 

Robert Brewer 92fbf96 

Robert Brewer db6ef02 



































Robert Brewer 4679833 

Robert Brewer 09e3214 
Robert Brewer db6ef02 

Robert Brewer 4679833 

Remi Delon e67ad7b 
Robert Brewer 4679833 

Remi Delon e67ad7b 

Sylvain Hellegou… a471393 
Remi Delon e67ad7b 
Sylvain Hellegou… a471393 
Robert Brewer 4679833 
Robert Brewer a81f30c 
Robert Brewer 4679833 


Remi Delon 6231d1a 

Robert Brewer 8524902 
Robert Brewer db6ef02 




Robert Brewer 4679833 
Remi Delon e67ad7b 
Robert Brewer 4679833 
Robert Brewer a81f30c 




Robert Brewer 4679833 

Robert Brewer bab5d1e 
Robert Brewer 4679833 






Remi Delon e67ad7b 
Robert Brewer 4679833 




Remi Delon e67ad7b 
Robert Brewer db6ef02 
Remi Delon e67ad7b 
Robert Brewer 4679833 
Robert Brewer 5fcb532 

Robert Brewer 4679833 
Remi Delon 6231d1a 
Robert Brewer 4679833 
Robert Brewer 5fcb532 
Robert Brewer 4679833 
Remi Delon e67ad7b 
Robert Brewer 53b652b 
Robert Brewer 4679833 
Robert Brewer 09e3214 
Robert Brewer 4679833 


Remi Delon 458c613 
Robert Brewer 4679833 
Remi Delon e67ad7b 


Robert Brewer 4679833 


Remi Delon 458c613 
Robert Brewer 4679833 
















Remi Delon e67ad7b 
Robert Brewer 4679833 
Remi Delon e67ad7b 
Robert Brewer 4679833 




Robert Brewer 794330e 
Robert Brewer 4679833 
Remi Delon e67ad7b 
Robert Brewer a423ffd 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
"""CherryPy core request/response handling."""

import Cookie
import os
import sys
import types

import cherrypy
from cherrypy import _cputil, _cpcgifs
from cherrypy.filters import applyFilters
from cherrypy.lib import cptools, httptools


class Request(object):
    """An HTTP request."""
    
    def __init__(self, remoteAddr, remotePort, remoteHost, scheme="http"):
        """Populate a new Request object.
        
        remoteAddr should be the client IP address
        remotePort should be the client Port
        remoteHost should be string of the client's IP address.
        scheme should be a string, either "http" or "https".
        """
        self.remoteAddr = remoteAddr
        self.remotePort = remotePort
        self.remoteHost = remoteHost
        self.scheme = scheme
        self.execute_main = True
        self.closed = False
    
    def close(self):
        if not self.closed:
            self.closed = True
            applyFilters('on_end_request', failsafe=True)
    
    def run(self, requestLine, headers, rfile):
        """Process the Request.
        
        requestLine should be of the form "GET /path HTTP/1.0".
        headers should be a list of (name, value) tuples.
        rfile should be a file-like object containing the HTTP request
            entity.
        
        When run() is done, the returned object should have 3 attributes:
          status, e.g. "200 OK"
          headers, a list of (name, value) tuples
          body, an iterable yielding strings
        
        Consumer code (HTTP servers) should then access these response
        attributes to build the outbound stream.
        
        """
        self.requestLine = requestLine.strip()
        self.header_list = list(headers)
        self.rfile = rfile
        
        self.headers = httptools.HeaderMap()
        self.headerMap = self.headers # Backward compatibility
        self.simple_cookie = Cookie.SimpleCookie()
        self.simpleCookie = self.simple_cookie # Backward compatibility
        
        if cherrypy.profiler:
            cherrypy.profiler.run(self._run)
        else:
            self._run()
        
        if self.method == "HEAD":
            # HEAD requests MUST NOT return a message-body in the response.
            cherrypy.response.body = []
        
        _cputil.get_special_attribute("_cp_log_access", "_cpLogAccess")()
        
        return cherrypy.response
    
    def _run(self):
        
        try:
            # This has to be done very early in the request process,
            # because request.object_path is used for config lookups
            # right away.
            self.processRequestLine()
            
            try:
                applyFilters('on_start_resource', failsafe=True)
                
                try:
                    self.processHeaders()
                    
                    applyFilters('before_request_body')
                    if self.processRequestBody:
                        self.processBody()
                    
                    # Loop to allow for InternalRedirect.
                    while True:
                        try:
                            applyFilters('before_main')
                            if self.execute_main:
                                self.main()
                            break
                        except cherrypy.InternalRedirect, ir:
                            self.object_path = ir.path
                    
                    applyFilters('before_finalize')
                    cherrypy.response.finalize()
                except cherrypy.RequestHandled:
                    pass
                except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst:
                    # For an HTTPRedirect or HTTPError (including NotFound),
                    # we don't go through the regular mechanism:
                    # we return the redirect or error page immediately
                    inst.set_response()
                    applyFilters('before_finalize')
                    cherrypy.response.finalize()
            finally:
                applyFilters('on_end_resource', failsafe=True)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            if cherrypy.config.get("server.throw_errors", False):
                raise
            cherrypy.response.handleError(sys.exc_info())
    
    def processRequestLine(self):
        rl = self.requestLine
        method, path, qs, proto = httptools.parseRequestLine(rl)
        if path == "*":
            path = "global"
        
        self.method = method
        self.processRequestBody = method in ("POST", "PUT")
        
        self.path = path
        self.query_string = qs
        self.queryString = qs # Backward compatibility
        self.protocol = proto
        
        # Change object_path in filters to change
        # the object that will get rendered
        self.object_path = path
        
        # Compare request and server HTTP versions, in case our server does
        # not support the requested version. We can't tell the server what
        # version number to write in the response, so we limit our output
        # to min(req, server). We want the following output:
        #     request    server     actual written   supported response
        #     version    version   response version  feature set (resp.v)
        # a     1.0        1.0           1.0                1.0
        # b     1.0        1.1           1.1                1.0
        # c     1.1        1.0           1.0                1.0
        # d     1.1        1.1           1.1                1.1
        # Notice that, in (b), the response will be "HTTP/1.1" even though
        # the client only understands 1.0. RFC 2616 10.5.6 says we should
        # only return 505 if the _major_ version is different.
        
        # cherrypy.request.version == request.protocol in a Version instance.
        self.version = httptools.Version.from_http(self.protocol)
        server_v = cherrypy.config.get("server.protocol_version", "HTTP/1.0")
        server_v = httptools.Version.from_http(server_v)
        
        # cherrypy.response.version should be used to determine whether or
        # not to include a given HTTP/1.1 feature in the response content.
        cherrypy.response.version = min(self.version, server_v)
    
    def processHeaders(self):
        
        self.params = httptools.parseQueryString(self.query_string)
        self.paramMap = self.params # Backward compatibility
        
        # Process the headers into self.headers
        for name, value in self.header_list:
            value = value.strip()
            # Warning: if there is more than one header entry for cookies (AFAIK,
            # only Konqueror does that), only the last one will remain in headers
            # (but they will be correctly stored in request.simple_cookie).
            self.headers[name] = value
            
            # Handle cookies differently because on Konqueror, multiple
            # cookies come on different lines with the same key
            if name.title() == 'Cookie':
                self.simple_cookie.load(value)
        
        # Save original values (in case they get modified by filters)
        # This feature is deprecated in 2.2 and will be removed in 2.3.
        self._original_params = self.params.copy()
        
        if self.version >= "1.1":
            # All Internet-based HTTP/1.1 servers MUST respond with a 400
            # (Bad Request) status code to any HTTP/1.1 request message
            # which lacks a Host header field.
            if not self.headers.has_key("Host"):
                msg = "HTTP/1.1 requires a 'Host' request header."
                raise cherrypy.HTTPError(400, msg)
        self.base = "%s://%s" % (self.scheme, self.headers.get('Host', ''))
    
    def _get_original_params(self):
        # This feature is deprecated in 2.2 and will be removed in 2.3.
        return self._original_params
    original_params = property(_get_original_params,
                        doc="Deprecated. A copy of the original params.")
    
    def _get_browser_url(self):
        url = self.base + self.path
        if self.query_string:
            url += '?' + self.query_string
        return url
    browser_url = property(_get_browser_url,
                          doc="The URL as entered in a browser (read-only).")
    browserUrl = browser_url # Backward compatibility
    
    def processBody(self):
        # Create a copy of headers with lowercase keys because
        # FieldStorage doesn't work otherwise
        lowerHeaderMap = {}
        for key, value in self.headers.items():
            lowerHeaderMap[key.lower()] = value
        
        # FieldStorage only recognizes POST, so fake it.
        methenv = {'REQUEST_METHOD': "POST"}
        try:
            forms = _cpcgifs.FieldStorage(fp=self.rfile,
                                          headers=lowerHeaderMap,
                                          environ=methenv,
                                          keep_blank_values=1)
        except httptools.MaxSizeExceeded:
            # Post data is too big
            raise cherrypy.HTTPError(413)
        
        if forms.file:
            # request body was a content-type other than form params.
            self.body = forms.file
        else:
            self.params.update(httptools.paramsFromCGIForm(forms))
    
    def main(self, path=None):
        """Obtain and set cherrypy.response.body from a page handler."""
        if path is None:
            path = self.object_path
        
        page_handler, object_path, virtual_path = self.mapPathToObject(path)
        
        # Decode any leftover %2F in the virtual_path atoms.
        virtual_path = [x.replace("%2F", "/") for x in virtual_path]
        
        # Remove "root" from object_path and join it to get object_path
        self.object_path = '/' + '/'.join(object_path[1:])
        try:
            body = page_handler(*virtual_path, **self.params)
        except Exception, x:
            x.args = x.args + (page_handler,)
            raise
        cherrypy.response.body = body
    
    def mapPathToObject(self, objectpath):
        """For path, return the corresponding exposed callable (or raise NotFound).
        
        path should be a "relative" URL path, like "/app/a/b/c". Leading and
        trailing slashes are ignored.
        
        Traverse path:
        for /a/b?arg=val, we'll try:
          root.a.b.index -> redirect to /a/b/?arg=val
          root.a.b.default(arg='val') -> redirect to /a/b/?arg=val
          root.a.b(arg='val')
          root.a.default('b', arg='val')
          root.default('a', 'b', arg='val')
        
        The target method must have an ".exposed = True" attribute.
        """
        
        objectTrail = _cputil.get_object_trail(objectpath)
        names = [name for name, candidate in objectTrail]
        
        # Try successive objects (reverse order)
        mounted_app_roots = cherrypy.tree.mount_points.values()
        for i in xrange(len(objectTrail) - 1, -1, -1):
            
            name, candidate = objectTrail[i]
            
            # Try a "default" method on the current leaf.
            defhandler = getattr(candidate, "default", None)
            if callable(defhandler) and getattr(defhandler, 'exposed', False):
                return defhandler, names[:i+1] + ["default"], names[i+1:-1]
            
            # Uncomment the next line to restrict positional params to "default".
            # if i < len(objectTrail) - 2: continue
            
            # Try the current leaf.
            if callable(candidate) and getattr(candidate, 'exposed', False):
                if i == len(objectTrail) - 1:
                    # We found the extra ".index". Check if the original path
                    # had a trailing slash (otherwise, do a redirect).
                    if not objectpath.endswith('/'):
                        atoms = self.browser_url.split("?", 1)
                        newUrl = atoms.pop(0) + '/'
                        if atoms:
                            newUrl += "?" + atoms[0]
                        raise cherrypy.HTTPRedirect(newUrl)
                return candidate, names[:i+1], names[i+1:-1]
            
            if candidate in mounted_app_roots:
                break
        
        # We didn't find anything
        raise cherrypy.NotFound(objectpath)


class Body(object):
    """The body of the HTTP response (the response entity)."""
    
    def __get__(self, obj, objclass=None):
        if obj is None:
            # When calling on the class instead of an instance...
            return self
        else:
            return obj._body
    
    def __set__(self, obj, value):
        # Convert the given value to an iterable object.
        if isinstance(value, types.FileType):
            value = cptools.fileGenerator(value)
        elif isinstance(value, types.GeneratorType):
            value = flattener(value)
        elif isinstance(value, basestring):
            # strings get wrapped in a list because iterating over a single
            # item list is much faster than iterating over every character
            # in a long string.
            value = [value]
        elif value is None:
            value = []
        obj._body = value


def flattener(input):
    """Yield the given input, recursively iterating over each result (if needed)."""
    for x in input:
        if not isinstance(x, types.GeneratorType):
            yield x
        else:
            for y in flattener(x):
                yield y 


class Response(object):
    """An HTTP Response."""
    
    body = Body()
    
    def __init__(self):
        self.status = None
        self.header_list = None
        self.body = None
        
        self.headers = httptools.HeaderMap()
        self.headerMap = self.headers # Backward compatibility
        content_type = cherrypy.config.get('server.default_content_type', 'text/html')
        self.headers.update({
            "Content-Type": content_type,
            "Server": "CherryPy/" + cherrypy.__version__,
            "Date": httptools.HTTPDate(),
            "Set-Cookie": [],
            "Content-Length": None
        })
        self.simple_cookie = Cookie.SimpleCookie()
        self.simpleCookie = self.simple_cookie # Backward compatibility
    
    def collapse_body(self):
        newbody = ''.join([chunk for chunk in self.body])
        self.body = newbody
        return newbody
    
    def finalize(self):
        """Transform headers (and cookies) into cherrypy.response.header_list."""
        
        try:
            code, reason, _ = httptools.validStatus(self.status)
        except ValueError, x:
            raise cherrypy.HTTPError(500, x.args[0])
        
        self.status = "%s %s" % (code, reason)
        
        stream = cherrypy.config.get("stream_response", False)
        # OPTIONS requests MUST include a Content-Length of 0 if no body.
        # Just punt and figure Content-Length for all OPTIONS requests.
        if cherrypy.request.method == "OPTIONS":
            stream = False
        
        if stream:
            try:
                del self.headers['Content-Length']
            except KeyError:
                pass
        else:
            # Responses which are not streamed should have a Content-Length,
            # but allow user code to set Content-Length if desired.
            if self.headers.get('Content-Length') is None:
                content = self.collapse_body()
                self.headers['Content-Length'] = len(content)
        
        # Transform our header dict into a sorted list of tuples.
        self.header_list = self.headers.sorted_list()
        
        cookie = self.simple_cookie.output()
        if cookie:
            for line in cookie.split("\n"):
                name, value = line.split(": ", 1)
                self.header_list.append((name, value))
    
    dbltrace = "\n===First Error===\n\n%s\n\n===Second Error===\n\n%s\n\n"
    
    def handleError(self, exc):
        """Set status, headers, and body when an unanticipated error occurs."""
        try:
            applyFilters('before_error_response')
           
            # _cp_on_error will probably change self.body.
            # It may also change the headers, etc.
            _cputil.get_special_attribute('_cp_on_error', '_cpOnError')()
            
            self.finalize()
            
            applyFilters('after_error_response')
            return
        except cherrypy.HTTPRedirect, inst:
            try:
                inst.set_response()
                self.finalize()
                return
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                # Fall through to the second error handler
                pass
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            # Fall through to the second error handler
            pass
        
        # Failure in _cp_on_error, error filter, or finalize.
        # Bypass them all.
        if cherrypy.config.get('server.show_tracebacks', False):
            body = self.dbltrace % (_cputil.formatExc(exc),
                                    _cputil.formatExc())
        else:
            body = ""
        self.setBareError(body)
    
    def setBareError(self, body=None):
        self.status, self.header_list, self.body = _cputil.bareError(body)