2
from Cookie 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."""
18
__metaclass__ = cherrypy._AttributeDocstrings
22
The bare callable that this Hook object is wrapping, which will
23
be called when the Hook is called."""
27
If True, the callback is guaranteed to run even if other callbacks
28
from the same call point raise exceptions."""
32
Defines the order of execution for a list of Hooks. Priority numbers
33
should be limited to the closed interval [0, 100], but values outside
34
this range are acceptable, as are fractional values."""
38
A set of keyword arguments that will be passed to the
39
callable on each call."""
41
def __init__(self, callback, failsafe=None, priority=None, **kwargs):
42
self.callback = callback
45
failsafe = getattr(callback, "failsafe", False)
46
self.failsafe = failsafe
49
priority = getattr(callback, "priority", 50)
50
self.priority = priority
54
def __cmp__(self, other):
55
return cmp(self.priority, other.priority)
58
"""Run self.callback(**self.kwargs)."""
59
return self.callback(**self.kwargs)
63
return ("%s.%s(callback=%r, failsafe=%r, priority=%r, %s)"
64
% (cls.__module__, cls.__name__, self.callback,
65
self.failsafe, self.priority,
66
", ".join(['%s=%r' % (k, v)
67
for k, v in self.kwargs.items()])))
71
"""A map of call points to lists of callbacks (Hook objects)."""
73
def __new__(cls, points=None):
75
for p in points or []:
79
def __init__(self, *a, **kw):
82
def attach(self, point, callback, failsafe=None, priority=None, **kwargs):
83
"""Append a new Hook made from the supplied arguments."""
84
self[point].append(Hook(callback, failsafe, priority, **kwargs))
87
"""Execute all registered Hooks (callbacks) for the given point."""
92
# Some hooks are guaranteed to run even if others at
93
# the same hookpoint fail. We will still log the failure,
94
# but proceed on to the next hook. The only way
95
# to stop all processing from one of these hooks is
96
# to raise SystemExit and stop the whole server.
97
if exc is None or hook.failsafe:
100
except (KeyboardInterrupt, SystemExit):
102
except (cherrypy.HTTPError, cherrypy.HTTPRedirect,
103
cherrypy.InternalRedirect):
104
exc = sys.exc_info()[1]
106
exc = sys.exc_info()[1]
107
cherrypy.log(traceback=True, severity=40)
112
newmap = self.__class__()
113
# We can't just use 'update' because we want copies of the
114
# mutable values (each is a list) as well.
115
for k, v in self.items():
122
return "%s.%s(points=%r)" % (cls.__module__, cls.__name__, self.keys())
125
# Config namespace handlers
127
def hooks_namespace(k, v):
128
"""Attach bare hooks declared in config."""
129
# Use split again to allow multiple hooks for a single
130
# hookpoint per path (e.g. "hooks.before_handler.1").
131
# Little-known fact you only get from reading source ;)
132
hookpoint = k.split(".", 1)[0]
133
if isinstance(v, basestring):
134
v = cherrypy.lib.attributes(v)
135
if not isinstance(v, Hook):
137
cherrypy.serving.request.hooks[hookpoint].append(v)
139
def request_namespace(k, v):
140
"""Attach request attributes declared in config."""
141
# Provides config entries to set request.body attrs (like attempt_charsets).
143
setattr(cherrypy.serving.request.body, k[5:], v)
145
setattr(cherrypy.serving.request, k, v)
147
def response_namespace(k, v):
148
"""Attach response attributes declared in config."""
149
# Provides config entries to set default response headers
150
# http://cherrypy.org/ticket/889
151
if k[:8] == 'headers.':
152
cherrypy.serving.response.headers[k.split('.', 1)[1]] = v
154
setattr(cherrypy.serving.response, k, v)
156
def error_page_namespace(k, v):
157
"""Attach error pages declared in config."""
160
cherrypy.serving.request.error_page[k] = v
163
hookpoints = ['on_start_resource', 'before_request_body',
164
'before_handler', 'before_finalize',
165
'on_end_resource', 'on_end_request',
166
'before_error_response', 'after_error_response']
169
class Request(object):
172
This object represents the metadata of an HTTP request message;
173
that is, it contains attributes which describe the environment
174
in which the request URL, headers, and body were sent (if you
175
want tools to interpret the headers and body, those are elsewhere,
176
mostly in Tools). This 'metadata' consists of socket data,
177
transport characteristics, and the Request-Line. This object
178
also contains data regarding the configuration in effect for
179
the given URL, and the execution plan for generating a response.
182
__metaclass__ = cherrypy._AttributeDocstrings
186
The previous Request object (if any). This should be None
187
unless we are processing an InternalRedirect."""
189
# Conversation/connection attributes
190
local = httputil.Host("127.0.0.1", 80)
192
"An httputil.Host(ip, port, hostname) object for the server socket."
194
remote = httputil.Host("127.0.0.1", 1111)
196
"An httputil.Host(ip, port, hostname) object for the client socket."
200
The protocol used between client and server. In most cases,
201
this will be either 'http' or 'https'."""
203
server_protocol = "HTTP/1.1"
204
server_protocol__doc = """
205
The HTTP version for which the HTTP server is at least
206
conditionally compliant."""
209
base__doc = """The (scheme://host) portion of the requested URL.
210
In some cases (e.g. when proxying via mod_rewrite), this may contain
211
path segments which cherrypy.url uses when constructing url's, but
212
which otherwise are ignored by CherryPy. Regardless, this value
213
MUST NOT end in a slash."""
215
# Request-Line attributes
217
request_line__doc = """
218
The complete Request-Line received from the client. This is a
219
single string consisting of the request method, URI, and protocol
220
version (joined by spaces). Any final CRLF is removed."""
224
Indicates the HTTP method to be performed on the resource identified
225
by the Request-URI. Common methods include GET, HEAD, POST, PUT, and
226
DELETE. CherryPy allows any extension method; however, various HTTP
227
servers and gateways may restrict the set of allowable methods.
228
CherryPy applications SHOULD restrict the set (on a per-URI basis)."""
231
query_string__doc = """
232
The query component of the Request-URI, a string of information to be
233
interpreted by the resource. The query portion of a URI follows the
234
path component, and is separated by a '?'. For example, the URI
235
'http://www.cherrypy.org/wiki?a=3&b=4' has the query component,
238
query_string_encoding = 'utf8'
239
query_string_encoding__doc = """
240
The encoding expected for query string arguments after % HEX HEX decoding).
241
If a query string is provided that cannot be decoded with this encoding,
242
404 is raised (since technically it's a different URI). If you want
243
arbitrary encodings to not error, set this to 'Latin-1'; you can then
244
encode back to bytes and re-decode to whatever encoding you like later.
248
protocol__doc = """The HTTP protocol version corresponding to the set
249
of features which should be allowed in the response. If BOTH
250
the client's request message AND the server's level of HTTP
251
compliance is HTTP/1.1, this attribute will be the tuple (1, 1).
252
If either is 1.0, this attribute will be the tuple (1, 0).
253
Lower HTTP protocol versions are not explicitly supported."""
257
A dict which combines query string (GET) and request entity (POST)
258
variables. This is populated in two stages: GET params are added
259
before the 'on_start_resource' hook, and POST params are added
260
between the 'before_request_body' and 'before_handler' hooks."""
264
header_list__doc = """
265
A list of the HTTP request headers as (name, value) tuples.
266
In general, you should use request.headers (a dict) instead."""
268
headers = httputil.HeaderMap()
270
A dict-like object containing the request headers. Keys are header
271
names (in Title-Case format); however, you may get and set them in
272
a case-insensitive manner. That is, headers['Content-Type'] and
273
headers['content-type'] refer to the same value. Values are header
274
values (decoded according to RFC 2047 if necessary). See also:
275
httputil.HeaderMap, httputil.HeaderElement."""
277
cookie = SimpleCookie()
278
cookie__doc = """See help(Cookie)."""
281
body__doc = """See help(cherrypy.request.body)"""
285
If the request included an entity (body), it will be available
286
as a stream in this attribute. However, the rfile will normally
287
be read for you between the 'before_request_body' hook and the
288
'before_handler' hook, and the resulting string is placed into
289
either request.params or the request.body attribute.
291
You may disable the automatic consumption of the rfile by setting
292
request.process_request_body to False, either in config for the desired
293
path, or in an 'on_start_resource' or 'before_request_body' hook.
295
WARNING: In almost every case, you should not attempt to read from the
296
rfile stream after CherryPy's automatic mechanism has read it. If you
297
turn off the automatic parsing of rfile, you should read exactly the
298
number of bytes specified in request.headers['Content-Length'].
299
Ignoring either of these warnings may result in a hung request thread
300
or in corruption of the next (pipelined) request.
303
process_request_body = True
304
process_request_body__doc = """
305
If True, the rfile (if any) is automatically read and parsed,
306
and the result placed into request.params or request.body."""
308
methods_with_bodies = ("POST", "PUT")
309
methods_with_bodies__doc = """
310
A sequence of HTTP methods for which CherryPy will automatically
311
attempt to read a body from the rfile."""
315
If the request Content-Type is 'application/x-www-form-urlencoded'
316
or multipart, this will be None. Otherwise, this will contain the
317
request entity body as an open file object (which you can .read());
318
this value is set between the 'before_request_body' and 'before_handler'
319
hooks (assuming that process_request_body is True)."""
322
body_params__doc = """
323
If the request Content-Type is 'application/x-www-form-urlencoded' or
324
multipart, this will be a dict of the params pulled from the entity
325
body; that is, it will be the portion of request.params that come
326
from the message body (sometimes called "POST params", although they
327
can be sent with various HTTP method verbs). This value is set between
328
the 'before_request_body' and 'before_handler' hooks (assuming that
329
process_request_body is True)."""
331
# Dispatch attributes
332
dispatch = cherrypy.dispatch.Dispatcher()
334
The object which looks up the 'page handler' callable and collects
335
config for the current request based on the path_info, other
336
request attributes, and the application architecture. The core
337
calls the dispatcher as early as possible, passing it a 'path_info'
340
The default dispatcher discovers the page handler by matching path_info
341
to a hierarchical arrangement of objects, starting at request.app.root.
342
See help(cherrypy.dispatch) for more information."""
345
script_name__doc = """
346
The 'mount point' of the application which is handling this request.
348
This attribute MUST NOT end in a slash. If the script_name refers to
349
the root of the URI, it MUST be an empty string (not "/").
354
The 'relative path' portion of the Request-URI. This is relative
355
to the script_name ('mount point') of the application which is
356
handling this request."""
360
When authentication is used during the request processing this is
361
set to 'False' if it failed and to the 'username' value if it succeeded.
362
The default 'None' implies that no authentication happened."""
364
# Note that cherrypy.url uses "if request.app:" to determine whether
365
# the call is during a real HTTP request or not. So leave this None.
368
"""The cherrypy.Application object which is handling this request."""
372
The function, method, or other callable which CherryPy will call to
373
produce the response. The discovery of the handler and the arguments
374
it will receive are determined by the request.dispatch object.
375
By default, the handler is discovered by walking a tree of objects
376
starting at request.app.root, and is then passed all HTTP params
377
(from the query string and POST body) as keyword arguments."""
381
A nested dict of all Toolboxes and Tools in effect for this request,
382
of the form: {Toolbox.namespace: {Tool.name: config dict}}."""
386
A flat dict of all configuration entries which apply to the
387
current request. These entries are collected from global config,
388
application config (based on request.path_info), and from handler
389
config (exactly how is governed by the request.dispatch object in
390
effect for this request; by default, handler config can be attached
391
anywhere in the tree between request.app.root and the final handler,
392
and inherits downward)."""
396
This will be True if the current request is mapped to an 'index'
397
resource handler (also, a 'default' handler if path_info ends with
398
a slash). The value may be used to automatically redirect the
399
user-agent to a 'more canonical' URL which either adds or removes
400
the trailing slash. See cherrypy.tools.trailing_slash."""
402
hooks = HookMap(hookpoints)
404
A HookMap (dict-like object) of the form: {hookpoint: [hook, ...]}.
405
Each key is a str naming the hook point, and each value is a list
406
of hooks which will be called at that hook point during this request.
407
The list of hooks is generally populated as early as possible (mostly
408
from Tools specified in config), but may be extended at any time.
409
See also: _cprequest.Hook, _cprequest.HookMap, and cherrypy.tools."""
411
error_response = cherrypy.HTTPError(500).set_response
412
error_response__doc = """
413
The no-arg callable which will handle unexpected, untrapped errors
414
during request processing. This is not used for expected exceptions
415
(like NotFound, HTTPError, or HTTPRedirect) which are raised in
416
response to expected conditions (those should be customized either
417
via request.error_page or by overriding HTTPError.set_response).
418
By default, error_response uses HTTPError(500) to return a generic
419
error response to the user-agent."""
422
error_page__doc = """
423
A dict of {error code: response filename or callable} pairs.
425
The error code must be an int representing a given HTTP error code,
426
or the string 'default', which will be used if no matching entry
427
is found for a given numeric code.
429
If a filename is provided, the file should contain a Python string-
430
formatting template, and can expect by default to receive format
431
values with the mapping keys %(status)s, %(message)s, %(traceback)s,
432
and %(version)s. The set of format mappings can be extended by
433
overriding HTTPError.set_response.
435
If a callable is provided, it will be called by default with keyword
436
arguments 'status', 'message', 'traceback', and 'version', as for a
437
string-formatting template. The callable must return a string or iterable of
438
strings which will be set to response.body. It may also override headers or
439
perform any other processing.
441
If no entry is given for an error code, and no 'default' entry exists,
442
a default template will be used.
445
show_tracebacks = True
446
show_tracebacks__doc = """
447
If True, unexpected errors encountered during request processing will
448
include a traceback in the response body."""
450
show_mismatched_params = True
451
show_mismatched_params__doc = """
452
If True, mismatched parameters encountered during PageHandler invocation
453
processing will be included in the response body."""
455
throws = (KeyboardInterrupt, SystemExit, cherrypy.InternalRedirect)
457
"""The sequence of exceptions which Request.run does not trap."""
460
throw_errors__doc = """
461
If True, Request.run will not trap any errors (except HTTPRedirect and
462
HTTPError, which are more properly called 'exceptions', not errors)."""
466
True once the close method has been called, False otherwise."""
470
A string containing the stage reached in the request-handling process.
471
This is useful when debugging a live server with hung requests."""
473
namespaces = _cpconfig.NamespaceSet(
474
**{"hooks": hooks_namespace,
475
"request": request_namespace,
476
"response": response_namespace,
477
"error_page": error_page_namespace,
478
"tools": cherrypy.tools,
481
def __init__(self, local_host, remote_host, scheme="http",
482
server_protocol="HTTP/1.1"):
483
"""Populate a new Request object.
485
local_host should be an httputil.Host object with the server info.
486
remote_host should be an httputil.Host object with the client info.
487
scheme should be a string, either "http" or "https".
489
self.local = local_host
490
self.remote = remote_host
492
self.server_protocol = server_protocol
496
# Put a *copy* of the class error_page into self.
497
self.error_page = self.error_page.copy()
499
# Put a *copy* of the class namespaces into self.
500
self.namespaces = self.namespaces.copy()
505
"""Run cleanup code. (Core)"""
508
self.stage = 'on_end_request'
509
self.hooks.run('on_end_request')
512
def run(self, method, path, query_string, req_protocol, headers, rfile):
513
"""Process the Request. (Core)
515
method, path, query_string, and req_protocol should be pulled directly
516
from the Request-Line (e.g. "GET /path?key=val HTTP/1.0").
517
path should be %XX-unquoted, but query_string should not be.
518
They both MUST be byte strings, not unicode strings.
519
headers should be a list of (name, value) tuples.
520
rfile should be a file-like object containing the HTTP request entity.
522
When run() is done, the returned object should have 3 attributes:
523
status, e.g. "200 OK"
524
header_list, a list of (name, value) tuples
525
body, an iterable yielding strings
527
Consumer code (HTTP servers) should then access these response
528
attributes to build the outbound stream.
531
response = cherrypy.serving.response
534
self.error_response = cherrypy.HTTPError(500).set_response
538
self.query_string = query_string or ''
541
# Compare request and server HTTP protocol versions, in case our
542
# server does not support the requested protocol. Limit our output
543
# to min(req, server). We want the following output:
544
# request server actual written supported response
545
# protocol protocol response protocol feature set
550
# Notice that, in (b), the response will be "HTTP/1.1" even though
551
# the client only understands 1.0. RFC 2616 10.5.6 says we should
552
# only return 505 if the _major_ version is different.
553
rp = int(req_protocol[5]), int(req_protocol[7])
554
sp = int(self.server_protocol[5]), int(self.server_protocol[7])
555
self.protocol = min(rp, sp)
556
response.headers.protocol = self.protocol
558
# Rebuild first line of the request (e.g. "GET /path HTTP/1.0").
561
url += '?' + query_string
562
self.request_line = '%s %s %s' % (method, url, req_protocol)
564
self.header_list = list(headers)
565
self.headers = httputil.HeaderMap()
570
self.cookie = SimpleCookie()
573
# path_info should be the path from the
574
# app root (script_name) to the handler.
575
self.script_name = self.app.script_name
576
self.path_info = pi = path[len(self.script_name):]
578
self.stage = 'respond'
584
if self.throw_errors:
587
# Failure in setup, error handler or finalize. Bypass them.
588
# Can't use handle_error because we may not have hooks yet.
589
cherrypy.log(traceback=True, severity=40)
590
if self.show_tracebacks:
595
response.output_status, response.header_list, response.body = r
597
if self.method == "HEAD":
598
# HEAD requests MUST NOT return a message-body in the response.
602
cherrypy.log.access()
604
cherrypy.log.error(traceback=True)
606
if response.timed_out:
607
raise cherrypy.TimeoutError()
611
# Uncomment for stage debugging
612
# stage = property(lambda self: self._stage, lambda self, v: print(v))
614
def respond(self, path_info):
615
"""Generate a response for the resource at self.path_info. (Core)"""
616
response = cherrypy.serving.response
621
raise cherrypy.NotFound()
623
# Get the 'Host' header, so we can HTTPRedirect properly.
624
self.stage = 'process_headers'
625
self.process_headers()
627
# Make a copy of the class hooks
628
self.hooks = self.__class__.hooks.copy()
631
self.stage = 'get_resource'
632
self.get_resource(path_info)
634
self.body = _cpreqbody.RequestBody(
635
self.rfile, self.headers, request_params=self.params)
637
self.namespaces(self.config)
639
self.stage = 'on_start_resource'
640
self.hooks.run('on_start_resource')
642
# Parse the querystring
643
self.stage = 'process_query_string'
644
self.process_query_string()
647
if self.process_request_body:
648
if self.method not in self.methods_with_bodies:
649
self.process_request_body = False
650
self.stage = 'before_request_body'
651
self.hooks.run('before_request_body')
652
if self.process_request_body:
656
self.stage = 'before_handler'
657
self.hooks.run('before_handler')
659
self.stage = 'handler'
660
response.body = self.handler()
663
self.stage = 'before_finalize'
664
self.hooks.run('before_finalize')
666
except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst:
668
self.stage = 'before_finalize (HTTPError)'
669
self.hooks.run('before_finalize')
672
self.stage = 'on_end_resource'
673
self.hooks.run('on_end_resource')
677
if self.throw_errors:
681
def process_query_string(self):
682
"""Parse the query string into Python structures. (Core)"""
684
p = httputil.parse_query_string(
685
self.query_string, encoding=self.query_string_encoding)
686
except UnicodeDecodeError:
687
raise cherrypy.HTTPError(
688
404, "The given query string could not be processed. Query "
689
"strings for this resource must be encoded with %r." %
690
self.query_string_encoding)
692
# Python 2 only: keyword arguments must be byte strings (type 'str').
693
for key, value in p.items():
694
if isinstance(key, unicode):
696
p[key.encode(self.query_string_encoding)] = value
697
self.params.update(p)
699
def process_headers(self):
700
"""Parse HTTP header data into Python structures. (Core)"""
701
# Process the headers into self.headers
702
headers = self.headers
703
for name, value in self.header_list:
704
# Call title() now (and use dict.__method__(headers))
705
# so title doesn't have to be called twice.
707
value = value.strip()
709
# Warning: if there is more than one header entry for cookies (AFAIK,
710
# only Konqueror does that), only the last one will remain in headers
711
# (but they will be correctly stored in request.cookie).
713
dict.__setitem__(headers, name, httputil.decode_TEXT(value))
715
dict.__setitem__(headers, name, value)
717
# Handle cookies differently because on Konqueror, multiple
718
# cookies come on different lines with the same key
721
self.cookie.load(value)
723
msg = "Illegal cookie name %s" % value.split('=')[0]
724
raise cherrypy.HTTPError(400, msg)
726
if not dict.__contains__(headers, 'Host'):
727
# All Internet-based HTTP/1.1 servers MUST respond with a 400
728
# (Bad Request) status code to any HTTP/1.1 request message
729
# which lacks a Host header field.
730
if self.protocol >= (1, 1):
731
msg = "HTTP/1.1 requires a 'Host' request header."
732
raise cherrypy.HTTPError(400, msg)
733
host = dict.get(headers, 'Host')
735
host = self.local.name or self.local.ip
736
self.base = "%s://%s" % (self.scheme, host)
738
def get_resource(self, path):
739
"""Call a dispatcher (which sets self.handler and .config). (Core)"""
740
# First, see if there is a custom dispatch at this URI. Custom
741
# dispatchers can only be specified in app.config, not in _cp_config
742
# (since custom dispatchers may not even have an app.root).
743
dispatch = self.app.find_config(path, "request.dispatch", self.dispatch)
745
# dispatch() should set self.handler and self.config
748
def handle_error(self):
749
"""Handle the last unanticipated exception. (Core)"""
751
self.hooks.run("before_error_response")
752
if self.error_response:
753
self.error_response()
754
self.hooks.run("after_error_response")
755
cherrypy.serving.response.finalize()
756
except cherrypy.HTTPRedirect, inst:
758
cherrypy.serving.response.finalize()
760
# ------------------------- Properties ------------------------- #
762
def _get_body_params(self):
764
"body_params is deprecated in CherryPy 3.2, will be removed in "
768
return self.body.params
769
body_params = property(_get_body_params,
771
If the request Content-Type is 'application/x-www-form-urlencoded' or
772
multipart, this will be a dict of the params pulled from the entity
773
body; that is, it will be the portion of request.params that come
774
from the message body (sometimes called "POST params", although they
775
can be sent with various HTTP method verbs). This value is set between
776
the 'before_request_body' and 'before_handler' hooks (assuming that
777
process_request_body is True).
779
Deprecated in 3.2, will be removed for 3.3""")
782
class ResponseBody(object):
783
"""The body of the HTTP response (the response entity)."""
785
def __get__(self, obj, objclass=None):
787
# When calling on the class instead of an instance...
792
def __set__(self, obj, value):
793
# Convert the given value to an iterable object.
794
if isinstance(value, basestring):
795
# strings get wrapped in a list because iterating over a single
796
# item list is much faster than iterating over every character
801
# [''] doesn't evaluate to False, so replace it with [].
803
elif isinstance(value, types.FileType):
804
value = file_generator(value)
810
class Response(object):
811
"""An HTTP Response, including status, headers, and body.
813
Application developers should use Response.headers (a dict) to
814
set or modify HTTP response headers. When the response is finalized,
815
Response.headers is transformed into Response.header_list as
819
__metaclass__ = cherrypy._AttributeDocstrings
821
# Class attributes for dev-time introspection.
823
status__doc = """The HTTP Status-Code and Reason-Phrase."""
826
header_list__doc = """
827
A list of the HTTP response headers as (name, value) tuples.
828
In general, you should use response.headers (a dict) instead."""
830
headers = httputil.HeaderMap()
832
A dict-like object containing the response headers. Keys are header
833
names (in Title-Case format); however, you may get and set them in
834
a case-insensitive manner. That is, headers['Content-Type'] and
835
headers['content-type'] refer to the same value. Values are header
836
values (decoded according to RFC 2047 if necessary). See also:
837
httputil.HeaderMap, httputil.HeaderElement."""
839
cookie = SimpleCookie()
840
cookie__doc = """See help(Cookie)."""
842
body = ResponseBody()
843
body__doc = """The body (entity) of the HTTP response."""
846
time__doc = """The value of time.time() when created. Use in HTTP dates."""
849
timeout__doc = """Seconds after which the response will be aborted."""
853
Flag to indicate the response should be aborted, because it has
854
exceeded its timeout."""
857
stream__doc = """If False, buffer the response body."""
861
self.header_list = None
863
self.time = time.time()
865
self.headers = httputil.HeaderMap()
866
# Since we know all our keys are titled strings, we can
867
# bypass HeaderMap.update and get a big speed boost.
868
dict.update(self.headers, {
869
"Content-Type": 'text/html',
870
"Server": "CherryPy/" + cherrypy.__version__,
871
"Date": httputil.HTTPDate(self.time),
873
self.cookie = SimpleCookie()
875
def collapse_body(self):
876
"""Collapse self.body to a single string; replace it and return it."""
877
if isinstance(self.body, basestring):
880
newbody = ''.join([chunk for chunk in self.body])
885
"""Transform headers (and cookies) into self.header_list. (Core)"""
887
code, reason, _ = httputil.valid_status(self.status)
888
except ValueError, x:
889
raise cherrypy.HTTPError(500, x.args[0])
891
headers = self.headers
893
self.output_status = str(code) + " " + headers.encode(reason)
896
# The upshot: wsgiserver will chunk the response if
897
# you pop Content-Length (or set it explicitly to None).
898
# Note that lib.static sets C-L to the file's st_size.
899
if dict.get(headers, 'Content-Length') is None:
900
dict.pop(headers, 'Content-Length', None)
901
elif code < 200 or code in (204, 205, 304):
902
# "All 1xx (informational), 204 (no content),
903
# and 304 (not modified) responses MUST NOT
904
# include a message-body."
905
dict.pop(headers, 'Content-Length', None)
908
# Responses which are not streamed should have a Content-Length,
909
# but allow user code to set Content-Length if desired.
910
if dict.get(headers, 'Content-Length') is None:
911
content = self.collapse_body()
912
dict.__setitem__(headers, 'Content-Length', len(content))
914
# Transform our header dict into a list of tuples.
915
self.header_list = h = headers.output()
917
cookie = self.cookie.output()
919
for line in cookie.split("\n"):
920
if line.endswith("\r"):
921
# Python 2.4 emits cookies joined by LF but 2.5+ by CRLF.
923
name, value = line.split(": ", 1)
924
if isinstance(name, unicode):
925
name = name.encode("ISO-8859-1")
926
if isinstance(value, unicode):
927
value = headers.encode(value)
928
h.append((name, value))
930
def check_timeout(self):
931
"""If now > self.time + self.timeout, set self.timed_out.
933
This purposefully sets a flag, rather than raising an error,
934
so that a monitor thread can interrupt the Response thread.
936
if time.time() > self.time + self.timeout:
937
self.timed_out = True