Changeset 1102
- Timestamp:
- 05/10/06 01:11:38
- Files:
-
- trunk/cherrypy/__init__.py (modified) (2 diffs)
- trunk/cherrypy/_cprequest.py (modified) (8 diffs)
- trunk/cherrypy/_cputil.py (modified) (1 diff)
- trunk/cherrypy/config.py (modified) (2 diffs)
- trunk/cherrypy/lib/caching.py (modified) (1 diff)
- trunk/cherrypy/lib/cptools.py (modified) (4 diffs)
- trunk/cherrypy/lib/httptools.py (modified) (1 diff)
- trunk/cherrypy/lib/xmlrpc.py (modified) (2 diffs)
- trunk/cherrypy/test/benchmark.py (modified) (1 diff)
- trunk/cherrypy/test/test_core.py (modified) (3 diffs)
- trunk/cherrypy/test/test_static_filter.py (modified) (1 diff)
- trunk/cherrypy/test/test_xmlrpc_filter.py (modified) (2 diffs)
- trunk/cherrypy/tools.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/__init__.py
r1097 r1102 3 3 __version__ = '3.0.0alpha' 4 4 5 import cgi 6 import datetime 5 7 import sys 8 import traceback 6 9 import types 7 10 … … 99 102 return wrapper 100 103 104 105 def logtime(): 106 now = datetime.datetime.now() 107 from cherrypy.lib import httptools 108 month = httptools.monthname[now.month][:3].capitalize() 109 return '%02d/%s/%04d:%02d:%02d:%02d' % ( 110 now.day, month, now.year, now.hour, now.minute, now.second) 111 112 def log_access(): 113 """Default method for logging access""" 114 tmpl = '%(h)s %(l)s %(u)s [%(t)s] "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' 115 s = tmpl % {'h': request.remote_host, 116 'l': '-', 117 'u': getattr(request, "login", None) or "-", 118 't': logtime(), 119 'r': request.request_line, 120 's': response.status.split(" ", 1)[0], 121 'b': response.headers.get('Content-Length', '') or "-", 122 'f': request.headers.get('referer', ''), 123 'a': request.headers.get('user-agent', ''), 124 } 125 126 if config.get('log_to_screen', True): 127 print s 128 129 fname = config.get('log_access_file', '') 130 if fname: 131 f = open(fname, 'ab') 132 f.write(s + '\n') 133 f.close() 134 135 _log_severity_levels = {0: "INFO", 1: "WARNING", 2: "ERROR"} 136 137 def _log_message(msg, context = '', severity = 0): 138 """Default method for logging messages (error log). 139 140 This is not just for errors! Applications may call this at any time to 141 log application-specific information. 142 """ 143 144 level = _log_severity_levels.get(severity, "UNKNOWN") 145 146 s = ' '.join((logtime(), context, level, msg)) 147 148 if config.get('log_to_screen', True): 149 print s 150 151 fname = config.get('log_file', '') 152 #logdir = os.path.dirname(fname) 153 #if logdir and not os.path.exists(logdir): 154 # os.makedirs(logdir) 155 if fname: 156 f = open(fname, 'ab') 157 f.write(s + '\n') 158 f.close() 159 101 160 def log(msg='', context='', severity=0, traceback=False): 102 """Syntactic sugar for writing to the (error) log.""" 103 # Load _cputil lazily to avoid circular references, and 104 # to allow profiler and coverage tools to work on it. 105 import _cputil, _cperror 106 logfunc = config.get('log_function', _cputil.log) 161 """Syntactic sugar for writing to the (error) log. 107 162 163 This is not just for errors! Applications may call this at any time to 164 log application-specific information. 165 """ 108 166 if traceback: 109 msg += _cperror.format_exc()110 167 msg += format_exc() 168 logfunc = config.get('log_function', _log_message) 111 169 logfunc(msg, context, severity) 112 170 trunk/cherrypy/_cprequest.py
r1100 r1102 7 7 8 8 import cherrypy 9 from cherrypy import _cp error, _cputil, _cpcgifs, tools9 from cherrypy import _cputil, _cpcgifs, tools 10 10 from cherrypy.lib import cptools, httptools, profiler 11 11 … … 67 67 self.headers = httptools.HeaderMap() 68 68 self.simple_cookie = Cookie.SimpleCookie() 69 self.handler = None 69 70 70 71 # Set up the profiler if requested. … … 83 84 cherrypy.response.body = [] 84 85 85 log_access = cherrypy.config.get("log_access", _cputil.log_access)86 log_access = cherrypy.config.get("log_access", cherrypy.log_access) 86 87 if log_access: 87 88 log_access() … … 91 92 def _run(self): 92 93 try: 93 # This has to be done very early in the request process,94 # because request.path_info is used for config lookups95 # right away.96 94 self.process_request_line() 97 self.dispatch = self.config.get("dispatch") or _cputil.dispatch 98 self.hooks.setup() 99 100 try: 101 self.hooks.run('on_start_resource') 102 95 96 # Get the 'Host' header, so we can do HTTPRedirects properly. 97 self.process_headers() 98 99 # path_info should be the path from the app 100 # root (script_name) to the handler. 101 self.script_name = r = cherrypy.tree.script_name(self.path) 102 self.app = cherrypy.tree.apps[r] 103 self.path_info = self.path[len(r.rstrip("/")):] 104 105 # Loop to allow for InternalRedirect. 106 pi = self.path_info 107 while True: 103 108 try: 104 self.process_headers() 105 106 # Prepare the SizeCheckWrapper for the request body 107 mbs = int(self.config.get('server.max_request_body_size', 108 100 * 1024 * 1024)) 109 if mbs > 0: 110 self.rfile = httptools.SizeCheckWrapper(self.rfile, mbs) 111 112 self.hooks.run('before_request_body') 113 if self.process_request_body: 114 self.process_body() 115 116 # Loop to allow for InternalRedirect. 117 while True: 118 try: 119 self.hooks.run('before_main') 120 if self.dispatch: 121 self.dispatch(self.path_info) 122 break 123 except cherrypy.InternalRedirect, ir: 124 self.path_info = ir.path 125 126 self.hooks.run('before_finalize') 127 cherrypy.response.finalize() 128 except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst: 129 # For an HTTPRedirect or HTTPError (including NotFound), 130 # we don't go through the regular mechanism: 131 # we return the redirect or error page immediately 132 inst.set_response() 133 self.hooks.run('before_finalize') 134 cherrypy.response.finalize() 135 finally: 136 self.hooks.run('on_end_resource') 109 self.respond(pi) 110 break 111 except cherrypy.InternalRedirect, ir: 112 pi = ir.path 137 113 except (KeyboardInterrupt, SystemExit): 138 114 raise … … 142 118 self.handle_error(sys.exc_info()) 143 119 144 def _get_path_info(self): 145 return self._path_info 146 def _set_path_info(self, value): 147 self._path_info = value 148 self.config = cherrypy.config.request_config() 149 150 # Get all 'tools.*' config entries as a {toolname: {k: v}} dict. 151 self.toolmap = {} 152 for k, v in self.config.iteritems(): 153 atoms = k.split(".") 154 namespace = atoms.pop(0) 155 if namespace == "tools": 156 toolname = atoms.pop(0) 157 bucket = self.toolmap.setdefault(toolname, {}) 158 bucket[".".join(atoms)] = v 159 path_info = property(_get_path_info, _set_path_info, 160 doc="The path to the rendered resource.") 120 def respond(self, path_info): 121 """Generate a response for the resource at self.path_info.""" 122 try: 123 try: 124 self.get_resource(path_info) 125 self.hooks.setup() 126 self.hooks.run('on_start_resource') 127 128 if self.process_request_body: 129 # Prepare the SizeCheckWrapper for the request body 130 mbs = int(self.config.get('server.max_request_body_size', 131 100 * 1024 * 1024)) 132 if mbs > 0: 133 self.rfile = httptools.SizeCheckWrapper(self.rfile, mbs) 134 135 self.hooks.run('before_request_body') 136 if self.process_request_body: 137 self.process_body() 138 # Guard against re-reading body on InternalRedirect 139 self.process_request_body = False 140 141 self.hooks.run('before_main') 142 if self.handler: 143 self.handler() 144 self.hooks.run('before_finalize') 145 cherrypy.response.finalize() 146 except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst: 147 # For an HTTPRedirect or HTTPError (including NotFound), 148 # we don't go through the regular mechanism: 149 # we return the redirect or error page immediately 150 inst.set_response() 151 self.hooks.run('before_finalize') 152 cherrypy.response.finalize() 153 finally: 154 self.hooks.run('on_end_resource') 161 155 162 156 def process_request_line(self): … … 196 190 server_v = httptools.Version.from_http(server_v) 197 191 cherrypy.response.version = min(self.version, server_v) 198 199 # Change path_info to change the object that will get rendered.200 # path_info should be the path from the app root to the handler.201 self.script_name = r = cherrypy.tree.script_name(path)202 self.app = cherrypy.tree.apps[r]203 self.path_info = path[len(r.rstrip("/")):]204 192 205 193 def process_headers(self): … … 227 215 raise cherrypy.HTTPError(400, msg) 228 216 self.base = "%s://%s" % (self.scheme, self.headers.get('Host', '')) 217 218 def get_resource(self, path): 219 dispatch = _cputil.dispatch 220 # First, see if there is a custom dispatch at this URI. Custom 221 # dispatchers can only be specified in app.conf, not in _cp_config 222 # (since custom dispatchers may not even have an app.root). 223 trail = path 224 while trail: 225 nodeconf = self.app.conf.get(trail, {}) 226 d = nodeconf.get("dispatch") 227 if d: 228 dispatch = d 229 break 230 231 env = nodeconf.get("environment") 232 if env: 233 d = cherrypy.config.environments[env].get("dispatch") 234 if d: 235 dispatch = d 236 break 237 238 lastslash = trail.rfind("/") 239 if lastslash == -1: 240 break 241 elif lastslash == 0 and trail != "/": 242 trail = "/" 243 else: 244 trail = trail[:lastslash] 245 246 # dispatch() should set self.handler and self.config 247 dispatch(path) 248 249 # Get all 'tools.*' config entries as a {toolname: {k: v}} dict. 250 self.toolmap = {} 251 for k, v in self.config.iteritems(): 252 atoms = k.split(".") 253 namespace = atoms.pop(0) 254 if namespace == "tools": 255 toolname = atoms.pop(0) 256 bucket = self.toolmap.setdefault(toolname, {}) 257 bucket[".".join(atoms)] = v 229 258 230 259 def _get_browser_url(self): … … 290 319 dbltrace = ("\n===First Error===\n\n%s" 291 320 "\n\n===Second Error===\n\n%s\n\n") 292 body = dbltrace % ( _cperror.format_exc(exc),293 _cperror.format_exc())321 body = dbltrace % (cherrypy.format_exc(exc), 322 cherrypy.format_exc()) 294 323 else: 295 324 body = "" 296 r = _cperror.bare_error(body)325 r = cherrypy.bare_error(body) 297 326 response.status, response.header_list, response.body = r 298 327 trunk/cherrypy/_cputil.py
r1097 r1102 1 1 """A few utility classes/functions used by CherryPy.""" 2 2 3 import cgi4 import datetime5 import sys6 import traceback7 8 3 import cherrypy 9 from cherrypy.lib import httptools10 4 11 5 12 def get_object_trail(path=None, root=None): 13 """List of (name, object) pairs, from root (app.root) down path. 6 def notfound(): 7 raise cherrypy.NotFound() 8 9 class Dispatcher(object): 14 10 15 If any named objects are unreachable, (name, None) pairs are used. 16 """ 11 def __call__(self, path_info): 12 """Set handler and config for the current request.""" 13 request = cherrypy.request 14 func, vpath = self.find_handler(path_info) 15 16 # Decode any leftover %2F in the virtual_path atoms. 17 vpath = [x.replace("%2F", "/") for x in vpath] 18 19 if func: 20 def handler(): 21 cherrypy.response.body = func(*vpath, **request.params) 22 request.handler = handler 23 else: 24 request.handler = notfound 17 25 18 if path is None: 19 try: 20 path = cherrypy.request.path_info 21 except AttributeError: 22 pass 23 24 if path is not None: 25 path = path.strip('/') 26 27 # Convert the path into a list of names 28 if not path: 29 nameList = [] 30 else: 31 nameList = path.split('/') 32 33 if root is None: 34 try: 35 root = cherrypy.request.app.root 36 except AttributeError: 37 return [('root', None), ('index', None)] 38 39 nameList.append('index') 40 41 # Convert the list of names into a list of objects 42 node = root 43 object_trail = [('root', root)] 44 for name in nameList: 45 # maps virtual names to Python identifiers (replaces '.' with '_') 46 objname = name.replace('.', '_') 47 node = getattr(node, objname, None) 48 if node is None: 49 object_trail.append((name, node)) 50 else: 51 object_trail.append((objname, node)) 52 53 return object_trail 26 def find_handler(self, path): 27 """Find the appropriate page handler for the given path.""" 28 request = cherrypy.request 29 app = request.app 30 root = app.root 31 32 # Get config for the root object/path. 33 curpath = "" 34 nodeconf = getattr(root, "_cp_config", {}).copy() 35 nodeconf.update(app.conf.get("/", {})) 36 object_trail = [('root', root, nodeconf, curpath)] 37 38 node = root 39 names = [x for x in path.strip('/').split('/') if x] + ['index'] 40 for name in names: 41 # map to legal Python identifiers (replace '.' with '_') 42 objname = name.replace('.', '_') 43 44 nodeconf = {} 45 node = getattr(node, objname, None) 46 if node is not None: 47 # Get _cp_config attached to this node. 48 nodeconf = getattr(node, "_cp_config", {}).copy() 49 50 # Mix in values from app.conf for this path. 51 curpath = "/".join((curpath, name)) 52 nodeconf.update(app.conf.get(curpath, {})) 53 54 # Resolve "environment" entries. This must be done node-by-node 55 # so that a child's "environment" can override concrete settings 56 # of a parent. However, concrete settings in this node will 57 # override "environment" settings in the same node. 58 env = nodeconf.get("environment") 59 if env: 60 for k, v in cherrypy.config.environments[env].iteritems(): 61 if k not in nodeconf: 62 nodeconf[k] = v 63 64 object_trail.append((objname, node, nodeconf, curpath)) 65 66 def set_conf(): 67 """Set cherrypy.request.config.""" 68 base = cherrypy.config.globalconf.copy() 69 if 'tools.staticdir.dir' in base: 70 base['tools.staticdir.section'] = "global" 71 for name, obj, conf, curpath in object_trail: 72 base.update(conf) 73 if 'tools.staticdir.dir' in conf: 74 base['tools.staticdir.section'] = curpath 75 request.config = base 76 77 # Try successive objects (reverse order) 78 for i in xrange(len(object_trail) - 1, -1, -1): 79 80 name, candidate, nodeconf, curpath = object_trail[i] 81 82 # Try a "default" method on the current leaf. 83 defhandler = getattr(candidate, "default", None) 84 if callable(defhandler) and getattr(defhandler, 'exposed', False): 85 # Insert any extra _cp_config from the default handler. 86 conf = getattr(defhandler, "_cp_config", {}) 87 object_trail.insert(i+1, ("default", defhandler, conf, curpath)) 88 set_conf() 89 return defhandler, names[i:-1] 90 91 # Uncomment the next line to restrict positional params to "default". 92 # if i < len(object_trail) - 2: continue 93 94 # Try the current leaf. 95 if callable(candidate) and getattr(candidate, 'exposed', False): 96 set_conf() 97 if i == len(object_trail) - 1: 98 # We found the extra ".index". Check if the original path 99 # had a trailing slash (otherwise, do a redirect). 100 if not path.endswith('/'): 101 atoms = request.browser_url.split("?", 1) 102 newUrl = atoms.pop(0) + '/' 103 if atoms: 104 newUrl += "?" + atoms[0] 105 raise cherrypy.HTTPRedirect(newUrl) 106 return candidate, names[i:-1] 107 108 # We didn't find anything 109 set_conf() 110 return None, [] 54 111 55 def dispatch(path): 56 """Find and run the appropriate page handler.""" 57 request = cherrypy.request 58 handler, opath, vpath = find_handler(path) 59 60 # Remove "root" from opath and join it to get found_object_path 61 # There are no consumers of this info right now, so this block 62 # may disappear soon. 63 if opath and opath[0] == "root": 64 opath.pop(0) 65 request.found_object_path = '/' + '/'.join(opath) 66 67 # Decode any leftover %2F in the virtual_path atoms. 68 vpath = [x.replace("%2F", "/") for x in vpath] 69 cherrypy.response.body = handler(*vpath, **request.params) 70 71 def find_handler(path): 72 """Find the appropriate page handler for the given path.""" 73 object_trail = get_object_trail(path) 74 names = [name for name, candidate in object_trail] 75 76 # Try successive objects (reverse order) 77 for i in xrange(len(object_trail) - 1, -1, -1): 78 79 name, candidate = object_trail[i] 80 81 # Try a "default" method on the current leaf. 82 defhandler = getattr(candidate, "default", None) 83 if callable(defhandler) and getattr(defhandler, 'exposed', False): 84 return defhandler, names[:i+1] + ["default"], names[i+1:-1] 85 86 # Uncomment the next line to restrict positional params to "default". 87 # if i < len(object_trail) - 2: continue 88 89 # Try the current leaf. 90 if callable(candidate) and getattr(candidate, 'exposed', False): 91 if i == len(object_trail) - 1: 92 # We found the extra ".index". Check if the original path 93 # had a trailing slash (otherwise, do a redirect). 94 if not path.endswith('/'): 95 atoms = cherrypy.request.browser_url.split("?", 1) 96 newUrl = atoms.pop(0) + '/' 97 if atoms: 98 newUrl += "?" + atoms[0] 99 raise cherrypy.HTTPRedirect(newUrl) 100 return candidate, names[:i+1], names[i+1:-1] 101 102 # We didn't find anything 103 raise cherrypy.NotFound(path) 104 105 106 def logtime(): 107 now = datetime.datetime.now() 108 month = httptools.monthname[now.month][:3].capitalize() 109 return '%02d/%s/%04d:%02d:%02d:%02d' % ( 110 now.day, month, now.year, now.hour, now.minute, now.second) 111 112 def log_access(): 113 """Default method for logging access""" 114 request = cherrypy.request 115 116 tmpl = '%(h)s %(l)s %(u)s [%(t)s] "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' 117 s = tmpl % {'h': request.remote_host, 118 'l': '-', 119 'u': getattr(request, "login", None) or "-", 120 't': logtime(), 121 'r': request.request_line, 122 's': cherrypy.response.status.split(" ", 1)[0], 123 'b': cherrypy.response.headers.get('Content-Length', '') or "-", 124 'f': request.headers.get('referer', ''), 125 'a': request.headers.get('user-agent', ''), 126 } 127 128 if cherrypy.config.get('log_to_screen', True): 129 print s 130 131 fname = cherrypy.config.get('log_access_file', '') 132 if fname: 133 f = open(fname, 'ab') 134 f.write(s + '\n') 135 f.close() 136 137 _log_severity_levels = {0: "INFO", 1: "WARNING", 2: "ERROR"} 138 139 def log(msg, context = '', severity = 0): 140 """Default method for logging messages (error log). 141 142 This is not just for errors! Applications may call this at any time to 143 log application-specific information. 144 """ 145 146 level = _log_severity_levels.get(severity, "UNKNOWN") 147 148 s = ' '.join((logtime(), context, level, msg)) 149 150 if cherrypy.config.get('log_to_screen', True): 151 print s 152 153 fname = cherrypy.config.get('log_file', '') 154 #logdir = os.path.dirname(fname) 155 #if logdir and not os.path.exists(logdir): 156 # os.makedirs(logdir) 157 if fname: 158 f = open(fname, 'ab') 159 f.write(s + '\n') 160 f.close() 161 112 dispatch = Dispatcher() trunk/cherrypy/config.py
r1100 r1102 31 31 }, 32 32 } 33 34 def merge(base, other):35 """Merge one config (from a dict, file, or filename) into another."""36 if isinstance(other, basestring):37 if other not in autoreload.reloadFiles:38 autoreload.reloadFiles.append(other)39 other = dict_from_config_file(other)40 elif hasattr(other, 'read'):41 other = dict_from_config_file(other)42 43 # Load other into base44 for section, value_map in other.iteritems():45 base.setdefault(section, {}).update(value_map)46 47 33 48 34 default_conf = { … … 96 82 except KeyError: 97 83 return default 98 99 def request_config():100 """Return all configs in effect for the current request in a single dict."""101 path = cherrypy.request.path_info102 app = cherrypy.request.app103 104 # Convert the path into a list of names105 if (not path) or path == "/":106 nameList = []107 else:108 nameList = path.strip('/').split('/')109 nameList.append('index')110 111 curpath = ""112 node = app.root113 conf = getattr(node, "_cp_config", {}).copy()114 conf.update(app.conf.get("/", {}))115 for name in nameList:116 # Get _cp_config attached to each node on this app's tree.117 objname = name.replace('.', '_')118 node = getattr(node, objname, None)119 nodeconf = getattr(node, "_cp_config", {})120 121 # Get values from app.config for this path.122 curpath = "/".join((curpath, name))123 nodeconf.update(app.conf.get(curpath, {}))124 125 # Resolve "environment" entries. This must be done node-by-node126 # so that a child's "environment" can override concrete settings127 # of a parent. However, concrete settings in this node will128 # override "environment" settings in the same node.129 env = nodeconf.get("environment")130 if env:131 for k, v in environments[env].iteritems():132 if k not in nodeconf:133 nodeconf[k] = v134 135 conf.update(nodeconf)136 137 base = globalconf.copy()138 base.update(conf)139 return base140 141 142 def request_config_section(key):143 """Return the (longest) path where the given key is defined (or None)."""144 path = cherrypy.request.path_info145 app = cherrypy.request.app146 147 # Convert the path into a list of names148 if (not path):149 nameList = []150 else:151 nameList = path.strip('/').split('/')152 nameList.append('index')153 154 foundpath = None155 156 curpath = ""157 node = app.root158 if key in getattr(node, "_cp_config", {}) or key in app.conf.get("/", {}):159 foundpath = "/"160 for name in nameList:161 curpath = "/".join((curpath, name))162 163 # Get _cp_config attached to each node on this app's tree.164 objname = name.replace('.', '_')165 node = getattr(node, objname, None)166 if node is not None:167 if key in getattr(node, "_cp_config", {}):168 foundpath = curpath169 break170 171 # Get values from cherrypy.config for this path.172 if key in app.conf.get(curpath, {}):173 foundpath = curpath174 175 if foundpath is None:176 foundpath = globalconf.get(key)177 return foundpath178 84 179 85 trunk/cherrypy/lib/caching.py
r1089 r1102 160 160 def wrapper(): 161 161 if get(): 162 cherrypy.request. dispatch= None162 cherrypy.request.handler = None 163 163 else: 164 164 # Note the devious technique here of adding hooks on the fly trunk/cherrypy/lib/cptools.py
r1096 r1102 7 7 8 8 import cherrypy 9 import httptools 9 10 10 11 … … 288 289 289 290 def virtual_host(use_x_forwarded_host=True, **domains): 290 """ Change the path_info based on the Host.291 """Redirect internally based on the Host header. 291 292 292 293 Useful when running multiple sites within one CP server. … … 305 306 their own right. 306 307 """ 308 if hasattr(cherrypy.request, "virtual_prefix"): 309 return 307 310 308 311 domain = cherrypy.request.headers.get('Host', '') … … 310 313 domain = cherrypy.request.headers.get("X-Forwarded-Host", domain) 311 314 312 prefix = domains.get(domain, "")315 cherrypy.request.virtual_prefix = prefix = domains.get(domain, "") 313 316 if prefix: 314 cherrypy.request.path_info = prefix + "/" + cherrypy.request.path_info317 raise cherrypy.InternalRedirect(httptools.urljoin(prefix, cherrypy.request.path_info)) 315 318 316 319 def log_traceback(): trunk/cherrypy/lib/httptools.py
r1082 r1102 288 288 code = int(code) 289 289 except ValueError: 290 raise ValueError("Illegal response status from server (non-numeric).") 290 raise ValueError("Illegal response status from server " 291 "(%s is non-numeric)." % repr(code)) 291 292 292 293 if code < 100 or code > 599: 293 raise ValueError("Illegal response status from server (out of range).") 294 raise ValueError("Illegal response status from server " 295 "(%s is out of range)." % repr(code)) 294 296 295 297 if code not in responseCodes: trunk/cherrypy/lib/xmlrpc.py
r1082 r1102 13 13 14 14 15 def patched_path(path , method):16 """Return 'path' with the rpcMethod appended."""15 def patched_path(path): 16 """Return 'path', doctored for RPC.""" 17 17 if not path.endswith('/'): 18 18 path += '/' … … 20 20 # strip the first /rpc2 21 21 path = path[5:] 22 path += str(method).replace('.', '/')23 22 return path 24 23 trunk/cherrypy/test/benchmark.py
r1101 r1102 228 228 return rows 229 229 230 def size_report(sizes=(1 , 10, 50, 100, 100000, 100000000),230 def size_report(sizes=(10, 100, 1000, 10000, 100000, 100000000), 231 231 concurrency=50): 232 232 sess = ABSession(concurrency=concurrency) trunk/cherrypy/test/test_core.py
r1096 r1102 411 411 self.getPage("/status/illegal") 412 412 self.assertStatus(500) 413 msg = "Illegal response status from server ( out of range)."413 msg = "Illegal response status from server (781 is out of range)." 414 414 self.assertErrorPage(500, msg) 415 415 … … 420 420 self.getPage("/status/bad") 421 421 self.assertStatus(500) 422 msg = "Illegal response status from server ( non-numeric)."422 msg = "Illegal response status from server ('error' is non-numeric)." 423 423 self.assertErrorPage(500, msg) 424 424 … … 590 590 # No traceback should be present 591 591 self.getPage("/error/cause_err_in_finalize") 592 msg = "Illegal response status from server ( non-numeric)."592 msg = "Illegal response status from server ('ZOO' is non-numeric)." 593 593 self.assertErrorPage(500, msg, None) 594 594 finally: trunk/cherrypy/test/test_static_filter.py
r1096 r1102 113 113 self.assertErrorPage(500) 114 114 self.assertInBody("TypeError: staticdir() takes at least 2 " 115 "arguments ( 1given)")115 "arguments (0 given)") 116 116 117 117 # Test up-level security trunk/cherrypy/test/test_xmlrpc_filter.py
r1096 r1102 6 6 def setup_server(): 7 7 import cherrypy 8 8 from cherrypy import tools 9 9 10 class Root: 10 11 def index(self): … … 13 14 14 15 15 class XmlRpc: 16 17 _cp_config = {'tools.xmlrpc.on': True} 16 class XmlRpc(tools.XMLRPCController): 18 17 19 18 def return_single_item_list(self): trunk/cherrypy/tools.py
r1096 r1102 177 177 def wrapper(): 178 178 if self.callable(**self.merged_args()): 179 cherrypy.request. dispatch= None179 cherrypy.request.handler = None 180 180 # Don't pass conf (or our wrapper will get wrapped!) 181 181 cherrypy.request.hooks.attach(self.point, wrapper) … … 206 206 base_url = Tool('before_request_body', cptools.base_url) 207 207 response_headers = Tool('before_finalize', cptools.response_headers) 208 # We can't call virtual_host in on_start_resource, 209 # because it's failsafe and the redirect would be swallowed. 208 210 virtual_host = Tool('before_request_body', cptools.virtual_host) 209 211 log_tracebacks = Tool('before_error_response', cptools.log_traceback) … … 222 224 def setup(self): 223 225 """Hook this tool into cherrypy.request using the given conf.""" 224 # Stick the section where "dir" was defined into the params.225 226 conf = self.merged_args() 226 section = cherrypy.config.request_config_section('tools.staticdir.dir')227 227 def wrapper(): 228 if self.callable( section,**conf):229 cherrypy.request. dispatch= None228 if self.callable(**conf): 229 cherrypy.request.handler = None 230 230 # Don't pass conf (or our wrapper will get wrapped!) 231 231 cherrypy.request.hooks.attach(self.point, wrapper) … … 285 285 286 286 from cherrypy.lib import xmlrpc as _xmlrpc 287 class _XMLRPCTool(object): 288 """Tool for using XMLRPC over HTTP. 289 290 Python's None value cannot be used in standard XML-RPC; to allow 291 using it via an extension, provide a true value for allow_none. 292 """ 293 294 def dispatch(self, path): 295 """Use this tool for cherrypy.request dispatch. 296 297 For example: 298 [/rpc] 299 dispatch = tools.xmlrpc.dispatch 300 """ 301 request = cherrypy.request 302 request.error_response = _xmlrpc.on_error 303 287 class XMLRPCController(object): 288 289 _cp_config = {'tools.xmlrpc.on': True} 290 291 def __call__(self, *vpath, **params): 304 292 rpcparams, rpcmethod = _xmlrpc.process_body() 305 path = _xmlrpc.patched_path(path, rpcmethod) 306 307 handler, opath, vpath = _cputil.find_handler(path) 308 309 # Decode any leftover %2F in the virtual_path atoms. 310 vpath = tuple([x.replace("%2F", "/") for x in vpath]) 311 312 body = handler(*(vpath + rpcparams), **request.params) 293 294 subhandler = self 295 for attr in str(rpcmethod).split('.'): 296 subhandler = getattr(subhandler, attr, None) 297 if subhandler is None: 298 raise cherrypy.NotFound() 299 if not getattr(subhandler, "exposed", False): 300 raise cherrypy.NotFound() 301 302 body = subhandler(*(vpath + rpcparams), **params) 313 303 conf = cherrypy.request.toolmap.get("xmlrpc", {}) 314 304 _xmlrpc.respond(body, 315 305 conf.get('encoding', 'utf-8'), 316 306 conf.get('allow_none', 0)) 307 return cherrypy.response.body 308 __call__.exposed = True 309 310 index = __call__ 311 312 class _XMLRPCTool(object): 313 """Tool for using XMLRPC over HTTP. 314 315 Python's None value cannot be used in standard XML-RPC; to allow 316 using it via an extension, provide a true value for allow_none. 317 """ 317 318 318 319 def setup(self): 319 320 """Hook this tool into cherrypy.request using the given conf.""" 320 cherrypy.request. dispatch = self.dispatch321 cherrypy.request.path_info = _xmlrpc.patched_path(cherrypy.request.path_info) 321 322 cherrypy.request.error_response = _xmlrpc.on_error 322 323 xmlrpc = _XMLRPCTool()

