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

Changeset 1047

Show
Ignore:
Timestamp:
04/21/06 15:44:17
Author:
fumanchu
Message:

CP 3 initial checkin. Global filters have been replaced by request.hooks. Lots of renaming and reorg of modules.

Files:

Legend:

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

    r1037 r1047  
    11"""Global module that all modules developing with CherryPy should import.""" 
    22 
    3 __version__ = '2.2.0
     3__version__ = '3.0.0alpha
    44 
    55import datetime 
     
    99from _cperror import * 
    1010import config 
     11import tools 
    1112 
    1213import _cptree 
     
    1516# Legacy code may clobber this. 
    1617root = None 
    17  
    18 lowercase_api = False 
    1918 
    2019import _cpserver 
     
    5958# Create thread_data object as a thread-specific all-purpose storage 
    6059thread_data = local() 
    61 threadData = thread_data # Backward compatibility 
    6260 
    63 # Create variables needed for session (see lib/sessionfilter.py for more info) 
    64 from filters import sessionfilter 
    65 session = sessionfilter.SessionWrapper() 
    66 _session_data_holder = {} # Needed for RAM sessions only 
    67 _session_lock_dict = {} # Needed for RAM sessions only 
    68 _session_last_clean_up_time = datetime.datetime.now() 
     61### Create variables needed for session (see lib/sessionfilter.py for more info) 
     62##from filters import sessionfilter 
     63##session = sessionfilter.SessionWrapper() 
     64##_session_data_holder = {} # Needed for RAM sessions only 
     65##_session_lock_dict = {} # Needed for RAM sessions only 
     66##_session_last_clean_up_time = datetime.datetime.now() 
    6967 
    7068def expose(func=None, alias=None): 
     
    102100     
    103101    logfunc(msg, context, severity) 
     102 
  • trunk/cherrypy/_cpengine.py

    r989 r1047  
    88 
    99import cherrypy 
    10 from cherrypy import _cphttptools, filters 
     10from cherrypy import _cprequest 
    1111from cherrypy.lib import autoreload, profiler, cptools 
    1212 
     
    2020    """The application server engine, connecting HTTP servers to Requests.""" 
    2121     
    22     request_class = _cphttptools.Request 
    23     response_class = _cphttptools.Response 
     22    request_class = _cprequest.Request 
     23    response_class = _cprequest.Response 
    2424     
    2525    def __init__(self): 
     
    7474         
    7575        # Initialize the built in filters 
    76         filters.init() 
     76##        filters.init() 
    7777     
    7878    def start(self): 
     
    160160                                    " receive requests, False otherwise.") 
    161161     
    162     def request(self, clientAddress, remoteHost, scheme="http"): 
     162    def request(self, clientAddress, remote_host, scheme="http"): 
    163163        """Obtain an HTTP Request object. 
    164164         
    165165        clientAddress: the (IP address, port) of the client 
    166         remoteHost: the IP address of the client 
     166        remote_host: the IP address of the client 
    167167        scheme: either "http" or "https"; defaults to "http" 
    168168        """ 
     
    186186         
    187187        r = self.request_class(clientAddress[0], clientAddress[1], 
    188                                remoteHost, scheme) 
     188                               remote_host, scheme) 
    189189        cherrypy.serving.request = r 
    190190        cherrypy.serving.response = self.response_class() 
  • trunk/cherrypy/_cperror.py

    r972 r1047  
    4141            if isinstance(params, basestring): 
    4242                request.query_string = params 
    43                 request.queryString = request.query_string # Backward compatibility 
    4443                pm = cgi.parse_qs(params, keep_blank_values=True) 
    4544                for key, val in pm.items(): 
  • trunk/cherrypy/_cprequest.py

    r1046 r1047  
    77 
    88import cherrypy 
    9 from cherrypy import _cputil, _cpcgifs 
    10 from cherrypy.filters import applyFilters 
     9from cherrypy import _cputil, _cpcgifs, tools 
    1110from cherrypy.lib import cptools, httptools 
     11 
     12 
     13class HookMap(object): 
     14     
     15    def __init__(self, points=None, failsafe=None): 
     16        points = points or [] 
     17        self.callbacks = dict([(point, []) for point in points]) 
     18        self.failsafe = failsafe or [] 
     19     
     20    def attach(self, point, callback, conf=None): 
     21        if conf is None: 
     22            self.callbacks[point].append(callback) 
     23        else: 
     24            def wrapper(): 
     25                callback(**conf) 
     26            self.callbacks[point].append(wrapper) 
     27     
     28    def populate_from_config(self): 
     29        configs = cherrypy.config.configs 
     30        collapsed_map = {} 
     31         
     32        def collect_tools(section): 
     33            local_conf = configs.get(section, {}) 
     34            for k, v in local_conf.iteritems(): 
     35                atoms = k.split(".") 
     36                namespace = atoms.pop(0) 
     37                if namespace == "tools": 
     38                    toolname = atoms.pop(0) 
     39                    bucket = collapsed_map.setdefault(toolname, {}) 
     40                    bucket[".".join(atoms)] = v 
     41         
     42        collect_tools("global") 
     43        path = "" 
     44        for b in cherrypy.request.object_path.split('/'): 
     45            path = "/".join((path, b)) 
     46            collect_tools(path) 
     47         
     48        for toolname, conf in collapsed_map.iteritems(): 
     49            if conf.get("on", False): 
     50                del conf["on"] 
     51                getattr(tools, toolname).setup(conf) 
     52     
     53    def run(self, point): 
     54        """Execute all registered callbacks for the given point.""" 
     55        failsafe = point in self.failsafe 
     56        for callback in self.callbacks[point]: 
     57            # Some hookpoints guarantee all callbacks are run even if 
     58            # others at the same hookpoint fail. We will still log the 
     59            # failure, but proceed on to the next callback. The only way 
     60            # to stop all processing from one of these callbacks is 
     61            # to raise SystemExit and stop the whole server. So, trap 
     62            # your own errors in these callbacks! 
     63            if failsafe: 
     64                try: 
     65                    callback() 
     66                except (KeyboardInterrupt, SystemExit): 
     67                    raise 
     68                except: 
     69                    cherrypy.log(traceback=True) 
     70            else: 
     71                callback() 
     72 
    1273 
    1374 
     
    1576    """An HTTP request.""" 
    1677     
    17     def __init__(self, remoteAddr, remotePort, remoteHost, scheme="http"): 
     78    def __init__(self, remote_addr, remote_port, remote_host, scheme="http"): 
    1879        """Populate a new Request object. 
    1980         
    20         remoteAddr should be the client IP address 
    21         remotePort should be the client Port 
    22         remoteHost should be string of the client's IP address. 
     81        remote_addr should be the client IP address 
     82        remote_port should be the client Port 
     83        remote_host should be string of the client's IP address. 
    2384        scheme should be a string, either "http" or "https". 
    2485        """ 
    25         self.remote_addr  = remoteAddr 
    26         self.remote_port  = remotePort 
    27         self.remote_host  = remoteHost 
    28         # backward compatibility 
    29         self.remoteAddr = remoteAddr 
    30         self.remotePort = remotePort 
    31         self.remoteHost = remoteHost 
     86        self.remote_addr  = remote_addr 
     87        self.remote_port  = remote_port 
     88        self.remote_host  = remote_host 
    3289         
    3390        self.scheme = scheme 
    3491        self.execute_main = True 
    3592        self.closed = False 
     93         
     94        self.hooks = HookMap(['on_start_resource', 'before_request_body', 
     95                              'before_main', 'before_finalize', 
     96                              'on_end_resource', 'on_end_request', 
     97                              'before_error_response', 'after_error_response']) 
     98        self.hooks.failsafe = ['on_start_resource', 'on_end_resource', 
     99                               'on_end_request'] 
    36100     
    37101    def close(self): 
    38102        if not self.closed: 
    39103            self.closed = True 
    40             applyFilters('on_end_request', failsafe=True
     104            self.hooks.run('on_end_request'
    41105            cherrypy.serving.__dict__.clear() 
    42106     
     
    63127         
    64128        self.headers = httptools.HeaderMap() 
    65         self.headerMap = self.headers # Backward compatibility 
    66129        self.simple_cookie = Cookie.SimpleCookie() 
    67         self.simpleCookie = self.simple_cookie # Backward compatibility 
    68130         
    69131        if cherrypy.profiler: 
     
    87149            # right away. 
    88150            self.processRequestLine() 
     151            self.hooks.populate_from_config() 
    89152             
    90153            try: 
    91                 applyFilters('on_start_resource', failsafe=True
     154                self.hooks.run('on_start_resource'
    92155                 
    93156                try: 
    94157                    self.processHeaders() 
    95158                     
    96                     applyFilters('before_request_body') 
     159                    self.hooks.run('before_request_body') 
    97160                    if self.processRequestBody: 
    98161                        self.processBody() 
     
    101164                    while True: 
    102165                        try: 
    103                             applyFilters('before_main') 
     166                            self.hooks.run('before_main') 
    104167                            if self.execute_main: 
    105168                                self.main() 
     
    108171                            self.object_path = ir.path 
    109172                     
    110                     applyFilters('before_finalize') 
     173                    self.hooks.run('before_finalize') 
    111174                    cherrypy.response.finalize() 
    112175                except cherrypy.RequestHandled: 
     
    117180                    # we return the redirect or error page immediately 
    118181                    inst.set_response() 
    119                     applyFilters('before_finalize') 
     182                    self.hooks.run('before_finalize') 
    120183                    cherrypy.response.finalize() 
    121184            finally: 
    122                 applyFilters('on_end_resource', failsafe=True
     185                self.hooks.run('on_end_resource'
    123186        except (KeyboardInterrupt, SystemExit): 
    124187            raise 
     
    139202        self.path = path 
    140203        self.query_string = qs 
    141         self.queryString = qs # Backward compatibility 
    142204        self.protocol = proto 
    143205         
    144         # Change object_path in filters to change 
    145         # the object that will get rendered 
     206        # Change object_path to change the object that will get rendered 
    146207        self.object_path = path 
    147208         
     
    162223        # cherrypy.request.version == request.protocol in a Version instance. 
    163224        self.version = httptools.Version.from_http(self.protocol) 
     225         
     226        # cherrypy.response.version should be used to determine whether or 
     227        # not to include a given HTTP/1.1 feature in the response content. 
    164228        server_v = cherrypy.config.get("server.protocol_version", "HTTP/1.0") 
    165229        server_v = httptools.Version.from_http(server_v) 
    166          
    167         # cherrypy.response.version should be used to determine whether or 
    168         # not to include a given HTTP/1.1 feature in the response content. 
    169230        cherrypy.response.version = min(self.version, server_v) 
    170231     
     232    def main(self, path=None): 
     233        """Obtain and set cherrypy.response.body from a page handler.""" 
     234        dispatch = cherrypy.config.get("dispatcher") or Dispatcher() 
     235        handler = dispatch(path) 
     236        cherrypy.response.body = handler(*self.virtual_path, **self.params) 
     237     
    171238    def processHeaders(self): 
    172          
    173239        self.params = httptools.parseQueryString(self.query_string) 
    174         self.paramMap = self.params # Backward compatibility 
    175240         
    176241        # Process the headers into self.headers 
     
    186251            if name.title() == 'Cookie': 
    187252                self.simple_cookie.load(value) 
    188          
    189         # Save original values (in case they get modified by filters) 
    190         # This feature is deprecated in 2.2 and will be removed in 2.3. 
    191         self._original_params = self.params.copy() 
    192253         
    193254        if self.version >= "1.1": 
     
    200261        self.base = "%s://%s" % (self.scheme, self.headers.get('Host', '')) 
    201262     
    202     def _get_original_params(self): 
    203         # This feature is deprecated in 2.2 and will be removed in 2.3. 
    204         return self._original_params 
    205     original_params = property(_get_original_params, 
    206                         doc="Deprecated. A copy of the original params.") 
    207      
    208263    def _get_browser_url(self): 
    209264        url = self.base + self.path 
     
    213268    browser_url = property(_get_browser_url, 
    214269                          doc="The URL as entered in a browser (read-only).") 
    215     browserUrl = browser_url # Backward compatibility 
    216270     
    217271    def processBody(self): 
     
    238292        else: 
    239293            self.params.update(httptools.paramsFromCGIForm(forms)) 
    240      
    241     def main(self, path=None): 
    242         """Obtain and set cherrypy.response.body from a page handler.""" 
     294 
     295 
     296class Dispatcher(object): 
     297     
     298    def __call__(self, path=None): 
     299        """Find the appropriate page handler.""" 
     300        request = cherrypy.request 
    243301        if path is None: 
    244             path = self.object_path 
    245          
    246         page_handler, object_path, virtual_path = self.mapPathToObject(path) 
     302            path = request.object_path 
     303         
     304        handler, opath, vpath = self.find(request.browser_url, path) 
     305         
     306        # Remove "root" from opath and join it to get object_path 
     307        request.object_path = '/' + '/'.join(opath[1:]) 
    247308         
    248309        # Decode any leftover %2F in the virtual_path atoms. 
    249         virtual_path = [x.replace("%2F", "/") for x in virtual_path] 
    250          
    251         # Remove "root" from object_path and join it to get object_path 
    252         self.object_path = '/' + '/'.join(object_path[1:]) 
    253         try: 
    254             body = page_handler(*virtual_path, **self.params) 
    255         except Exception, x: 
    256             if hasattr(x, "args"): 
    257                 x.args = x.args + (page_handler,) 
    258             raise 
    259         cherrypy.response.body = body 
    260      
    261     def mapPathToObject(self, objectpath): 
    262         """For path, return the corresponding exposed callable (or raise NotFound). 
    263          
    264         path should be a "relative" URL path, like "/app/a/b/c". Leading and 
    265         trailing slashes are ignored. 
    266          
    267         Traverse path: 
    268         for /a/b?arg=val, we'll try: 
    269           root.a.b.index -> redirect to /a/b/?arg=val 
    270           root.a.b.default(arg='val') -> redirect to /a/b/?arg=val 
    271           root.a.b(arg='val') 
    272           root.a.default('b', arg='val') 
    273           root.default('a', 'b', arg='val') 
    274          
    275         The target method must have an ".exposed = True" attribute. 
    276         """ 
    277          
     310        request.virtual_path = [x.replace("%2F", "/") for x in vpath] 
     311         
     312        return handler 
     313     
     314    def find(self, browser_url, objectpath): 
    278315        objectTrail = _cputil.get_object_trail(objectpath) 
    279316        names = [name for name, candidate in objectTrail] 
     
    299336                    # had a trailing slash (otherwise, do a redirect). 
    300337                    if not objectpath.endswith('/'): 
    301                         atoms = self.browser_url.split("?", 1) 
     338                        atoms = browser_url.split("?", 1) 
    302339                        newUrl = atoms.pop(0) + '/' 
    303340                        if atoms: 
     
    360397         
    361398        self.headers = httptools.HeaderMap() 
    362         self.headerMap = self.headers # Backward compatibility 
    363399        content_type = cherrypy.config.get('server.default_content_type', 'text/html') 
    364400        self.headers.update({ 
     
    370406        }) 
    371407        self.simple_cookie = Cookie.SimpleCookie() 
    372         self.simpleCookie = self.simple_cookie # Backward compatibility 
    373408     
    374409    def collapse_body(self): 
     
    419454        """Set status, headers, and body when an unanticipated error occurs.""" 
    420455        try: 
    421             applyFilters('before_error_response') 
    422             
    423             # _cp_on_error will probably change self.body. 
    424             # It may also change the headers, etc. 
    425             _cputil.get_special_attribute('_cp_on_error', '_cpOnError')() 
    426              
     456            cherrypy.request.hooks.run('before_error_response') 
     457             
     458            self.error_response() 
    427459            self.finalize() 
    428460             
    429             applyFilters('after_error_response') 
     461            cherrypy.request.hooks.run('after_error_response') 
    430462            return 
    431463        except cherrypy.HTTPRedirect, inst: 
     
    445477            pass 
    446478         
    447         # Failure in _cp_on_error, error filter, or finalize. 
     479        # Failure in self.error_response, error hooks, or finalize. 
    448480        # Bypass them all. 
    449481        if cherrypy.config.get('server.show_tracebacks', False): 
     
    454486        self.setBareError(body) 
    455487     
     488    def error_response(self): 
     489        # Allow logging of only *unexpected* HTTPError's. 
     490        if (not cherrypy.config.get('server.log_tracebacks', True) 
     491            and cherrypy.config.get('server.log_unhandled_tracebacks', True)): 
     492            cherrypy.log(traceback=True) 
     493        cherrypy.HTTPError(500).set_response() 
     494     
    456495    def setBareError(self, body=None): 
    457496        self.status, self.header_list, self.body = _cputil.bareError(body) 
  • trunk/cherrypy/_cpserver.py

    r1004 r1047  
    55 
    66import cherrypy 
    7 from cherrypy import _cphttptools 
    87from cherrypy.lib import cptools 
    9  
    108from cherrypy._cpengine import Engine, STOPPED, STARTING, STARTED 
    119 
     
    2119         
    2220        self.httpserver = None 
    23         # Starting in 2.2, the "httpserverclass" attr is essentially dead; 
    24         # no CP code uses it. Inspect "httpserver" instead. 
    25         self.httpserverclass = None 
    26          
    27         # Backward compatibility: 
    28         self.onStopServerList = self.on_stop_server_list 
    29         self.onStartThreadList = self.on_start_thread_list 
    30         self.onStartServerList = self.on_start_server_list 
    31         self.onStopThreadList = self.on_stop_thread_list 
    3221     
    3322    def start(self, init_only=False, server_class=_missing, server=None, **kwargs): 
     
    3726        Set serverClass and server to None to skip starting any HTTP server. 
    3827        """ 
    39          
    40         # Read old variable names for backward compatibility 
    41         if 'initOnly' in kwargs: 
    42             init_only = kwargs['initOnly'] 
    43         if 'serverClass' in kwargs: 
    44             server_class = kwargs['serverClass'] 
    4528         
    4629        conf = cherrypy.config.get 
     
    6043                # Dynamically load the class from the given string 
    6144                server_class = cptools.attributes(server_class) 
    62             self.httpserverclass = server_class 
    6345            if server_class is not None: 
    6446                self.httpserver = server_class() 
     
    6648            if isinstance(server, basestring): 
    6749                server = cptools.attributes(server) 
    68             self.httpserverclass = server.__class__ 
    6950            self.httpserver = server 
    7051         
     
    182163        """Start, then callback the given func in a new thread.""" 
    183164         
    184         # Read old name for backward compatibility 
    185         if serverClass is not None: 
    186             server_class = None 
    187          
    188165        if args is None: 
    189166            args = () 
  • trunk/cherrypy/_cputil.py

    r1030 r1047  
    107107 
    108108def _cp_log_access(): 
    109     """ Default method for logging access """ 
     109    """Default method for logging access""" 
    110110     
    111111    tmpl = '%(h)s %(l)s %(u)s [%(t)s] "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' 
    112     s = tmpl % {'h': cherrypy.request.remoteHost, 
     112    s = tmpl % {'h': cherrypy.request.remote_host, 
    113113                'l': '-', 
    114114                'u': getattr(cherrypy.request, "login", None) or "-", 
     
    230230 
    231231def _cp_on_http_error(status, message): 
    232     """ Default _cp_on_http_error method. 
     232    """Default _cp_on_http_error method. 
    233233     
    234234    status should be an int. 
     
    362362            [body]) 
    363363 
    364 def _cp_on_error(): 
    365     """ Default _cp_on_error method """ 
    366     # Allow logging of only *unexpected* HTTPError's. 
    367     if (not cherrypy.config.get('server.log_tracebacks', True) 
    368         and cherrypy.config.get('server.log_unhandled_tracebacks', True)): 
    369         cherrypy.log(traceback=True) 
    370      
    371     cherrypy.HTTPError(500).set_response() 
    372  
    373364def headers(headers): 
    374365    """ Provides a simple way to add specific headers to page handler 
     
    387378    return wrapper 
    388379 
    389 _cp_filters = [] 
    390  
  • trunk/cherrypy/_cpwsgiserver.py

    r1037 r1047  
    218218class CherryPyWSGIServer(object): 
    219219     
    220     version = "CherryPy/2.2.0
     220    version = "CherryPy/3.0.0alpha
    221221    ready = False 
    222222    interrupt = None 
  • trunk/cherrypy/config.py

    r1004 r1047  
    33import ConfigParser 
    44import os 
     5_favicon_path = os.path.join(os.path.dirname(__file__), "favicon.ico") 
    56 
    67import cherrypy 
     
    1213# Keys are URL paths, and values are dicts. 
    1314configs = {} 
    14 configMap = configs # Backward compatibility 
    1515 
    1616default_global = { 
     
    2727    'server.environment': "development", 
    2828     
    29     '/favicon.ico': { 
    30         'static_filter.on': True, 
    31         'static_filter.file': os.path.join(os.path.dirname(__file__), "favicon.ico"),} 
     29    '/favicon.ico': {'hooks.static.on': True, 
     30                     'hooks.static.file': _favicon_path}, 
    3231    } 
    3332 
     
    3534    "development": { 
    3635        'autoreload.on': True, 
    37         'log_debug_info_filter.on': True, 
    3836        'server.log_file_not_found': True, 
    3937        'server.show_tracebacks': True, 
     
    4240    "staging": { 
    4341        'autoreload.on': False, 
    44         'log_debug_info_filter.on': False, 
    4542        'server.log_file_not_found': False, 
    4643        'server.show_tracebacks': False, 
     
    4946    "production": { 
    5047        'autoreload.on': False, 
    51         'log_debug_info_filter.on': False, 
    5248        'server.log_file_not_found': False, 
    5349        'server.show_tracebacks': False, 
     
    122118            path = "/" 
    123119         
    124         if cherrypy.lowercase_api is False: 
    125             # We don't know for sure if user uses the new lowercase API 
    126             try: 
    127                 result = configs[path][_cputil.lower_to_camel(key)] 
    128                 break 
    129             except KeyError: 
    130                 try: 
    131                     result = configs[path][key] 
    132                     break 
    133                 except KeyError: 
    134                     pass 
    135                 pass 
    136              
    137             try: 
    138                 # Check for a server.environment entry at this node. 
    139                 env = configs[path]["server.environment"] 
    140                 # For backward compatibility, check for camelCase key first 
    141                 result = environments[env][_cputil.lower_to_camel(key)] 
    142                 break 
    143             except KeyError: 
    144                 try: 
    145                     env = configs[path]["server.environment"] 
    146                     result = environments[env][key] 
    147                     break 
    148                 except KeyError: 
    149                     pass 
    150                 pass 
    151         else: 
    152             # We know for sure that user uses the new lowercase api 
    153             try: 
    154                 result = configs[path][key] 
    155                 break 
    156             except KeyError: 
    157                 pass 
    158              
    159             try: 
    160                 env = configs[path]["server.environment"] 
    161                 result = environments[env][key] 
    162                 break 
    163             except KeyError: 
    164                 pass 
     120        try: 
     121            result = configs[path][key] 
     122            break 
     123        except KeyError: 
    165124            pass 
     125         
     126        try: 
     127            env = configs[path]["server.environment"] 
     128            result = environments[env][key] 
     129            break 
     130        except KeyError: 
     131            pass 
     132        pass 
    166133 
    167134        if path == "global": 
     
    183150    else: 
    184151        return result 
    185  
    186 def getAll(key): 
    187     """Lookup key in the current node and all of its parent nodes 
    188     as a list of path, value pairs. 
    189     """ 
    190     # Needed by the session filter 
    191      
    192     try: 
    193         results = [('global', configs['global'][key])] 
    194     except KeyError: 
    195         results = [] 
    196      
    197     try: 
    198         path = cherrypy.request.object_path 
    199     except AttributeError: 
    200         return results 
    201      
    202     pathList = path.split('/') 
    203      
    204     for n in xrange(1, len(pathList)): 
    205         path = '/' + '/'.join(pathList[0:n+1]) 
    206         try: 
    207             results.append((path, configs[path][key])) 
    208         except KeyError: 
    209             pass 
    210      
    211     return results 
    212152 
    213153 
  • trunk/cherrypy/lib/cptools.py

    <
    r1045 r1047  
    22 
    33import inspect 
    4 import mimetools 
    5 import mimetypes 
    6 mimetypes.init() 
    7 mimetypes.types_map['.dwg']='image/x-dwg' 
    8 mimetypes.types_map['.ico']='image/x-icon' 
    9  
    104import os 
    115import sys 
     
    137 
    148import cherrypy 
    15 import httptools 
    16  
    17 from cherrypy.filters.wsgiappfilter import WSGIAppFilter 
     9 
    1810 
    1911 
     
    6456        return self.items[key] 
    6557 
    66 def modified_since(path, stat=None): 
    67     """Check whether a file has been modified since the date 
    68     provided in 'If-Modified-Since' 
    69     It doesn't check if the file exists or not 
    70     Return True if has been modified, False otherwise 
    71     """ 
    72     # serveFile already creates a stat object so let's not 
    73     # waste our energy to do it again 
    74     if not stat: 
    75         try: 
    76             stat = os.stat(path) 
    77         except OSError: 
    78             if cherrypy.config.get('server.log_file_not_found', False): 
    79                 cherrypy.log("    NOT FOUND file: %s" % path, "DEBUG") 
    80             raise cherrypy.NotFound() 
    81      
    82     response = cherrypy.response 
    83     strModifTime = httptools.HTTPDate(time.gmtime(stat.st_mtime)) 
    84     if cherrypy.request.headers.has_key('If-Modified-Since'): 
    85         if cherrypy.request.headers['If-Modified-Since'] == strModifTime: 
    86             response.status = "304 Not Modified" 
    87             response.body = None 
    88             if getattr(cherrypy, "debug", None): 
    89                 cherrypy.log("    Found file (304 Not Modified): %s" % path, "DEBUG") 
    90             return False 
    91     response.headers['Last-Modified'] = strModifTime 
    92     return True 
    93      
    94 def serveFile(path, contentType=None, disposition=None, name=None): 
    95     """Set status, headers, and body in order to serve the given file. 
    96      
    97     The Content-Type header will be set to the contentType arg, if provided. 
    98     If not provided, the Content-Type will be guessed by its extension. 
    99      
    100     If disposition is not None, the Content-Disposition header will be set 
    101     to "<disposition>; filename=<name>". If name is None, it will be set 
    102     to the basename of path. If disposition is None, no Content-Disposition 
    103     header will be written. 
    104     """ 
    105      
    106     response = cherrypy.response 
    107      
    108     # If path is relative, users should fix it by making path absolute. 
    109     # That is, CherryPy should not guess where the application root is. 
    110     # It certainly should *not* use cwd (since CP may be invoked from a 
    111     # variety of paths). If using static_filter, you can make your relative 
    112     # paths become absolute by supplying a value for "static_filter.root". 
    113     if not os.path.isabs(path): 
    114         raise ValueError("'%s' is not an absolute path." % path) 
    115      
    116     try: 
    117         stat = os.stat(path) 
    118     except OSError: 
    119         if cherrypy.config.get('server.log_file_not_found', False): 
    120             cherrypy.log("    NOT FOUND file: %s" % path, "DEBUG") 
    121         raise cherrypy.NotFound() 
    122      
    123     if os.path.isdir(path): 
    124         # Let the caller deal with it as they like. 
    125         raise cherrypy.NotFound() 
    126      
    127     if contentType is None: 
    128         # Set content-type based on filename extension 
    129         ext = "" 
    130         i = path.rfind('.') 
    131         if i != -1: 
    132             ext = path[i:].lower() 
    133         contentType = mimetypes.types_map.get(ext, "text/plain") 
    134     response.headers['Content-Type'] = contentType 
    135      
    136     if not modified_since(path, stat): 
    137         return [] 
    138      
    139     if disposition is not None: 
    140         if name is None: 
    141             name = os.path.basename(path) 
    142         cd = "%s; filename=%s" % (disposition, name) 
    143         response.headers["Content-Disposition"] = cd 
    144      
    145     # Set Content-Length and use an iterable (file object) 
    146     #   this way CP won't load the whole file in memory 
    147     c_len = stat.st_size 
    148     bodyfile = open(path, 'rb') 
    149     if getattr(cherrypy, "debug", None): 
    150         cherrypy.log("    Found file: %s" % path, "DEBUG") 
    151      
    152     # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code 
    153     if cherrypy.response.version >= "1.1": 
    154         response.headers["Accept-Ranges"] = "bytes" 
    155         r = httptools.getRanges(cherrypy.request.headers.get('Range'), c_len) 
    156         if r == []: 
    157             response.headers['Content-Range'] = "bytes */%s" % c_len 
    158             message = "Invalid Range (first-byte-pos greater than Content-Length)" 
    159             raise cherrypy.HTTPError(416, message) 
    160         if r: 
    161             if len(r) == 1: 
    162                 # Return a single-part response. 
    163                 start, stop = r[0] 
    164                 r_len = stop - start 
    165                 response.status = "206 Partial Content" 
    166                 response.headers['Content-Range'] = ("bytes %s-%s/%s" % 
    167                                                        (start, stop - 1, c_len)) 
    168                 response.headers['Content-Length'] = r_len 
    169                 bodyfile.seek(start) 
    170                 response.body = bodyfile.read(r_len) 
    171             else: 
    172                 # Return a multipart/byteranges response. 
    173                 response.status = "206 Partial Content" 
    174                 boundary = mimetools.choose_boundary() 
    175                 ct = "multipart/byteranges; boundary=%s" % boundary 
    176                 response.headers['Content-Type'] = ct 
    177 ##                del response.headers['Content-Length'] 
    178                  
    179                 def fileRanges(): 
    180                     for start, stop in r: 
    181                         yield "--" + boundary 
    182                         yield "\nContent-type: %s" % contentType 
    183                         yield ("\nContent-range: bytes %s-%s/%s\n\n" 
    184                                % (start, stop - 1, c_len)) 
    185                         bodyfile.seek(start) 
    186                         yield bodyfile.read((stop + 1) - start) 
    187                         yield "\n" 
    188                     # Final boundary 
    189                     yield "--" + boundary 
    190                 response.body = fileRanges() 
    191         else: 
    192             response.headers['Content-Length'] = c_len 
    193             response.body = bodyfile 
    194     else: 
    195         response.headers['Content-Length'] = c_len 
    196         response.body = bodyfile 
    197     return response.body 
    19858 
    19959def fileGenerator(input, chunkSize=65536): 
     
    20464        chunk = input.read(chunkSize) 
    20565    input.close() 
     66 
    20667 
    20768def modules(modulePath): 
     
    23697 
    23798 
    238 class WSGIApp(object): 
    239     """a convenience class that uses the WSGIAppFilter 
    240      
    241     to easily add a WSGI application to the CP object tree. 
    242  
    243     example: 
    244     cherrypy.tree.mount(SomeRoot(), '/') 
    245     cherrypy.tree.mount(WSGIApp(other_wsgi_app), '/ext_app') 
    246     """ 
    247     def __init__(self, app, env_update=None): 
    248         self._cpFilterList = [WSGIAppFilter(app, env_update)] 
    249  
    250  
    25199# public domain "unrepr" implementation, found on the web and then improved. 
    252100import compiler 
     
    329177    return Builder().build(getObj(s)) 
    330178 
     179 
     180# Old filter code 
     181 
     182def base_url(base=None, use_x_forwarded_host=True): 
     183    """Change the base URL. 
     184     
     185    Useful when running a CP server behind Apache. 
     186    """ 
     187     
     188    request = cherrypy.request 
     189     
     190    if base is None: 
     191        port = str(cherrypy.config.get('server.socket_port', '80')) 
     192        if port == "80": 
     193            base = 'http://localhost' 
     194        else: 
     195            base = 'http://localhost:%s' % port