diff options
Diffstat (limited to 'cherrypy')
-rw-r--r-- | cherrypy/_cpdispatch.py | 32 | ||||
-rw-r--r-- | cherrypy/_cperror.py | 19 | ||||
-rw-r--r-- | cherrypy/_cplogging.py | 14 | ||||
-rw-r--r-- | cherrypy/_cpreqbody.py | 2 | ||||
-rw-r--r-- | cherrypy/_cprequest.py | 31 | ||||
-rw-r--r-- | cherrypy/_cptools.py | 36 | ||||
-rw-r--r-- | cherrypy/_cptree.py | 7 | ||||
-rw-r--r-- | cherrypy/lib/auth.py | 19 | ||||
-rw-r--r-- | cherrypy/lib/auth_basic.py | 13 | ||||
-rw-r--r-- | cherrypy/lib/auth_digest.py | 27 | ||||
-rw-r--r-- | cherrypy/lib/caching.py | 21 | ||||
-rw-r--r-- | cherrypy/lib/cptools.py | 81 | ||||
-rw-r--r-- | cherrypy/lib/encoding.py | 29 | ||||
-rw-r--r-- | cherrypy/lib/jsontools.py | 6 | ||||
-rw-r--r-- | cherrypy/lib/sessions.py | 26 | ||||
-rw-r--r-- | cherrypy/lib/static.py | 28 | ||||
-rw-r--r-- | cherrypy/test/test.py | 4 | ||||
-rw-r--r-- | cherrypy/wsgiserver/__init__.py | 8 |
18 files changed, 217 insertions, 186 deletions
diff --git a/cherrypy/_cpdispatch.py b/cherrypy/_cpdispatch.py index 92d5e211..af5773cb 100644 --- a/cherrypy/_cpdispatch.py +++ b/cherrypy/_cpdispatch.py @@ -52,7 +52,8 @@ def test_callable_spec(callable, callable_args, callable_kwargs): incorrect, then a 404 Not Found should be raised. Conversely the body parameters are part of the request; if they are invalid a 400 Bad Request. """ - show_mismatched_params = getattr(cherrypy.request, 'show_mismatched_params', False) + show_mismatched_params = getattr( + cherrypy.serving.request, 'show_mismatched_params', False) try: (args, varargs, varkw, defaults) = inspect.getargspec(callable) except TypeError: @@ -119,7 +120,7 @@ def test_callable_spec(callable, callable_args, callable_kwargs): if not varargs and vararg_usage > 0: raise cherrypy.HTTPError(404) - body_params = cherrypy.request.body.params or {} + body_params = cherrypy.serving.request.body.params or {} body_params = set(body_params.keys()) qs_params = set(callable_kwargs.keys()) - body_params @@ -176,7 +177,7 @@ class LateParamPageHandler(PageHandler): """ def _get_kwargs(self): - kwargs = cherrypy.request.params.copy() + kwargs = cherrypy.serving.request.params.copy() if self._kwargs: kwargs.update(self._kwargs) return kwargs @@ -216,7 +217,7 @@ class Dispatcher(object): def __call__(self, path_info): """Set handler and config for the current request.""" - request = cherrypy.request + request = cherrypy.serving.request func, vpath = self.find_handler(path_info) if func: @@ -245,7 +246,7 @@ class Dispatcher(object): These virtual path components are passed to the handler as positional arguments. """ - request = cherrypy.request + request = cherrypy.serving.request app = request.app root = app.root dispatch_name = self.dispatch_method_name @@ -356,7 +357,7 @@ class MethodDispatcher(Dispatcher): def __call__(self, path_info): """Set handler and config for the current request.""" - request = cherrypy.request + request = cherrypy.serving.request resource, vpath = self.find_handler(path_info) if resource: @@ -365,7 +366,7 @@ class MethodDispatcher(Dispatcher): if "GET" in avail and "HEAD" not in avail: avail.append("HEAD") avail.sort() - cherrypy.response.headers['Allow'] = ", ".join(avail) + cherrypy.serving.response.headers['Allow'] = ", ".join(avail) # Find the subhandler meth = request.method.upper() @@ -414,20 +415,20 @@ class RoutesDispatcher(object): """Set handler and config for the current request.""" func = self.find_handler(path_info) if func: - cherrypy.request.handler = LateParamPageHandler(func) + cherrypy.serving.request.handler = LateParamPageHandler(func) else: - cherrypy.request.handler = cherrypy.NotFound() + cherrypy.serving.request.handler = cherrypy.NotFound() def find_handler(self, path_info): """Find the right page handler, and set request.config.""" import routes - request = cherrypy.request + request = cherrypy.serving.request config = routes.request_config() config.mapper = self.mapper - if hasattr(cherrypy.request, 'wsgi_environ'): - config.environ = cherrypy.request.wsgi_environ + if hasattr(request, 'wsgi_environ'): + config.environ = request.wsgi_environ config.host = request.headers.get('Host', None) config.protocol = request.scheme config.redirect = self.redirect @@ -541,7 +542,8 @@ def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True, **domai """ from cherrypy.lib import httputil def vhost_dispatch(path_info): - header = cherrypy.request.headers.get + request = cherrypy.serving.request + header = request.headers.get domain = header('Host', '') if use_x_forwarded_host: @@ -554,10 +556,10 @@ def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True, **domai result = next_dispatcher(path_info) # Touch up staticdir config. See http://www.cherrypy.org/ticket/614. - section = cherrypy.request.config.get('tools.staticdir.section') + section = request.config.get('tools.staticdir.section') if section: section = section[len(prefix):] - cherrypy.request.config['tools.staticdir.section'] = section + request.config['tools.staticdir.section'] = section return result return vhost_dispatch diff --git a/cherrypy/_cperror.py b/cherrypy/_cperror.py index 6e554876..106c9dd2 100644 --- a/cherrypy/_cperror.py +++ b/cherrypy/_cperror.py @@ -29,7 +29,7 @@ class InternalRedirect(CherryPyException): def __init__(self, path, query_string=""): import cherrypy - request = cherrypy.request + request = cherrypy.serving.request self.query_string = query_string if "?" in path: @@ -60,7 +60,7 @@ class HTTPRedirect(CherryPyException): def __init__(self, urls, status=None): import cherrypy - request = cherrypy.request + request = cherrypy.serving.request if isinstance(urls, basestring): urls = [urls] @@ -99,7 +99,7 @@ class HTTPRedirect(CherryPyException): HTTPRedirect object and set its output without *raising* the exception. """ import cherrypy - response = cherrypy.response + response = cherrypy.serving.response response.status = status = self.status if status in (300, 301, 302, 303, 307): @@ -158,7 +158,7 @@ def clean_headers(status): """Remove any headers which should not apply to an error response.""" import cherrypy - response = cherrypy.response + response = cherrypy.serving.response # Remove headers which applied to the original content, # but do not apply to the error page. @@ -211,7 +211,7 @@ class HTTPError(CherryPyException): """ import cherrypy - response = cherrypy.response + response = cherrypy.serving.response clean_headers(self.code) @@ -219,7 +219,7 @@ class HTTPError(CherryPyException): # so don't bother cleaning up response values here. response.status = self.status tb = None - if cherrypy.request.show_tracebacks: + if cherrypy.serving.request.show_tracebacks: tb = format_exc() response.headers['Content-Type'] = "text/html;charset=utf-8" response.headers.pop('Content-Length', None) @@ -244,7 +244,8 @@ class NotFound(HTTPError): def __init__(self, path=None): if path is None: import cherrypy - path = cherrypy.request.script_name + cherrypy.request.path_info + request = cherrypy.serving.request + path = request.script_name + request.path_info self.args = (path,) HTTPError.__init__(self, 404, "The path %r was not found." % path) @@ -309,7 +310,7 @@ def get_error_page(status, **kwargs): kwargs[k] = _escape(kwargs[k]) # Use a custom template or callable for the error page? - pages = cherrypy.request.error_page + pages = cherrypy.serving.request.error_page error_page = pages.get(code) or pages.get('default') if error_page: try: @@ -337,7 +338,7 @@ _ie_friendly_error_sizes = { def _be_ie_unfriendly(status): import cherrypy - response = cherrypy.response + response = cherrypy.serving.response # For some statuses, Internet Explorer 5+ shows "friendly error # messages" instead of our response.body if the body is smaller diff --git a/cherrypy/_cplogging.py b/cherrypy/_cplogging.py index 4ac69b1f..554a5294 100644 --- a/cherrypy/_cplogging.py +++ b/cherrypy/_cplogging.py @@ -72,9 +72,9 @@ class LogManager(object): escaped by prepending a backslash, and all whitespace characters, which are written in their C-style notation (\\n, \\t, etc). """ - request = cherrypy.request + request = cherrypy.serving.request remote = request.remote - response = cherrypy.response + response = cherrypy.serving.response outheaders = response.headers inheaders = request.headers if response.output_status is None: @@ -88,9 +88,9 @@ class LogManager(object): 't': self.time(), 'r': request.request_line, 's': status, - 'b': outheaders.get('Content-Length', '') or "-", - 'f': inheaders.get('Referer', ''), - 'a': inheaders.get('User-Agent', ''), + 'b': dict.get(outheaders, 'Content-Length', '') or "-", + 'f': dict.get(inheaders, 'Referer', ''), + 'a': dict.get(inheaders, 'User-Agent', ''), } for k, v in atoms.items(): if isinstance(v, unicode): @@ -220,7 +220,7 @@ class WSGIErrorHandler(logging.Handler): def flush(self): """Flushes the stream.""" try: - stream = cherrypy.request.wsgi_environ.get('wsgi.errors') + stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors') except (AttributeError, KeyError): pass else: @@ -229,7 +229,7 @@ class WSGIErrorHandler(logging.Handler): def emit(self, record): """Emit a record.""" try: - stream = cherrypy.request.wsgi_environ.get('wsgi.errors') + stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors') except (AttributeError, KeyError): pass else: diff --git a/cherrypy/_cpreqbody.py b/cherrypy/_cpreqbody.py index 2af43c82..a7135eef 100644 --- a/cherrypy/_cpreqbody.py +++ b/cherrypy/_cpreqbody.py @@ -588,7 +588,7 @@ class RequestBody(Entity): # It is possible to send a POST request with no body, for example; # however, app developers are responsible in that case to set # cherrypy.request.process_body to False so this method isn't called. - h = cherrypy.request.headers + h = cherrypy.serving.request.headers if u'Content-Length' not in h and u'Transfer-Encoding' not in h: raise cherrypy.HTTPError(411) diff --git a/cherrypy/_cprequest.py b/cherrypy/_cprequest.py index 40b89561..0c8e3d7c 100644 --- a/cherrypy/_cprequest.py +++ b/cherrypy/_cprequest.py @@ -134,26 +134,26 @@ def hooks_namespace(k, v): v = cherrypy.lib.attributes(v) if not isinstance(v, Hook): v = Hook(v) - cherrypy.request.hooks[hookpoint].append(v) + cherrypy.serving.request.hooks[hookpoint].append(v) def request_namespace(k, v): """Attach request attributes declared in config.""" - setattr(cherrypy.request, k, v) + setattr(cherrypy.serving.request, k, v) def response_namespace(k, v): """Attach response attributes declared in config.""" # Provides config entries to set default response headers # http://cherrypy.org/ticket/889 if k[:8] == 'headers.': - cherrypy.response.headers[k.split('.', 1)[1]] = v + cherrypy.serving.response.headers[k.split('.', 1)[1]] = v else: - setattr(cherrypy.response, k, v) + setattr(cherrypy.serving.response, k, v) def error_page_namespace(k, v): """Attach error pages declared in config.""" if k != 'default': k = int(k) - cherrypy.request.error_page[k] = v + cherrypy.serving.request.error_page[k] = v hookpoints = ['on_start_resource', 'before_request_body', @@ -524,6 +524,7 @@ class Request(object): attributes to build the outbound stream. """ + response = cherrypy.serving.response self.stage = 'run' try: self.error_response = cherrypy.HTTPError(500).set_response @@ -548,7 +549,7 @@ class Request(object): rp = int(req_protocol[5]), int(req_protocol[7]) sp = int(self.server_protocol[5]), int(self.server_protocol[7]) self.protocol = min(rp, sp) - cherrypy.response.headers.protocol = self.protocol + response.headers.protocol = self.protocol # Rebuild first line of the request (e.g. "GET /path HTTP/1.0"). url = path @@ -587,28 +588,28 @@ class Request(object): else: body = "" r = bare_error(body) - response = cherrypy.response response.output_status, response.header_list, response.body = r if self.method == "HEAD": # HEAD requests MUST NOT return a message-body in the response. - cherrypy.response.body = [] + response.body = [] try: cherrypy.log.access() except: cherrypy.log.error(traceback=True) - if cherrypy.response.timed_out: + if response.timed_out: raise cherrypy.TimeoutError() - return cherrypy.response + return response # Uncomment for stage debugging # stage = property(lambda self: self._stage, lambda self, v: print(v)) def respond(self, path_info): """Generate a response for the resource at self.path_info. (Core)""" + response = cherrypy.serving.response try: try: try: @@ -649,17 +650,17 @@ class Request(object): self.hooks.run('before_handler') if self.handler: self.stage = 'handler' - cherrypy.response.body = self.handler() + response.body = self.handler() # Finalize self.stage = 'before_finalize' self.hooks.run('before_finalize') - cherrypy.response.finalize() + response.finalize() except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst: inst.set_response() self.stage = 'before_finalize (HTTPError)' self.hooks.run('before_finalize') - cherrypy.response.finalize() + response.finalize() finally: self.stage = 'on_end_resource' self.hooks.run('on_end_resource') @@ -760,10 +761,10 @@ class Request(object): if self.error_response: self.error_response() self.hooks.run("after_error_response") - cherrypy.response.finalize() + cherrypy.serving.response.finalize() except cherrypy.HTTPRedirect, inst: inst.set_response() - cherrypy.response.finalize() + cherrypy.serving.response.finalize() # ------------------------- Properties ------------------------- # diff --git a/cherrypy/_cptools.py b/cherrypy/_cptools.py index 5c98430a..81dca463 100644 --- a/cherrypy/_cptools.py +++ b/cherrypy/_cptools.py @@ -86,7 +86,7 @@ class Tool(object): else: conf = {} - tm = cherrypy.request.toolmaps[self.namespace] + tm = cherrypy.serving.request.toolmaps[self.namespace] if self._name in tm: conf.update(tm[self._name]) @@ -129,8 +129,8 @@ class Tool(object): p = conf.pop("priority", None) if p is None: p = getattr(self.callable, "priority", self._priority) - cherrypy.request.hooks.attach(self._point, self.callable, - priority=p, **conf) + cherrypy.serving.request.hooks.attach(self._point, self.callable, + priority=p, **conf) class HandlerTool(Tool): @@ -159,13 +159,13 @@ class HandlerTool(Tool): handled = self.callable(*args, **self._merged_args(kwargs)) if not handled: raise cherrypy.NotFound() - return cherrypy.response.body + return cherrypy.serving.response.body handle_func.exposed = True return handle_func def _wrapper(self, **kwargs): if self.callable(**kwargs): - cherrypy.request.handler = None + cherrypy.serving.request.handler = None def _setup(self): """Hook this tool into cherrypy.request. @@ -177,8 +177,8 @@ class HandlerTool(Tool): p = conf.pop("priority", None) if p is None: p = getattr(self.callable, "priority", self._priority) - cherrypy.request.hooks.attach(self._point, self._wrapper, - priority=p, **conf) + cherrypy.serving.request.hooks.attach(self._point, self._wrapper, + priority=p, **conf) class HandlerWrapperTool(Tool): @@ -206,10 +206,10 @@ class HandlerWrapperTool(Tool): self._priority = priority def callable(self): - innerfunc = cherrypy.request.handler + innerfunc = cherrypy.serving.request.handler def wrap(*args, **kwargs): return self.newhandler(innerfunc, *args, **kwargs) - cherrypy.request.handler = wrap + cherrypy.serving.request.handler = wrap class ErrorTool(Tool): @@ -227,7 +227,7 @@ class ErrorTool(Tool): The standard CherryPy request object will automatically call this method when the tool is "turned on" in config. """ - cherrypy.request.error_response = self._wrapper + cherrypy.serving.request.error_response = self._wrapper # Builtin tools # @@ -266,7 +266,7 @@ class SessionTool(Tool): The standard CherryPy request object will automatically call this method when the tool is "turned on" in config. """ - hooks = cherrypy.request.hooks + hooks = cherrypy.serving.request.hooks conf = self._merged_args() @@ -358,11 +358,11 @@ class XMLRPCController(object): # cherrypy.lib.xmlrpc.on_error raise Exception('method "%s" is not supported' % attr) - conf = cherrypy.request.toolmaps['tools'].get("xmlrpc", {}) + conf = cherrypy.serving.request.toolmaps['tools'].get("xmlrpc", {}) _xmlrpc.respond(body, conf.get('encoding', 'utf-8'), conf.get('allow_none', 0)) - return cherrypy.response.body + return cherrypy.serving.response.body default.exposed = True @@ -378,7 +378,7 @@ class CachingTool(Tool): """Caching Tool for CherryPy.""" def _wrapper(self, invalid_methods=("POST", "PUT", "DELETE"), **kwargs): - request = cherrypy.request + request = cherrypy.serving.request if not hasattr(cherrypy, "_cache"): # Make a process-wide Cache object. @@ -402,8 +402,8 @@ class CachingTool(Tool): conf = self._merged_args() p = conf.pop("priority", None) - cherrypy.request.hooks.attach('before_handler', self._wrapper, - priority=p, **conf) + cherrypy.serving.request.hooks.attach('before_handler', self._wrapper, + priority=p, **conf) @@ -427,7 +427,7 @@ class Toolbox(object): def __enter__(self): """Populate request.toolmaps from tools specified in config.""" - cherrypy.request.toolmaps[self.namespace] = map = {} + cherrypy.serving.request.toolmaps[self.namespace] = map = {} def populate(k, v): toolname, arg = k.split(".", 1) bucket = map.setdefault(toolname, {}) @@ -436,7 +436,7 @@ class Toolbox(object): def __exit__(self, exc_type, exc_val, exc_tb): """Run tool._setup() for each tool in our toolmap.""" - map = cherrypy.request.toolmaps.get(self.namespace) + map = cherrypy.serving.request.toolmaps.get(self.namespace) if map: for name, settings in map.items(): if settings.get("on", False): diff --git a/cherrypy/_cptree.py b/cherrypy/_cptree.py index 1c4f0b7a..1096248b 100644 --- a/cherrypy/_cptree.py +++ b/cherrypy/_cptree.py @@ -82,7 +82,7 @@ class Application(object): def _get_script_name(self): if self._script_name is None: # None signals that the script name should be pulled from WSGI environ. - return cherrypy.request.wsgi_environ['SCRIPT_NAME'].rstrip("/") + return cherrypy.serving.request.wsgi_environ['SCRIPT_NAME'].rstrip("/") return self._script_name def _set_script_name(self, value): if value: @@ -211,8 +211,9 @@ class Tree(object): """ if path is None: try: - path = httputil.urljoin(cherrypy.request.script_name, - cherrypy.request.path_info) + request = cherrypy.serving.request + path = httputil.urljoin(request.script_name, + request.path_info) except AttributeError: return None diff --git a/cherrypy/lib/auth.py b/cherrypy/lib/auth.py index 1ef87f45..887e716d 100644 --- a/cherrypy/lib/auth.py +++ b/cherrypy/lib/auth.py @@ -4,9 +4,10 @@ from cherrypy.lib import httpauth def check_auth(users, encrypt=None, realm=None): """If an authorization header contains credentials, return True, else False.""" - if 'authorization' in cherrypy.request.headers: + request = cherrypy.serving.request + if 'authorization' in request.headers: # make sure the provided credentials are correctly set - ah = httpauth.parseAuthorization(cherrypy.request.headers['authorization']) + ah = httpauth.parseAuthorization(request.headers['authorization']) if ah is None: raise cherrypy.HTTPError(400, 'Bad Request') @@ -17,7 +18,7 @@ def check_auth(users, encrypt=None, realm=None): try: # backward compatibility users = users() # expect it to return a dictionary - + if not isinstance(users, dict): raise ValueError("Authentication users must be a dictionary") @@ -35,12 +36,12 @@ def check_auth(users, encrypt=None, realm=None): # validate the authorization by re-computing it here # and compare it with what the user-agent provided - if httpauth.checkResponse(ah, password, method=cherrypy.request.method, + if httpauth.checkResponse(ah, password, method=request.method, encrypt=encrypt, realm=realm): - cherrypy.request.login = ah["username"] + request.login = ah["username"] return True - - cherrypy.request.login = False + + request.login = False return False def basic_auth(realm, users, encrypt=None): @@ -55,7 +56,7 @@ def basic_auth(realm, users, encrypt=None): return # inform the user-agent this path is protected - cherrypy.response.headers['www-authenticate'] = httpauth.basicAuth(realm) + cherrypy.serving.response.headers['www-authenticate'] = httpauth.basicAuth(realm) raise cherrypy.HTTPError(401, "You are not authorized to access that resource") @@ -69,7 +70,7 @@ def digest_auth(realm, users): return # inform the user-agent this path is protected - cherrypy.response.headers['www-authenticate'] = httpauth.digestAuth(realm) + cherrypy.serving.response.headers['www-authenticate'] = httpauth.digestAuth(realm) raise cherrypy.HTTPError(401, "You are not authorized to access that resource") diff --git a/cherrypy/lib/auth_basic.py b/cherrypy/lib/auth_basic.py index 581470cc..4cde7833 100644 --- a/cherrypy/lib/auth_basic.py +++ b/cherrypy/lib/auth_basic.py @@ -59,11 +59,12 @@ def basic_auth(realm, checkpassword): 'authorization' header. If authentication succeeds, checkpassword returns True, else it returns False. """ - + if '"' in realm: raise ValueError('Realm cannot contain the " (quote) character.') - - auth_header = cherrypy.request.headers.get('authorization') + request = cherrypy.serving.request + + auth_header = request.headers.get('authorization') if auth_header is not None: try: scheme, params = auth_header.split(' ', 1) @@ -73,12 +74,12 @@ def basic_auth(realm, checkpassword): username_password = base64.decodestring(params) username, password = username_password.split(':', 1) if checkpassword(realm, username, password): - cherrypy.request.login = username + request.login = username return # successful authentication except (ValueError, binascii.Error): # split() error, base64.decodestring() error raise cherrypy.HTTPError(400, 'Bad Request') - + # Respond with 401 status and a WWW-Authenticate header - cherrypy.response.headers['www-authenticate'] = 'Basic realm="%s"' % realm + cherrypy.serving.response.headers['www-authenticate'] = 'Basic realm="%s"' % realm raise cherrypy.HTTPError(401, "You are not authorized to access that resource") diff --git a/cherrypy/lib/auth_digest.py b/cherrypy/lib/auth_digest.py index 0b629f27..011abcce 100644 --- a/cherrypy/lib/auth_digest.py +++ b/cherrypy/lib/auth_digest.py @@ -298,16 +298,16 @@ def www_authenticate(realm, key, algorithm='MD5', nonce=None, qop=qop_auth, stal def digest_auth(realm, get_ha1, key, debug=False): """digest_auth is a CherryPy tool which hooks at before_handler to perform HTTP Digest Access Authentication, as specified in RFC 2617. - + If the request has an 'authorization' header with a 'Digest' scheme, this tool authenticates the credentials supplied in that header. If the request has no 'authorization' header, or if it does but the scheme is not "Digest", or if authentication fails, the tool sends a 401 response with a 'WWW-Authenticate' Digest header. - + Arguments: realm: a string containing the authentication realm. - + get_ha1: a callable which looks up a username in a credentials store and returns the HA1 string, which is defined in the RFC to be MD5(username : realm : password). The function's signature is: @@ -315,27 +315,28 @@ def digest_auth(realm, get_ha1, key, debug=False): where username is obtained from the request's 'authorization' header. If username is not found in the credentials store, get_ha1() returns None. - + key: a secret string known only to the server, used in the synthesis of nonces. """ - - auth_header = cherrypy.request.headers.get('authorization') + request = cherrypy.serving.request + + auth_header = request.headers.get('authorization') nonce_is_stale = False if auth_header is not None: try: - auth = HttpDigestAuthorization(auth_header, cherrypy.request.method, debug=debug) + auth = HttpDigestAuthorization(auth_header, request.method, debug=debug) except ValueError, e: raise cherrypy.HTTPError(400, 'Bad Request: %s' % e) - + if debug: TRACE(str(auth)) - + if auth.validate_nonce(realm, key): ha1 = get_ha1(realm, auth.username) if ha1 is not None: # note that for request.body to be available we need to hook in at # before_handler, not on_start_resource like 3.1.x digest_auth does. - digest = auth.request_digest(ha1, entity_body=cherrypy.request.body) + digest = auth.request_digest(ha1, entity_body=request.body) if digest == auth.response: # authenticated if debug: TRACE("digest matches auth.response") @@ -343,15 +344,15 @@ def digest_auth(realm, get_ha1, key, debug=False): # The choice of ten minutes' lifetime for nonce is somewhat arbitrary nonce_is_stale = auth.is_nonce_stale(max_age_seconds=600) if not nonce_is_stale: - cherrypy.request.login = auth.username + request.login = auth.username if debug: TRACE("authentication of %s successful" % auth.username) return - + # Respond with 401 status and a WWW-Authenticate header header = www_authenticate(realm, key, stale=nonce_is_stale) if debug: TRACE(header) - cherrypy.response.headers['WWW-Authenticate'] = header + cherrypy.serving.response.headers['WWW-Authenticate'] = header raise cherrypy.HTTPError(401, "You are not authorized to access that resource") diff --git a/cherrypy/lib/caching.py b/cherrypy/lib/caching.py index 4e395560..e97decb6 100644 --- a/cherrypy/lib/caching.py +++ b/cherrypy/lib/caching.py @@ -123,9 +123,9 @@ class MemoryCache: self.cursize = 0 def key(self): - request = cherrypy.request + request = cherrypy.serving.request try: - response = cherrypy.response + response = cherrypy.serving.response except AttributeError: response = None return self.store.get_key_from_request(request, response) @@ -169,7 +169,7 @@ class MemoryCache: # checks if there's space for the object if (obj_size < self.maxobj_size and total_size < self.maxsize): # add to the expirations list and cache - expiration_time = cherrypy.response.time + self.delay + expiration_time = cherrypy.serving.response.time + self.delay obj_key = self.key() bucket = self.expirations.setdefault(expiration_time, []) bucket.append((obj_size, obj_key)) @@ -204,7 +204,8 @@ def get(invalid_methods=("POST", "PUT", "DELETE"), **kwargs): * sets request.cacheable = True * returns False """ - request = cherrypy.request + request = cherrypy.serving.request + response = cherrypy.serving.response # POST, PUT, DELETE should invalidate (delete) the cached copy. # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10. @@ -218,7 +219,6 @@ def get(invalid_methods=("POST", "PUT", "DELETE"), **kwargs): request.cached = c = bool(cache_data) request.cacheable = not c if c: - response = cherrypy.response s, h, b, create_time, original_req_headers = cache_data # Copy the response headers. See http://www.cherrypy.org/ticket/721. @@ -257,15 +257,14 @@ def tee_output(): if response.headers.get('Pragma', None) != 'no-cache': # save the cache data body = ''.join(output) - vary = [he.value for he in - cherrypy.response.headers.elements('Vary')] + vary = [he.value for he in response.headers.elements('Vary')] sel_headers = dict([(k, v) for k, v - in cherrypy.request.headers.items() + in cherrypy.serving.request.headers.items() if k in vary]) cherrypy._cache.put((response.status, response.headers or {}, body, response.time, sel_headers)) - response = cherrypy.response + response = cherrypy.serving.response response.body = tee(response.body) @@ -286,7 +285,7 @@ def expires(secs=0, force=False): none of the above response headers are set. """ - response = cherrypy.response + response = cherrypy.serving.response headers = response.headers cacheable = False @@ -304,7 +303,7 @@ def expires(secs=0, force=False): if secs == 0: if force or "Pragma" not in headers: headers["Pragma"] = "no-cache" - if cherrypy.request.protocol >= (1, 1): + if cherrypy.serving.request.protocol >= (1, 1): if force or "Cache-Control" not in headers: headers["Cache-Control"] = "no-cache, must-revalidate" # Set an explicit Expires date in the past. diff --git a/cherrypy/lib/cptools.py b/cherrypy/lib/cptools.py index cfa607b0..12594a4c 100644 --- a/cherrypy/lib/cptools.py +++ b/cherrypy/lib/cptools.py @@ -35,7 +35,7 @@ def validate_etags(autotags=False): will be incorrect, and your application will break. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24 """ - response = cherrypy.response + response = cherrypy.serving.response # Guard against being run twice. if hasattr(response, "ETag"): @@ -58,7 +58,7 @@ def validate_etags(autotags=False): # anything other than a 2xx or 412 status, then the If-Match header # MUST be ignored." if status >= 200 and status <= 299: - request = cherrypy.request + request = cherrypy.serving.request conditions = request.headers.elements('If-Match') or [] conditions = [str(x) for x in conditions] @@ -81,12 +81,12 @@ def validate_since(): If no code has set the Last-Modified response header, then no validation will be performed. """ - response = cherrypy.response + response = cherrypy.serving.response lastmod = response.headers.get('Last-Modified') if lastmod: status, reason, msg = _httputil.valid_status(response.status) - request = cherrypy.request + request = cherrypy.serving.request since = request.headers.get('If-Unmodified-Since') if since and since != lastmod: @@ -122,7 +122,7 @@ def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For', want to rewrite remote.ip, set the 'remote' arg to an empty string. """ - request = cherrypy.request + request = cherrypy.serving.request if scheme: s = request.headers.get(scheme, None) @@ -138,7 +138,7 @@ def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For', if local: base = request.headers.get(local, base) if not base: - port = cherrypy.request.local.port + port = request.local.port if port == 80: base = '127.0.0.1' else: @@ -166,7 +166,7 @@ def ignore_headers(headers=('Range',)): for example, Apache duplicates the work that CP does for 'Range' headers, and will doubly-truncate the response. """ - request = cherrypy.request + request = cherrypy.serving.request for name in headers: if name in request.headers: del request.headers[name] @@ -175,7 +175,7 @@ def ignore_headers(headers=('Range',)): def response_headers(headers=None): """Set headers on the response.""" for name, value in (headers or []): - cherrypy.response.headers[name] = value + cherrypy.serving.response.headers[name] = value response_headers.failsafe = True @@ -191,7 +191,7 @@ def referer(pattern, accept=True, accept_missing=False, error=403, message: a string to include in the response body on failure. """ try: - match = bool(re.match(pattern, cherrypy.request.headers['Referer'])) + match = bool(re.match(pattern, cherrypy.serving.request.headers['Referer'])) if accept == match: return except KeyError: @@ -236,16 +236,18 @@ Message: %(error_msg)s def do_login(self, username, password, from_page='..', **kwargs): """Login. May raise redirect, or return True if request handled.""" + response = cherrypy.serving.response error_msg = self.check_username_and_password(username, password) if error_msg: body = self.login_screen(from_page, username, error_msg) - cherrypy.response.body = body - if "Content-Length" in cherrypy.response.headers: + response.body = body + if "Content-Length" in response.headers: # Delete Content-Length header so finalize() recalcs it. - del cherrypy.response.headers["Content-Length"] + del response.headers["Content-Length"] return True else: - cherrypy.session[self.session_key] = cherrypy.request.login = username + cherrypy.serving.request.login = username + cherrypy.session[self.session_key] = username self.on_login(username) raise cherrypy.HTTPRedirect(from_page or "/") @@ -255,40 +257,43 @@ Message: %(error_msg)s username = sess.get(self.session_key) sess[self.session_key] = None if username: - cherrypy.request.login = None + cherrypy.serving.request.login = None self.on_logout(username) raise cherrypy.HTTPRedirect(from_page) def do_check(self): """Assert username. May raise redirect, or return True if request handled.""" sess = cherrypy.session - request = cherrypy.request + request = cherrypy.serving.request + response = cherrypy.serving.response username = sess.get(self.session_key) if not username: sess[self.session_key] = username = self.anonymous() if not username: - cherrypy.response.body = self.login_screen(cherrypy.url(qs=request.query_string)) - if "Content-Length" in cherrypy.response.headers: + response.body = self.login_screen(cherrypy.url(qs=request.query_string)) + if "Content-Length" in response.headers: # Delete Content-Length header so finalize() recalcs it. - del cherrypy.response.headers["Content-Length"] + del response.headers["Content-Length"] return True - cherrypy.request.login = username + request.login = username self.on_check(username) def run(self): - request = cherrypy.request + request = cherrypy.serving.request + response = cherrypy.serving.response + path = request.path_info if path.endswith('login_screen'): return self.login_screen(**request.params) elif path.endswith('do_login'): if request.method != 'POST': - cherrypy.response.headers['Allow'] = "POST" + response.headers['Allow'] = "POST" raise cherrypy.HTTPError(405) return self.do_login(**request.params) elif path.endswith('do_logout'): if request.method != 'POST': - cherrypy.response.headers['Allow'] = "POST" + response.headers['Allow'] = "POST" raise cherrypy.HTTPError(405) return self.do_logout(**request.params) else: @@ -315,22 +320,24 @@ def log_traceback(severity=logging.ERROR): def log_request_headers(): """Write request headers to the cherrypy error log.""" - h = [" %s: %s" % (k, v) for k, v in cherrypy.request.header_list] + h = [" %s: %s" % (k, v) for k, v in cherrypy.serving.request.header_list] cherrypy.log('\nRequest Headers:\n' + '\n'.join(h), "HTTP") def log_hooks(): """Write request.hooks to the cherrypy error log.""" + request = cherrypy.serving.request + msg = [] # Sort by the standard points if possible. from cherrypy import _cprequest points = _cprequest.hookpoints - for k in cherrypy.request.hooks.keys(): + for k in request.hooks.keys(): if k not in points: points.append(k) for k in points: msg.append(" %s:" % k) - v = cherrypy.request.hooks.get(k, []) + v = request.hooks.get(k, []) v.sort() for h in v: msg.append(" %r" % h) @@ -346,7 +353,7 @@ def redirect(url='', internal=True): def trailing_slash(missing=True, extra=False, status=None): """Redirect if path_info has (missing|extra) trailing slash.""" - request = cherrypy.request + request = cherrypy.serving.request pi = request.path_info if request.is_index is True: @@ -375,7 +382,7 @@ def flatten(): else: for y in flattener(x): yield y - response = cherrypy.response + response = cherrypy.serving.response response.body = flattener(response.body) @@ -407,10 +414,11 @@ def accept(media=None): return if isinstance(media, basestring): media = [media] + request = cherrypy.serving.request # Parse the Accept request header, and try to match one # of the requested media-ranges (in order of preference). - ranges = cherrypy.request.headers.elements('Accept') + ranges = request.headers.elements('Accept') if not ranges: # Any media type is acceptable. return media[0] @@ -433,7 +441,7 @@ def accept(media=None): return element.value # No suitable media-range found. - ah = cherrypy.request.headers.get('Accept') + ah = request.headers.get('Accept') if ah is None: msg = "Your client did not send an Accept header." else: @@ -467,22 +475,23 @@ class MonitoredHeaderMap(_httputil.HeaderMap): def autovary(ignore=None): """Auto-populate the Vary response header based on request.header access.""" - req_h = cherrypy.request.headers - cherrypy.request.headers = MonitoredHeaderMap() - cherrypy.request.headers.update(req_h) + request = cherrypy.serving.request + + req_h = request.headers + request.headers = MonitoredHeaderMap() + request.headers.update(req_h) if ignore is None: ignore = set(['Content-Disposition', 'Content-Length', 'Content-Type']) def set_response_header(): - resp_h = cherrypy.response.headers + resp_h = cherrypy.serving.response.headers v = set([e.value for e in resp_h.elements('Vary')]) - v = v.union(cherrypy.request.headers.accessed_headers) + v = v.union(request.headers.accessed_headers) v = v.difference(ignore) v = list(v) v.sort() resp_h['Vary'] = ', '.join(v) - cherrypy.request.hooks.attach( - 'before_finalize', set_response_header, 95) + request.hooks.attach('before_finalize', set_response_header, 95) diff --git a/cherrypy/lib/encoding.py b/cherrypy/lib/encoding.py index 287228dc..2e469e90 100644 --- a/cherrypy/lib/encoding.py +++ b/cherrypy/lib/encoding.py @@ -29,11 +29,11 @@ class ResponseEncoder: setattr(self, k, v) self.attempted_charsets = set() - - if cherrypy.request.handler is not None: + request = cherrypy.serving.request + if request.handler is not None: # Replace request.handler with self - self.oldhandler = cherrypy.request.handler - cherrypy.request.handler = self + self.oldhandler = request.handler + request.handler = self def encode_stream(self, encoding): """Encode a streaming response body. @@ -72,9 +72,10 @@ class ResponseEncoder: return True def find_acceptable_charset(self): - response = cherrypy.response + request = cherrypy.serving.request + response = cherrypy.serving.response - if cherrypy.response.stream: + if response.stream: encoder = self.encode_stream else: encoder = self.encode_string @@ -93,7 +94,7 @@ class ResponseEncoder: # Parse the Accept-Charset request header, and try to provide one # of the requested charsets (in order of user preference). - encs = cherrypy.request.headers.elements('Accept-Charset') + encs = request.headers.elements('Accept-Charset') charsets = [enc.value.lower() for enc in encs] if self.encoding is not None: @@ -132,7 +133,7 @@ class ResponseEncoder: return encoding # No suitable encoding found. - ac = cherrypy.request.headers.get('Accept-Charset') + ac = request.headers.get('Accept-Charset') if ac is None: msg = "Your client did not send an Accept-Charset header." else: @@ -141,6 +142,7 @@ class ResponseEncoder: raise cherrypy.HTTPError(406, msg) def __call__(self, *args, **kwargs): + response = cherrypy.serving.response self.body = self.oldhandler(*args, **kwargs) if isinstance(self.body, basestring): @@ -157,14 +159,14 @@ class ResponseEncoder: elif self.body is None: self.body = [] - ct = cherrypy.response.headers.elements("Content-Type") + ct = response.headers.elements("Content-Type") if ct: ct = ct[0] if (not self.text_only) or ct.value.lower().startswith("text/"): # Set "charset=..." param on response Content-Type header ct.params['charset'] = self.find_acceptable_charset() if self.add_charset: - cherrypy.response.headers["Content-Type"] = str(ct) + response.headers["Content-Type"] = str(ct) return self.body @@ -223,7 +225,8 @@ def gzip(compress_level=5, mime_types=['text/html', 'text/plain']): * No 'gzip' or 'x-gzip' with a qvalue > 0 is present * The 'identity' value is given with a qvalue > 0. """ - response = cherrypy.response + request = cherrypy.serving.request + response = cherrypy.serving.response set_vary_header(response, "Accept-Encoding") @@ -233,10 +236,10 @@ def gzip(compress_level=5, mime_types=['text/html', 'text/plain']): # If returning cached content (which should already have been gzipped), # don't re-zip. - if getattr(cherrypy.request, "cached", False): + if getattr(request, "cached", False): return - acceptable = cherrypy.request.headers.elements('Accept-Encoding') + acceptable = request.headers.elements('Accept-Encoding') if not acceptable: # If no Accept-Encoding field is present in a request, # the server MAY assume that the client will accept any diff --git a/cherrypy/lib/jsontools.py b/cherrypy/lib/jsontools.py index ef231183..b867a9e6 100644 --- a/cherrypy/lib/jsontools.py +++ b/cherrypy/lib/jsontools.py @@ -20,7 +20,7 @@ else: json_encode = json.JSONEncoder().iterencode def json_in(*args, **kwargs): - request = cherrypy.request + request = cherrypy.serving.request _h = request.headers if ('Content-Type' not in _h or _h.elements('Content-Type')[0].value != 'application/json'): @@ -37,8 +37,8 @@ def json_in(*args, **kwargs): request.json = json def json_out(*args, **kwargs): - request = cherrypy.request - response = cherrypy.response + request = cherrypy.serving.request + response = cherrypy.serving.response real_handler = request.handler def json_handler(*args, **kwargs): diff --git a/cherrypy/lib/sessions.py b/cherrypy/lib/sessions.py index c84794eb..11bce96f 100644 --- a/cherrypy/lib/sessions.py +++ b/cherrypy/lib/sessions.py @@ -578,21 +578,23 @@ def save(): if not hasattr(cherrypy.serving, "session"): return + request = cherrypy.serving.request + response = cherrypy.serving.response # Guard against running twice - if hasattr(cherrypy.request, "_sessionsaved"): + if hasattr(request, "_sessionsaved"): return - cherrypy.request._sessionsaved = True + request._sessionsaved = True - if cherrypy.response.stream: + if response.stream: # If the body is being streamed, we have to save the data # *after* the response has been written out - cherrypy.request.hooks.attach('on_end_request', cherrypy.session.save) + request.hooks.attach('on_end_request', cherrypy.session.save) else: # If the body is not being streamed, we save the data now # (so we can release the lock). - if isinstance(cherrypy.response.body, types.GeneratorType): - cherrypy.response.collapse_body() + if isinstance(response.body, types.GeneratorType): + response.collapse_body() cherrypy.session.save() save.failsafe = True @@ -635,7 +637,7 @@ def init(storage_type='ram', path=None, path_header=None, name='session_id', you're using for more information. """ - request = cherrypy.request + request = cherrypy.serving.request # Guard against running twice if hasattr(request, "_session_init_flag"): @@ -662,7 +664,7 @@ def init(storage_type='ram', path=None, path_header=None, name='session_id', cherrypy.serving.session = sess = storage_class(id, **kwargs) def update_cookie(id): """Update the cookie every time the session id changes.""" - cherrypy.response.cookie[name] = id + cherrypy.serving.response.cookie[name] = id sess.id_observers.append(update_cookie) # Create cherrypy.session which will proxy to cherrypy.serving.session @@ -695,9 +697,9 @@ def set_response_cookie(path=None, path_header=None, name='session_id', be set. If True, the cookie 'secure' value will be set (to 1). """ # Set response cookie - cookie = cherrypy.response.cookie + cookie = cherrypy.serving.response.cookie cookie[name] = cherrypy.serving.session.id - cookie[name]['path'] = (path or cherrypy.request.headers.get(path_header) + cookie[name]['path'] = (path or cherrypy.serving.request.headers.get(path_header) or '/') # We'd like to use the "max-age" param as indicated in @@ -716,9 +718,9 @@ def set_response_cookie(path=None, path_header=None, name='session_id', def expire(): """Expire the current session cookie.""" - name = cherrypy.request.config.get('tools.sessions.name', 'session_id') + name = cherrypy.serving.request.config.get('tools.sessions.name', 'session_id') one_year = 60 * 60 * 24 * 365 e = time.time() - one_year - cherrypy.response.cookie[name]['expires'] = httputil.HTTPDate(e) + cherrypy.serving.response.cookie[name]['expires'] = httputil.HTTPDate(e) diff --git a/cherrypy/lib/static.py b/cherrypy/lib/static.py index 697fa8e8..db64890a 100644 --- a/cherrypy/lib/static.py +++ b/cherrypy/lib/static.py @@ -29,7 +29,7 @@ def serve_file(path, content_type=None, disposition=None, name=None): header will be written. """ - response = cherrypy.response + response = cherrypy.serving.response # If path is relative, users should fix it by making path absolute. # That is, CherryPy should not guess where the application root is. @@ -94,7 +94,7 @@ def serve_fileobj(fileobj, content_type=None, disposition=None, name=None): position. """ - response = cherrypy.response + response = cherrypy.serving.response try: st = os.fstat(fileobj.fileno()) @@ -121,13 +121,13 @@ def serve_fileobj(fileobj, content_type=None, disposition=None, name=None): def _serve_fileobj(fileobj, content_type, content_length): """Internal. Set response.body to the given file object, perhaps ranged.""" - response = cherrypy.response + response = cherrypy.serving.response # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code - if cherrypy.request.protocol >= (1, 1): + request = cherrypy.serving.request + if request.protocol >= (1, 1): response.headers["Accept-Ranges"] = "bytes" - r = httputil.get_ranges(cherrypy.request.headers.get('Range'), - content_length) + r = httputil.get_ranges(request.headers.get('Range'), content_length) if r == []: response.headers['Content-Range'] = "bytes */%s" % content_length message = "Invalid Range (first-byte-pos greater than Content-Length)" @@ -221,10 +221,11 @@ def staticdir(section, dir, root="", match="", content_types=None, index=""): '/home/me', the Request-URI is 'myapp', and the index arg is 'index.html', the file '/home/me/myapp/index.html' will be sought. """ - if cherrypy.request.method not in ('GET', 'HEAD'): + request = cherrypy.serving.request + if request.method not in ('GET', 'HEAD'): return False - if match and not re.search(match, cherrypy.request.path_info): + if match and not re.search(match, request.path_info): return False # Allow the use of '~' to refer to a user's home directory. @@ -242,13 +243,13 @@ def staticdir(section, dir, root="", match="", content_types=None, index=""): if section == 'global': section = "/" section = section.rstrip(r"\/") - branch = cherrypy.request.path_info[len(section) + 1:] + branch = request.path_info[len(section) + 1:] branch = unquote(branch.lstrip(r"\/")) # If branch is "", filename will end in a slash filename = os.path.join(dir, branch) cherrypy.log('Checking file %r to fulfill %r' % - (filename, cherrypy.request.path_info), + (filename, request.path_info), context='tools.staticdir', severity=logging.DEBUG) # There's a chance that the branch pulled from the URL might @@ -263,7 +264,7 @@ def staticdir(section, dir, root="", match="", content_types=None, index=""): if index: handled = _attempt(os.path.join(filename, index), content_types) if handled: - cherrypy.request.is_index = filename[-1] in (r"\/") + request.is_index = filename[-1] in (r"\/") return handled def staticfile(filename, root=None, match="", content_types=None): @@ -277,10 +278,11 @@ def staticfile(filename, root=None, match="", content_types=None): a string (e.g. "gif") and 'content-type' is the value to write out in the Content-Type response header (e.g. "image/gif"). """ - if cherrypy.request.method not in ('GET', 'HEAD'): + request = cherrypy.serving.request + if request.method not in ('GET', 'HEAD'): return False - if match and not re.search(match, cherrypy.request.path_info): + if match and not re.search(match, request.path_info): return False # If filename is relative, make absolute using "root". diff --git a/cherrypy/test/test.py b/cherrypy/test/test.py index 3d75b6bf..76a3b0f2 100644 --- a/cherrypy/test/test.py +++ b/cherrypy/test/test.py @@ -18,6 +18,8 @@ serverpem = os.path.join(os.getcwd(), localDir, 'test.pem') import sys import warnings +from cherrypy.lib import profiler + class TestHarness(object): """A test harness for the CherryPy framework and CherryPy applications.""" @@ -152,7 +154,7 @@ class LocalServer(object): except ImportError: warnings.warn("Error importing wsgiconq. pyconquer will not run.") else: - app = wsgiconq.WSGILogger(app) + app = wsgiconq.WSGILogger(app, c_calls=True) if self.validate: try: from wsgiref import validate diff --git a/cherrypy/wsgiserver/__init__.py b/cherrypy/wsgiserver/__init__.py index eb335b8d..5ab12737 100644 --- a/cherrypy/wsgiserver/__init__.py +++ b/cherrypy/wsgiserver/__init__.py @@ -557,7 +557,13 @@ class HTTPRequest(object): data = StringIO.StringIO() while True: line = self.rfile.readline().strip().split(";", 1) - chunk_size = int(line.pop(0), 16) + try: + chunk_size = line.pop(0) + chunk_size = int(chunk_size, 16) + except ValueError: + self.simple_response("400 Bad Request", + "Bad chunked transfer size: " + repr(chunk_size)) + return if chunk_size <= 0: break ## if line: chunk_extension = line[0] |