8 from cherrypy
._cpcompat
import basestring
, copykeys
, ntob
, unicodestr
9 from cherrypy
._cpcompat
import SimpleCookie
, CookieError
10 from cherrypy
import _cpreqbody
, _cpconfig
11 from cherrypy
._cperror
import format_exc
, bare_error
12 from cherrypy
.lib
import httputil
, file_generator
16 """A callback and its metadata: failsafe, priority, and kwargs."""
20 The bare callable that this Hook object is wrapping, which will
21 be called when the Hook is called."""
25 If True, the callback is guaranteed to run even if other callbacks
26 from the same call point raise exceptions."""
30 Defines the order of execution for a list of Hooks. Priority numbers
31 should be limited to the closed interval [0, 100], but values outside
32 this range are acceptable, as are fractional values."""
36 A set of keyword arguments that will be passed to the
37 callable on each call."""
39 def __init__(self
, callback
, failsafe
=None, priority
=None, **kwargs
):
40 self
.callback
= callback
43 failsafe
= getattr(callback
, "failsafe", False)
44 self
.failsafe
= failsafe
47 priority
= getattr(callback
, "priority", 50)
48 self
.priority
= priority
52 def __cmp__(self
, other
):
53 return cmp(self
.priority
, other
.priority
)
56 """Run self.callback(**self.kwargs)."""
57 return self
.callback(**self
.kwargs
)
61 return ("%s.%s(callback=%r, failsafe=%r, priority=%r, %s)"
62 % (cls
.__module
__, cls
.__name
__, self
.callback
,
63 self
.failsafe
, self
.priority
,
64 ", ".join(['%s=%r' % (k
, v
)
65 for k
, v
in self
.kwargs
.items()])))
69 """A map of call points to lists of callbacks (Hook objects)."""
71 def __new__(cls
, points
=None):
73 for p
in points
or []:
77 def __init__(self
, *a
, **kw
):
80 def attach(self
, point
, callback
, failsafe
=None, priority
=None, **kwargs
):
81 """Append a new Hook made from the supplied arguments."""
82 self
[point
].append(Hook(callback
, failsafe
, priority
, **kwargs
))
85 """Execute all registered Hooks (callbacks) for the given point."""
90 # Some hooks are guaranteed to run even if others at
91 # the same hookpoint fail. We will still log the failure,
92 # but proceed on to the next hook. The only way
93 # to stop all processing from one of these hooks is
94 # to raise SystemExit and stop the whole server.
95 if exc
is None or hook
.failsafe
:
98 except (KeyboardInterrupt, SystemExit):
100 except (cherrypy
.HTTPError
, cherrypy
.HTTPRedirect
,
101 cherrypy
.InternalRedirect
):
102 exc
= sys
.exc_info()[1]
104 exc
= sys
.exc_info()[1]
105 cherrypy
.log(traceback
=True, severity
=40)
110 newmap
= self
.__class
__()
111 # We can't just use 'update' because we want copies of the
112 # mutable values (each is a list) as well.
113 for k
, v
in self
.items():
120 return "%s.%s(points=%r)" % (cls
.__module
__, cls
.__name
__, copykeys(self
))
123 # Config namespace handlers
125 def hooks_namespace(k
, v
):
126 """Attach bare hooks declared in config."""
127 # Use split again to allow multiple hooks for a single
128 # hookpoint per path (e.g. "hooks.before_handler.1").
129 # Little-known fact you only get from reading source ;)
130 hookpoint
= k
.split(".", 1)[0]
131 if isinstance(v
, basestring
):
132 v
= cherrypy
.lib
.attributes(v
)
133 if not isinstance(v
, Hook
):
135 cherrypy
.serving
.request
.hooks
[hookpoint
].append(v
)
137 def request_namespace(k
, v
):
138 """Attach request attributes declared in config."""
139 # Provides config entries to set request.body attrs (like attempt_charsets).
141 setattr(cherrypy
.serving
.request
.body
, k
[5:], v
)
143 setattr(cherrypy
.serving
.request
, k
, v
)
145 def response_namespace(k
, v
):
146 """Attach response attributes declared in config."""
147 # Provides config entries to set default response headers
148 # http://cherrypy.org/ticket/889
149 if k
[:8] == 'headers.':
150 cherrypy
.serving
.response
.headers
[k
.split('.', 1)[1]] = v
152 setattr(cherrypy
.serving
.response
, k
, v
)
154 def error_page_namespace(k
, v
):
155 """Attach error pages declared in config."""
158 cherrypy
.serving
.request
.error_page
[k
] = v
161 hookpoints
= ['on_start_resource', 'before_request_body',
162 'before_handler', 'before_finalize',
163 'on_end_resource', 'on_end_request',
164 'before_error_response', 'after_error_response']
167 class Request(object):
170 This object represents the metadata of an HTTP request message;
171 that is, it contains attributes which describe the environment
172 in which the request URL, headers, and body were sent (if you
173 want tools to interpret the headers and body, those are elsewhere,
174 mostly in Tools). This 'metadata' consists of socket data,
175 transport characteristics, and the Request-Line. This object
176 also contains data regarding the configuration in effect for
177 the given URL, and the execution plan for generating a response.
182 The previous Request object (if any). This should be None
183 unless we are processing an InternalRedirect."""
185 # Conversation/connection attributes
186 local
= httputil
.Host("127.0.0.1", 80)
187 "An httputil.Host(ip, port, hostname) object for the server socket."
189 remote
= httputil
.Host("127.0.0.1", 1111)
190 "An httputil.Host(ip, port, hostname) object for the client socket."
194 The protocol used between client and server. In most cases,
195 this will be either 'http' or 'https'."""
197 server_protocol
= "HTTP/1.1"
199 The HTTP version for which the HTTP server is at least
200 conditionally compliant."""
203 """The (scheme://host) portion of the requested URL.
204 In some cases (e.g. when proxying via mod_rewrite), this may contain
205 path segments which cherrypy.url uses when constructing url's, but
206 which otherwise are ignored by CherryPy. Regardless, this value
207 MUST NOT end in a slash."""
209 # Request-Line attributes
212 The complete Request-Line received from the client. This is a
213 single string consisting of the request method, URI, and protocol
214 version (joined by spaces). Any final CRLF is removed."""
218 Indicates the HTTP method to be performed on the resource identified
219 by the Request-URI. Common methods include GET, HEAD, POST, PUT, and
220 DELETE. CherryPy allows any extension method; however, various HTTP
221 servers and gateways may restrict the set of allowable methods.
222 CherryPy applications SHOULD restrict the set (on a per-URI basis)."""
226 The query component of the Request-URI, a string of information to be
227 interpreted by the resource. The query portion of a URI follows the
228 path component, and is separated by a '?'. For example, the URI
229 'http://www.cherrypy.org/wiki?a=3&b=4' has the query component,
232 query_string_encoding
= 'utf8'
234 The encoding expected for query string arguments after % HEX HEX decoding).
235 If a query string is provided that cannot be decoded with this encoding,
236 404 is raised (since technically it's a different URI). If you want
237 arbitrary encodings to not error, set this to 'Latin-1'; you can then
238 encode back to bytes and re-decode to whatever encoding you like later.
242 """The HTTP protocol version corresponding to the set
243 of features which should be allowed in the response. If BOTH
244 the client's request message AND the server's level of HTTP
245 compliance is HTTP/1.1, this attribute will be the tuple (1, 1).
246 If either is 1.0, this attribute will be the tuple (1, 0).
247 Lower HTTP protocol versions are not explicitly supported."""
251 A dict which combines query string (GET) and request entity (POST)
252 variables. This is populated in two stages: GET params are added
253 before the 'on_start_resource' hook, and POST params are added
254 between the 'before_request_body' and 'before_handler' hooks."""
259 A list of the HTTP request headers as (name, value) tuples.
260 In general, you should use request.headers (a dict) instead."""
262 headers
= httputil
.HeaderMap()
264 A dict-like object containing the request headers. Keys are header
265 names (in Title-Case format); however, you may get and set them in
266 a case-insensitive manner. That is, headers['Content-Type'] and
267 headers['content-type'] refer to the same value. Values are header
268 values (decoded according to :rfc:`2047` if necessary). See also:
269 httputil.HeaderMap, httputil.HeaderElement."""
271 cookie
= SimpleCookie()
272 """See help(Cookie)."""
276 If the request included an entity (body), it will be available
277 as a stream in this attribute. However, the rfile will normally
278 be read for you between the 'before_request_body' hook and the
279 'before_handler' hook, and the resulting string is placed into
280 either request.params or the request.body attribute.
282 You may disable the automatic consumption of the rfile by setting
283 request.process_request_body to False, either in config for the desired
284 path, or in an 'on_start_resource' or 'before_request_body' hook.
286 WARNING: In almost every case, you should not attempt to read from the
287 rfile stream after CherryPy's automatic mechanism has read it. If you
288 turn off the automatic parsing of rfile, you should read exactly the
289 number of bytes specified in request.headers['Content-Length'].
290 Ignoring either of these warnings may result in a hung request thread
291 or in corruption of the next (pipelined) request.
294 process_request_body
= True
296 If True, the rfile (if any) is automatically read and parsed,
297 and the result placed into request.params or request.body."""
299 methods_with_bodies
= ("POST", "PUT")
301 A sequence of HTTP methods for which CherryPy will automatically
302 attempt to read a body from the rfile."""
306 If the request Content-Type is 'application/x-www-form-urlencoded'
307 or multipart, this will be None. Otherwise, this will be an instance
308 of :class:`RequestBody<cherrypy._cpreqbody.RequestBody>` (which you
309 can .read()); this value is set between the 'before_request_body' and
310 'before_handler' hooks (assuming that process_request_body is True)."""
312 # Dispatch attributes
313 dispatch
= cherrypy
.dispatch
.Dispatcher()
315 The object which looks up the 'page handler' callable and collects
316 config for the current request based on the path_info, other
317 request attributes, and the application architecture. The core
318 calls the dispatcher as early as possible, passing it a 'path_info'
321 The default dispatcher discovers the page handler by matching path_info
322 to a hierarchical arrangement of objects, starting at request.app.root.
323 See help(cherrypy.dispatch) for more information."""
327 The 'mount point' of the application which is handling this request.
329 This attribute MUST NOT end in a slash. If the script_name refers to
330 the root of the URI, it MUST be an empty string (not "/").
335 The 'relative path' portion of the Request-URI. This is relative
336 to the script_name ('mount point') of the application which is
337 handling this request."""
341 When authentication is used during the request processing this is
342 set to 'False' if it failed and to the 'username' value if it succeeded.
343 The default 'None' implies that no authentication happened."""
345 # Note that cherrypy.url uses "if request.app:" to determine whether
346 # the call is during a real HTTP request or not. So leave this None.
348 """The cherrypy.Application object which is handling this request."""
352 The function, method, or other callable which CherryPy will call to
353 produce the response. The discovery of the handler and the arguments
354 it will receive are determined by the request.dispatch object.
355 By default, the handler is discovered by walking a tree of objects
356 starting at request.app.root, and is then passed all HTTP params
357 (from the query string and POST body) as keyword arguments."""
361 A nested dict of all Toolboxes and Tools in effect for this request,
362 of the form: {Toolbox.namespace: {Tool.name: config dict}}."""
366 A flat dict of all configuration entries which apply to the
367 current request. These entries are collected from global config,
368 application config (based on request.path_info), and from handler
369 config (exactly how is governed by the request.dispatch object in
370 effect for this request; by default, handler config can be attached
371 anywhere in the tree between request.app.root and the final handler,
372 and inherits downward)."""
376 This will be True if the current request is mapped to an 'index'
377 resource handler (also, a 'default' handler if path_info ends with
378 a slash). The value may be used to automatically redirect the
379 user-agent to a 'more canonical' URL which either adds or removes
380 the trailing slash. See cherrypy.tools.trailing_slash."""
382 hooks
= HookMap(hookpoints
)
384 A HookMap (dict-like object) of the form: {hookpoint: [hook, ...]}.
385 Each key is a str naming the hook point, and each value is a list
386 of hooks which will be called at that hook point during this request.
387 The list of hooks is generally populated as early as possible (mostly
388 from Tools specified in config), but may be extended at any time.
389 See also: _cprequest.Hook, _cprequest.HookMap, and cherrypy.tools."""
391 error_response
= cherrypy
.HTTPError(500).set_response
393 The no-arg callable which will handle unexpected, untrapped errors
394 during request processing. This is not used for expected exceptions
395 (like NotFound, HTTPError, or HTTPRedirect) which are raised in
396 response to expected conditions (those should be customized either
397 via request.error_page or by overriding HTTPError.set_response).
398 By default, error_response uses HTTPError(500) to return a generic
399 error response to the user-agent."""
403 A dict of {error code: response filename or callable} pairs.
405 The error code must be an int representing a given HTTP error code,
406 or the string 'default', which will be used if no matching entry
407 is found for a given numeric code.
409 If a filename is provided, the file should contain a Python string-
410 formatting template, and can expect by default to receive format
411 values with the mapping keys %(status)s, %(message)s, %(traceback)s,
412 and %(version)s. The set of format mappings can be extended by
413 overriding HTTPError.set_response.
415 If a callable is provided, it will be called by default with keyword
416 arguments 'status', 'message', 'traceback', and 'version', as for a
417 string-formatting template. The callable must return a string or iterable of
418 strings which will be set to response.body. It may also override headers or
419 perform any other processing.
421 If no entry is given for an error code, and no 'default' entry exists,
422 a default template will be used.
425 show_tracebacks
= True
427 If True, unexpected errors encountered during request processing will
428 include a traceback in the response body."""
430 show_mismatched_params
= True
432 If True, mismatched parameters encountered during PageHandler invocation
433 processing will be included in the response body."""
435 throws
= (KeyboardInterrupt, SystemExit, cherrypy
.InternalRedirect
)
436 """The sequence of exceptions which Request.run does not trap."""
440 If True, Request.run will not trap any errors (except HTTPRedirect and
441 HTTPError, which are more properly called 'exceptions', not errors)."""
444 """True once the close method has been called, False otherwise."""
448 A string containing the stage reached in the request-handling process.
449 This is useful when debugging a live server with hung requests."""
451 namespaces
= _cpconfig
.NamespaceSet(
452 **{"hooks": hooks_namespace
,
453 "request": request_namespace
,
454 "response": response_namespace
,
455 "error_page": error_page_namespace
,
456 "tools": cherrypy
.tools
,
459 def __init__(self
, local_host
, remote_host
, scheme
="http",
460 server_protocol
="HTTP/1.1"):
461 """Populate a new Request object.
463 local_host should be an httputil.Host object with the server info.
464 remote_host should be an httputil.Host object with the client info.
465 scheme should be a string, either "http" or "https".
467 self
.local
= local_host
468 self
.remote
= remote_host
470 self
.server_protocol
= server_protocol
474 # Put a *copy* of the class error_page into self.
475 self
.error_page
= self
.error_page
.copy()
477 # Put a *copy* of the class namespaces into self.
478 self
.namespaces
= self
.namespaces
.copy()
483 """Run cleanup code. (Core)"""
486 self
.stage
= 'on_end_request'
487 self
.hooks
.run('on_end_request')
490 def run(self
, method
, path
, query_string
, req_protocol
, headers
, rfile
):
491 """Process the Request. (Core)
493 method, path, query_string, and req_protocol should be pulled directly
494 from the Request-Line (e.g. "GET /path?key=val HTTP/1.0").
497 This should be %XX-unquoted, but query_string should not be.
498 They both MUST be byte strings, not unicode strings.
501 A list of (name, value) tuples.
504 A file-like object containing the HTTP request entity.
506 When run() is done, the returned object should have 3 attributes:
508 * status, e.g. "200 OK"
509 * header_list, a list of (name, value) tuples
510 * body, an iterable yielding strings
512 Consumer code (HTTP servers) should then access these response
513 attributes to build the outbound stream.
516 response
= cherrypy
.serving
.response
519 self
.error_response
= cherrypy
.HTTPError(500).set_response
523 self
.query_string
= query_string
or ''
526 # Compare request and server HTTP protocol versions, in case our
527 # server does not support the requested protocol. Limit our output
528 # to min(req, server). We want the following output:
529 # request server actual written supported response
530 # protocol protocol response protocol feature set
535 # Notice that, in (b), the response will be "HTTP/1.1" even though
536 # the client only understands 1.0. RFC 2616 10.5.6 says we should
537 # only return 505 if the _major_ version is different.
538 rp
= int(req_protocol
[5]), int(req_protocol
[7])
539 sp
= int(self
.server_protocol
[5]), int(self
.server_protocol
[7])
540 self
.protocol
= min(rp
, sp
)
541 response
.headers
.protocol
= self
.protocol
543 # Rebuild first line of the request (e.g. "GET /path HTTP/1.0").
546 url
+= '?' + query_string
547 self
.request_line
= '%s %s %s' % (method
, url
, req_protocol
)
549 self
.header_list
= list(headers
)
550 self
.headers
= httputil
.HeaderMap()
555 self
.cookie
= SimpleCookie()
558 # path_info should be the path from the
559 # app root (script_name) to the handler.
560 self
.script_name
= self
.app
.script_name
561 self
.path_info
= pi
= path
[len(self
.script_name
):]
563 self
.stage
= 'respond'
569 if self
.throw_errors
:
572 # Failure in setup, error handler or finalize. Bypass them.
573 # Can't use handle_error because we may not have hooks yet.
574 cherrypy
.log(traceback
=True, severity
=40)
575 if self
.show_tracebacks
:
580 response
.output_status
, response
.header_list
, response
.body
= r
582 if self
.method
== "HEAD":
583 # HEAD requests MUST NOT return a message-body in the response.
587 cherrypy
.log
.access()
589 cherrypy
.log
.error(traceback
=True)
591 if response
.timed_out
:
592 raise cherrypy
.TimeoutError()
596 # Uncomment for stage debugging
597 # stage = property(lambda self: self._stage, lambda self, v: print(v))
599 def respond(self
, path_info
):
600 """Generate a response for the resource at self.path_info. (Core)"""
601 response
= cherrypy
.serving
.response
606 raise cherrypy
.NotFound()
608 # Get the 'Host' header, so we can HTTPRedirect properly.
609 self
.stage
= 'process_headers'
610 self
.process_headers()
612 # Make a copy of the class hooks
613 self
.hooks
= self
.__class
__.hooks
.copy()
616 self
.stage
= 'get_resource'
617 self
.get_resource(path_info
)
619 self
.body
= _cpreqbody
.RequestBody(
620 self
.rfile
, self
.headers
, request_params
=self
.params
)
622 self
.namespaces(self
.config
)
624 self
.stage
= 'on_start_resource'
625 self
.hooks
.run('on_start_resource')
627 # Parse the querystring
628 self
.stage
= 'process_query_string'
629 self
.process_query_string()
632 if self
.process_request_body
:
633 if self
.method
not in self
.methods_with_bodies
:
634 self
.process_request_body
= False
635 self
.stage
= 'before_request_body'
636 self
.hooks
.run('before_request_body')
637 if self
.process_request_body
:
641 self
.stage
= 'before_handler'
642 self
.hooks
.run('before_handler')
644 self
.stage
= 'handler'
645 response
.body
= self
.handler()
648 self
.stage
= 'before_finalize'
649 self
.hooks
.run('before_finalize')
651 except (cherrypy
.HTTPRedirect
, cherrypy
.HTTPError
):
652 inst
= sys
.exc_info()[1]
654 self
.stage
= 'before_finalize (HTTPError)'
655 self
.hooks
.run('before_finalize')
658 self
.stage
= 'on_end_resource'
659 self
.hooks
.run('on_end_resource')
663 if self
.throw_errors
:
667 def process_query_string(self
):
668 """Parse the query string into Python structures. (Core)"""
670 p
= httputil
.parse_query_string(
671 self
.query_string
, encoding
=self
.query_string_encoding
)
672 except UnicodeDecodeError:
673 raise cherrypy
.HTTPError(
674 404, "The given query string could not be processed. Query "
675 "strings for this resource must be encoded with %r." %
676 self
.query_string_encoding
)
678 # Python 2 only: keyword arguments must be byte strings (type 'str').
679 for key
, value
in p
.items():
680 if isinstance(key
, unicode):
682 p
[key
.encode(self
.query_string_encoding
)] = value
683 self
.params
.update(p
)
685 def process_headers(self
):
686 """Parse HTTP header data into Python structures. (Core)"""
687 # Process the headers into self.headers
688 headers
= self
.headers
689 for name
, value
in self
.header_list
:
690 # Call title() now (and use dict.__method__(headers))
691 # so title doesn't have to be called twice.
693 value
= value
.strip()
695 # Warning: if there is more than one header entry for cookies (AFAIK,
696 # only Konqueror does that), only the last one will remain in headers
697 # (but they will be correctly stored in request.cookie).
699 dict.__setitem
__(headers
, name
, httputil
.decode_TEXT(value
))
701 dict.__setitem
__(headers
, name
, value
)
703 # Handle cookies differently because on Konqueror, multiple
704 # cookies come on different lines with the same key
707 self
.cookie
.load(value
)
709 msg
= "Illegal cookie name %s" % value
.split('=')[0]
710 raise cherrypy
.HTTPError(400, msg
)
712 if not dict.__contains
__(headers
, 'Host'):
713 # All Internet-based HTTP/1.1 servers MUST respond with a 400
714 # (Bad Request) status code to any HTTP/1.1 request message
715 # which lacks a Host header field.
716 if self
.protocol
>= (1, 1):
717 msg
= "HTTP/1.1 requires a 'Host' request header."
718 raise cherrypy
.HTTPError(400, msg
)
719 host
= dict.get(headers
, 'Host')
721 host
= self
.local
.name
or self
.local
.ip
722 self
.base
= "%s://%s" % (self
.scheme
, host
)
724 def get_resource(self
, path
):
725 """Call a dispatcher (which sets self.handler and .config). (Core)"""
726 # First, see if there is a custom dispatch at this URI. Custom
727 # dispatchers can only be specified in app.config, not in _cp_config
728 # (since custom dispatchers may not even have an app.root).
729 dispatch
= self
.app
.find_config(path
, "request.dispatch", self
.dispatch
)
731 # dispatch() should set self.handler and self.config
734 def handle_error(self
):
735 """Handle the last unanticipated exception. (Core)"""
737 self
.hooks
.run("before_error_response")
738 if self
.error_response
:
739 self
.error_response()
740 self
.hooks
.run("after_error_response")
741 cherrypy
.serving
.response
.finalize()
742 except cherrypy
.HTTPRedirect
:
743 inst
= sys
.exc_info()[1]
745 cherrypy
.serving
.response
.finalize()
747 # ------------------------- Properties ------------------------- #
749 def _get_body_params(self
):
751 "body_params is deprecated in CherryPy 3.2, will be removed in "
755 return self
.body
.params
756 body_params
= property(_get_body_params
,
758 If the request Content-Type is 'application/x-www-form-urlencoded' or
759 multipart, this will be a dict of the params pulled from the entity
760 body; that is, it will be the portion of request.params that come
761 from the message body (sometimes called "POST params", although they
762 can be sent with various HTTP method verbs). This value is set between
763 the 'before_request_body' and 'before_handler' hooks (assuming that
764 process_request_body is True).
766 Deprecated in 3.2, will be removed for 3.3 in favor of
767 :attr:`request.body.params<cherrypy._cprequest.RequestBody.params>`.""")
770 class ResponseBody(object):
771 """The body of the HTTP response (the response entity)."""
773 def __get__(self
, obj
, objclass
=None):
775 # When calling on the class instead of an instance...
780 def __set__(self
, obj
, value
):
781 # Convert the given value to an iterable object.
782 if isinstance(value
, basestring
):
783 # strings get wrapped in a list because iterating over a single
784 # item list is much faster than iterating over every character
789 # [''] doesn't evaluate to False, so replace it with [].
791 # Don't use isinstance here; io.IOBase which has an ABC takes
792 # 1000 times as long as, say, isinstance(value, str)
793 elif hasattr(value
, 'read'):
794 value
= file_generator(value
)
800 class Response(object):
801 """An HTTP Response, including status, headers, and body."""
804 """The HTTP Status-Code and Reason-Phrase."""
808 A list of the HTTP response headers as (name, value) tuples.
809 In general, you should use response.headers (a dict) instead. This
810 attribute is generated from response.headers and is not valid until
811 after the finalize phase."""
813 headers
= httputil
.HeaderMap()
815 A dict-like object containing the response headers. Keys are header
816 names (in Title-Case format); however, you may get and set them in
817 a case-insensitive manner. That is, headers['Content-Type'] and
818 headers['content-type'] refer to the same value. Values are header
819 values (decoded according to :rfc:`2047` if necessary).
821 .. seealso:: classes :class:`HeaderMap`, :class:`HeaderElement`
824 cookie
= SimpleCookie()
825 """See help(Cookie)."""
827 body
= ResponseBody()
828 """The body (entity) of the HTTP response."""
831 """The value of time.time() when created. Use in HTTP dates."""
834 """Seconds after which the response will be aborted."""
838 Flag to indicate the response should be aborted, because it has
839 exceeded its timeout."""
842 """If False, buffer the response body."""
846 self
.header_list
= None
848 self
.time
= time
.time()
850 self
.headers
= httputil
.HeaderMap()
851 # Since we know all our keys are titled strings, we can
852 # bypass HeaderMap.update and get a big speed boost.
853 dict.update(self
.headers
, {
854 "Content-Type": 'text/html',
855 "Server": "CherryPy/" + cherrypy
.__version
__,
856 "Date": httputil
.HTTPDate(self
.time
),
858 self
.cookie
= SimpleCookie()
860 def collapse_body(self
):
861 """Collapse self.body to a single string; replace it and return it."""
862 if isinstance(self
.body
, basestring
):
865 newbody
= ''.join([chunk
for chunk
in self
.body
])
871 """Transform headers (and cookies) into self.header_list. (Core)"""
873 code
, reason
, _
= httputil
.valid_status(self
.status
)
875 raise cherrypy
.HTTPError(500, sys
.exc_info()[1].args
[0])
877 headers
= self
.headers
879 self
.output_status
= ntob(str(code
), 'ascii') + ntob(" ") + headers
.encode(reason
)
882 # The upshot: wsgiserver will chunk the response if
883 # you pop Content-Length (or set it explicitly to None).
884 # Note that lib.static sets C-L to the file's st_size.
885 if dict.get(headers
, 'Content-Length') is None:
886 dict.pop(headers
, 'Content-Length', None)
887 elif code
< 200 or code
in (204, 205, 304):
888 # "All 1xx (informational), 204 (no content),
889 # and 304 (not modified) responses MUST NOT
890 # include a message-body."
891 dict.pop(headers
, 'Content-Length', None)
894 # Responses which are not streamed should have a Content-Length,
895 # but allow user code to set Content-Length if desired.
896 if dict.get(headers
, 'Content-Length') is None:
897 content
= self
.collapse_body()
898 dict.__setitem
__(headers
, 'Content-Length', len(content
))
900 # Transform our header dict into a list of tuples.
901 self
.header_list
= h
= headers
.output()
903 cookie
= self
.cookie
.output()
905 for line
in cookie
.split("\n"):
906 if line
.endswith("\r"):
907 # Python 2.4 emits cookies joined by LF but 2.5+ by CRLF.
909 name
, value
= line
.split(": ", 1)
910 if isinstance(name
, unicodestr
):
911 name
= name
.encode("ISO-8859-1")
912 if isinstance(value
, unicodestr
):
913 value
= headers
.encode(value
)
914 h
.append((name
, value
))
916 def check_timeout(self
):
917 """If now > self.time + self.timeout, set self.timed_out.
919 This purposefully sets a flag, rather than raising an error,
920 so that a monitor thread can interrupt the Response thread.
922 if time
.time() > self
.time
+ self
.timeout
:
923 self
.timed_out
= True