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

Changeset 1202

Show
Ignore:
Timestamp:
07/17/06 10:21:56
Author:
dowski
Message:

1. New "expires" tool for setting the "Expires" header.

2. Tests for the expires tool.

Files:

Legend:

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

    r1196 r1202  
    307307default_toolbox.wsgiapp = WSGIAppTool(_wsgiapp.run) 
    308308default_toolbox.caching = CachingTool() 
     309default_toolbox.expires = Tool('before_finalize', _caching.expires) 
    309310default_toolbox.tidy = Tool('before_finalize', tidy.tidy) 
    310311default_toolbox.nsgmls = Tool('before_finalize', tidy.nsgmls) 
  • trunk/cherrypy/lib/caching.py

    r1198 r1202  
     1import datetime 
    12import threading 
    23import time 
    34 
    45import cherrypy 
    5 from cherrypy.lib import cptools 
     6from cherrypy.lib import cptools, http 
    67 
    78 
     
    166167    cherrypy.request.hooks.attach('before_main', _wrapper) 
    167168 
     169def expires(e_time=0, ignore_indicators=False, force=True): 
     170    """Tool for influencing cache mechanisms using the 'Expires' header. 
     171 
     172    e_time can either be a datetime.datetime instance or number zero. 
     173 
     174    If a datetime.datetime instance is supplied, the 'Expires' header will be 
     175    set to that value. 
     176 
     177    If a number zero is supplied, the following "cache prevention" headers 
     178    are set: 
     179       'Expires': '0' 
     180       'Pragma': 'no-cache' 
     181       'Cache-Control: 'no-cache' 
     182 
     183    By default, if e_time is set to zero, the tool checks to see if the 
     184    response already includes some common "cacheability indicator" headers. 
     185    If they are present, the "cache prevention" headers are not set.  This 
     186    behavior can be overridden by setting the ignore_indicators parameter 
     187    to True. 
     188 
     189    Setting force to False will keep the tool from overwriting any headers 
     190    that are already present in the response. 
     191    """ 
     192 
     193    if e_time and isinstance(e_time, datetime.datetime): 
     194        e_time = http.HTTPDate(time.mktime(e_time.timetuple())) 
     195        cptools.response_headers([("Expires", e_time)], force) 
     196        return 
     197 
     198    try: 
     199        if not(int(e_time)): 
     200            # some header names that indicate that the response can be cached 
     201            indicators = set(('Etag', 'Last-Modified', 'Age', 'Expires')) 
     202            cacheable = indicators.intersection(set(cherrypy.response.headers)) 
     203            if not e_time and (ignore_indicators or not cacheable): 
     204                cptools.response_headers([("Pragma", "no-cache"), 
     205                                          ("Expires", "0")], force) 
     206                if cherrypy.request.version >= (1, 1): 
     207                    cptools.response_headers([("Cache-Control", "no-cache")], force) 
     208 
     209        else: 
     210            raise ValueError("e_time value must be number zero or a datetime." 
     211                             "datetime instance") 
     212 
     213    except TypeError, e: 
     214        e.args = ["e_time value must be datetime.datetime instance or number" 
     215                  "zero."] 
     216        raise 
  • trunk/cherrypy/test/test_caching.py

    r1187 r1202  
    22test.prefer_parent_path() 
    33 
     4import os 
     5curdir = os.path.join(os.getcwd(), os.path.dirname(__file__)) 
     6 
    47import cherrypy 
     8import datetime 
    59import time 
    610 
     
    1923            return msg 
    2024        index.exposed = True 
    21      
     25 
     26    class UnCached(object): 
     27        _cp_config = {'tools.expires.on': True, 
     28                      'tools.staticdir.on': True, 
     29                      'tools.staticdir.dir': 'static', 
     30                      'tools.staticdir.root': curdir, 
     31                      } 
     32 
     33        def gentle(self): 
     34            self._cp_config['tools.expires.force'] = False 
     35            return "being forceful" 
     36        gentle.exposed = True 
     37 
     38        def ignorant(self): 
     39            self._cp_config['tools.expires.ignore_indicators'] = True 
     40            return "being ignorant" 
     41        ignorant.exposed = True 
     42 
     43        def dynamic(self): 
     44            cherrypy.response.headers['Cache-Control'] = 'private' 
     45            return "D-d-d-dynamic!" 
     46        dynamic.exposed = True 
     47 
     48        def cacheable(self): 
     49            cherrypy.response.headers['Etag'] = 'bibbitybobbityboo' 
     50            return "Hi, I'm cacheable." 
     51        cacheable.exposed = True 
     52 
     53        expire_on = datetime.datetime(2006, 7, 17, 8, 55, 59, 171000) 
     54 
     55        def specific(self): 
     56            return "I am being specific" 
     57        specific.exposed = True 
     58        specific._cp_config = {'tools.expires.e_time': expire_on} 
     59 
     60        class Foo(object):pass 
     61         
     62        def wrongtype(self): 
     63            return "Woops" 
     64        wrongtype.exposed = True 
     65        wrongtype._cp_config = {'tools.expires.e_time': Foo()} 
     66 
     67        def wrongvalue(self): 
     68            return "Uh oh" 
     69        wrongvalue.exposed = True 
     70        wrongvalue._cp_config = {'tools.expires.e_time': 42} 
     71 
     72 
    2273    cherrypy.tree.mount(Root()) 
     74    cherrypy.tree.mount(UnCached(), "/expires") 
    2375    cherrypy.config.update({ 
    2476        'log_to_screen': False, 
    2577        'environment': 'production', 
     78        'show_tracebacks': True, 
    2679    }) 
    2780 
     
    3083 
    3184class CacheTest(helper.CPWebCase): 
    32      
     85 
    3386    def testCaching(self): 
    3487        elapsed = 0.0 
     
    51104        self.assertBody('visit #3') 
    52105 
     106    def testExpiresTool(self): 
     107 
     108        # test setting a specific expires header 
     109        self.getPage("/expires/specific") 
     110        self.assertStatus("200 OK") 
     111        self.assertHeader("Expires", "Mon, 17 Jul 2006 12:55:59 GMT") 
     112 
     113        # test exceptions for bad e_time values 
     114        self.getPage("/expires/wrongtype") 
     115        self.assertStatus("500 Internal error") 
     116        self.assertInBody("TypeError") 
     117 
     118        self.getPage("/expires/wrongvalue") 
     119        self.assertStatus("500 Internal error") 
     120        self.assertInBody("ValueError") 
     121 
     122        self.getPage('/expires/dynamic') 
     123        self.assertBody("D-d-d-dynamic!") 
     124        # dynamic sets Cache-Control to private but it should  be 
     125        # overwritten here ... 
     126        self.assertHeader("Cache-Control", "no-cache") 
     127        self.assertHeader("Expires", "0") 
     128        self.assertHeader("Pragma", "no-cache") 
     129 
     130        # configure the tool to keep existing headers 
     131        self.getPage("/expires/gentle") 
     132        self.assertStatus("200 OK") 
     133 
     134        self.getPage('/expires/dynamic') 
     135        self.assertBody("D-d-d-dynamic!") 
     136        # the Cache-Control header should now be untouched 
     137        self.assertHeader("Cache-Control", "private") 
     138 
     139        # static content should not have "cache prevention" headers 
     140        self.getPage("/expires/index.html") 
     141        self.assertStatus("200 OK") 
     142        self.assertNoHeader("Pragma") 
     143        self.assertNoHeader("Cache-Control") 
     144        self.assertNoHeader("Expires") 
     145 
     146        # dynamic content that sets indicators should not have 
     147        # "cache prevention" headers 
     148        self.getPage("/expires/cacheable") 
     149        self.assertStatus("200 OK") 
     150        self.assertNoHeader("Pragma") 
     151        self.assertNoHeader("Cache-Control") 
     152        self.assertNoHeader("Expires") 
     153 
     154        # configure the tool to ignore indicators 
     155        self.getPage("/expires/ignorant") 
     156        self.assertStatus("200 OK") 
     157 
     158        # static content should now have "cache prevention" headers 
     159        self.getPage("/expires/index.html") 
     160        self.assertStatus("200 OK") 
     161        self.assertHeader("Pragma", "no-cache") 
     162        self.assertHeader("Cache-Control", "no-cache") 
     163        self.assertHeader("Expires", "0") 
     164 
     165        # the cacheable handler should now have "cache prevention" headers 
     166        self.getPage("/expires/cacheable") 
     167        self.assertStatus("200 OK") 
     168        self.assertHeader("Pragma", "no-cache") 
     169        self.assertHeader("Cache-Control", "no-cache") 
     170        self.assertHeader("Expires", "0") 
    53171 
    54172if __name__ == '__main__': 

Hosted by WebFaction

Log in as guest/cpguest to create tickets