Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 1102

Show
Ignore:
Timestamp:
05/10/06 01:11:38
Author:
fumanchu
Message:

Dispatch and config lookup now happens as early as possible, once per request (unless InternalRedirect? is raised). Also moved the logging code out of _cputil and into __init__. xmlrpc still needs fixed so it doesn't re-write path_info.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/__init__.py

    r1097 r1102  
    33__version__ = '3.0.0alpha' 
    44 
     5import cgi 
     6import datetime 
    57import sys 
     8import traceback 
    69import types 
    710 
     
    99102    return wrapper 
    100103 
     104 
     105def 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 
     112def 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 
     137def _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 
    101160def 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. 
    107162     
     163    This is not just for errors! Applications may call this at any time to 
     164    log application-specific information. 
     165    """ 
    108166    if traceback: 
    109         msg += _cperror.format_exc() 
    110      
     167        msg += format_exc() 
     168    logfunc = config.get('log_function', _log_message) 
    111169    logfunc(msg, context, severity) 
    112170 
  • trunk/cherrypy/_cprequest.py

    r1100 r1102  
    77 
    88import cherrypy 
    9 from cherrypy import _cperror, _cputil, _cpcgifs, tools 
     9from cherrypy import _cputil, _cpcgifs, tools 
    1010from cherrypy.lib import cptools, httptools, profiler 
    1111 
     
    6767        self.headers = httptools.HeaderMap() 
    6868        self.simple_cookie = Cookie.SimpleCookie() 
     69        self.handler = None 
    6970         
    7071        # Set up the profiler if requested. 
     
    8384            cherrypy.response.body = [] 
    8485         
    85         log_access = cherrypy.config.get("log_access", _cputil.log_access) 
     86        log_access = cherrypy.config.get("log_access", cherrypy.log_access) 
    8687        if log_access: 
    8788            log_access() 
     
    9192    def _run(self): 
    9293        try: 
    93             # This has to be done very early in the request process, 
    94             # because request.path_info is used for config lookups 
    95             # right away. 
    9694            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: 
    103108                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 
    137113        except (KeyboardInterrupt, SystemExit): 
    138114            raise 
     
    142118            self.handle_error(sys.exc_info()) 
    143119     
    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') 
    161155     
    162156    def process_request_line(self): 
     
    196190        server_v = httptools.Version.from_http(server_v) 
    197191        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("/")):] 
    204192     
    205193    def process_headers(self): 
     
    227215                raise cherrypy.HTTPError(400, msg) 
    228216        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 
    229258     
    230259    def _get_browser_url(self): 
     
    290319            dbltrace = ("\n===First Error===\n\n%s" 
    291320                        "\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()) 
    294323        else: 
    295324            body = "" 
    296         r = _cperror.bare_error(body) 
     325        r = cherrypy.bare_error(body) 
    297326        response.status, response.header_list, response.body = r 
    298327 
  • trunk/cherrypy/_cputil.py

    r1097 r1102  
    11"""A few utility classes/functions used by CherryPy.""" 
    22 
    3 import cgi 
    4 import datetime 
    5 import sys 
    6 import traceback 
    7  
    83import cherrypy 
    9 from cherrypy.lib import httptools 
    104 
    115 
    12 def get_object_trail(path=None, root=None): 
    13     """List of (name, object) pairs, from root (app.root) down path. 
     6def notfound(): 
     7    raise cherrypy.NotFound() 
     8 
     9class Dispatcher(object): 
    1410     
    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 
    1725     
    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, [] 
    54111 
    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  
     112dispatch = Dispatcher() 
  • trunk/cherrypy/config.py

    r1100 r1102  
    3131        }, 
    3232    } 
    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 base 
    44     for section, value_map in other.iteritems(): 
    45         base.setdefault(section, {}).update(value_map) 
    46  
    4733 
    4834default_conf = { 
     
    9682        except KeyError: 
    9783            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_info 
    102     app = cherrypy.request.app 
    103      
    104     # Convert the path into a list of names 
    105     if (not path) or path == "/": 
    106         nameList = [] 
    107     else: 
    108         nameList = path.strip('/').split('/') 
    109     nameList.append('index') 
    110      
    111     curpath = "" 
    112     node = app.root 
    113     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-node 
    126         # so that a child's "environment" can override concrete settings 
    127         # of a parent. However, concrete settings in this node will 
    128         # 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] = v 
    134          
    135         conf.update(nodeconf) 
    136      
    137     base = globalconf.copy() 
    138     base.update(conf) 
    139     return base 
    140  
    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_info 
    145     app = cherrypy.request.app 
    146      
    147     # Convert the path into a list of names 
    148     if (not path): 
    149         nameList = [] 
    150     else: 
    151         nameList = path.strip('/').split('/') 
    152     nameList.append('index') 
    153      
    154     foundpath = None 
    155      
    156     curpath = "" 
    157     node = app.root 
    158     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 = curpath 
    169                 break 
    170          
    171         # Get values from cherrypy.config for this path. 
    172         if key in app.conf.get(curpath, {}): 
    173             foundpath = curpath 
    174      
    175     if foundpath is None: 
    176         foundpath = globalconf.get(key) 
    177     return foundpath 
    17884 
    17985 
  • trunk/cherrypy/lib/caching.py

    r1089 r1102  
    160160    def wrapper(): 
    161161        if get(): 
    162             cherrypy.request.dispatch = None 
     162            cherrypy.request.handler = None 
    163163        else: 
    164164            # Note the devious technique here of adding hooks on the fly 
  • trunk/cherrypy/lib/cptools.py

    r1096 r1102  
    77 
    88import cherrypy 
     9import httptools 
    910 
    1011 
     
    288289 
    289290def 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
    291292     
    292293    Useful when running multiple sites within one CP server. 
     
    305306    their own right. 
    306307    """ 
     308    if hasattr(cherrypy.request, "virtual_prefix"): 
     309        return 
    307310     
    308311    domain = cherrypy.request.headers.get('Host', '') 
     
    310313        domain = cherrypy.request.headers.get("X-Forwarded-Host", domain) 
    311314     
    312     prefix = domains.get(domain, "") 
     315    cherrypy.request.virtual_prefix = prefix = domains.get(domain, "") 
    313316    if prefix: 
    314         cherrypy.request.path_info = prefix + "/" + cherrypy.request.path_info 
     317        raise cherrypy.InternalRedirect(httptools.urljoin(prefix, cherrypy.request.path_info)) 
    315318 
    316319def log_traceback(): 
  • trunk/cherrypy/lib/httptools.py

    r1082 r1102  
    288288        code = int(code) 
    289289    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)) 
    291292     
    292293    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)) 
    294296     
    295297    if code not in responseCodes: 
  • trunk/cherrypy/lib/xmlrpc.py

    r1082 r1102  
    1313 
    1414 
    15 def patched_path(path, method): 
    16     """Return 'path' with the rpcMethod appended.""" 
     15def patched_path(path): 
     16    """Return 'path', doctored for RPC.""" 
    1717    if not path.endswith('/'): 
    1818        path += '/' 
     
    2020        # strip the first /rpc2 
    2121        path = path[5:] 
    22     path += str(method).replace('.', '/') 
    2322    return path 
    2423 
  • trunk/cherrypy/test/benchmark.py

    r1101 r1102  
    228228    return rows 
    229229 
    230 def size_report(sizes=(1, 10, 50, 100, 100000, 100000000), 
     230def size_report(sizes=(10, 100, 1000, 10000, 100000, 100000000), 
    231231               concurrency=50): 
    232232    sess = ABSession(concurrency=concurrency) 
  • trunk/cherrypy/test/test_core.py

    r1096 r1102  
    411411        self.getPage("/status/illegal") 
    412412        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)." 
    414414        self.assertErrorPage(500, msg) 
    415415         
     
    420420        self.getPage("/status/bad") 
    421421        self.assertStatus(500) 
    422         msg = "Illegal response status from server (non-numeric)." 
     422        msg = "Illegal response status from server ('error' is non-numeric)." 
    423423        self.assertErrorPage(500, msg) 
    424424     
     
    590590            # No traceback should be present 
    591591            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)." 
    593593            self.assertErrorPage(500, msg, None) 
    594594        finally: 
  • trunk/cherrypy/test/test_static_filter.py

    r1096 r1102  
    113113        self.assertErrorPage(500) 
    114114        self.assertInBody("TypeError: staticdir() takes at least 2 " 
    115                           "arguments (1 given)") 
     115                          "arguments (0 given)") 
    116116         
    117117        # Test up-level security 
  • trunk/cherrypy/test/test_xmlrpc_filter.py

    r1096 r1102  
    66def setup_server(): 
    77    import cherrypy 
    8  
     8    from cherrypy import tools 
     9     
    910    class Root: 
    1011        def index(self): 
     
    1314 
    1415 
    15     class XmlRpc: 
    16          
    17         _cp_config = {'tools.xmlrpc.on': True} 
     16    class XmlRpc(tools.XMLRPCController): 
    1817         
    1918        def return_single_item_list(self): 
  • trunk/cherrypy/tools.py

    r1096 r1102  
    177177        def wrapper(): 
    178178            if self.callable(**self.merged_args()): 
    179                 cherrypy.request.dispatch = None 
     179                cherrypy.request.handler = None 
    180180        # Don't pass conf (or our wrapper will get wrapped!) 
    181181        cherrypy.request.hooks.attach(self.point, wrapper) 
     
    206206base_url = Tool('before_request_body', cptools.base_url) 
    207207response_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. 
    208210virtual_host = Tool('before_request_body', cptools.virtual_host) 
    209211log_tracebacks = Tool('before_error_response', cptools.log_traceback) 
     
    222224    def setup(self): 
    223225        """Hook this tool into cherrypy.request using the given conf.""" 
    224         # Stick the section where "dir" was defined into the params. 
    225226        conf = self.merged_args() 
    226         section = cherrypy.config.request_config_section('tools.staticdir.dir') 
    227227        def wrapper(): 
    228             if self.callable(section, **conf): 
    229                 cherrypy.request.dispatch = None 
     228            if self.callable(**conf): 
     229                cherrypy.request.handler = None 
    230230        # Don't pass conf (or our wrapper will get wrapped!) 
    231231        cherrypy.request.hooks.attach(self.point, wrapper) 
     
    285285 
    286286from 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          
     287class XMLRPCController(object): 
     288     
     289    _cp_config = {'tools.xmlrpc.on': True} 
     290     
     291    def __call__(self, *vpath, **params): 
    304292        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) 
    313303        conf = cherrypy.request.toolmap.get("xmlrpc", {}) 
    314304        _xmlrpc.respond(body, 
    315305                        conf.get('encoding', 'utf-8'), 
    316306                        conf.get('allow_none', 0)) 
     307        return cherrypy.response.body 
     308    __call__.exposed = True 
     309     
     310    index = __call__ 
     311 
     312class _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    """ 
    317318     
    318319    def setup(self): 
    319320        """Hook this tool into cherrypy.request using the given conf.""" 
    320         cherrypy.request.dispatch = self.dispatch 
     321        cherrypy.request.path_info = _xmlrpc.patched_path(cherrypy.request.path_info) 
    321322        cherrypy.request.error_response = _xmlrpc.on_error 
    322323xmlrpc = _XMLRPCTool() 

Hosted by WebFaction

Log in as guest/cpguest to create tickets