Changeset 1203
- Timestamp:
- 07/17/06 16:34:54
- Files:
-
- trunk/cherrypy/_cperror.py (modified) (1 diff)
- trunk/cherrypy/_cprequest.py (modified) (3 diffs)
- trunk/cherrypy/lib/caching.py (modified) (1 diff)
- trunk/cherrypy/lib/cptools.py (modified) (1 diff)
- trunk/cherrypy/test/test_caching.py (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cperror.py
r1141 r1203 70 70 71 71 # RFC 2616 indicates a 301 response code fits our goal; however, 72 # browser support for 301 is quite messy. Do 302 instead. See72 # browser support for 301 is quite messy. Do 302/303 instead. See 73 73 # http://ppewww.ph.gla.ac.uk/~flavell/www/post-redirect.html 74 74 if status is None: trunk/cherrypy/_cprequest.py
r1201 r1203 4 4 import os 5 5 import sys 6 import time 6 7 import types 7 8 … … 642 643 self.header_list = None 643 644 self.body = None 645 self.time = time.time() 644 646 645 647 self.headers = http.HeaderMap() … … 650 652 "Content-Type": content_type, 651 653 "Server": "CherryPy/" + cherrypy.__version__, 652 "Date": http.HTTPDate( ),654 "Date": http.HTTPDate(self.time), 653 655 "Set-Cookie": [], 654 656 "Content-Length": None trunk/cherrypy/lib/caching.py
r1202 r1203 167 167 cherrypy.request.hooks.attach('before_main', _wrapper) 168 168 169 def expires( e_time=0, ignore_indicators=False, force=True):169 def expires(secs=0, force=False): 170 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' 171 172 'secs' must be either an int or a datetime.timedelta, and indicates the 173 number of seconds between response.time and when the response should 174 expire. The 'Expires' header will be set to (response.time + secs). 175 176 If 'secs' is zero, the following "cache prevention" headers are also set: 180 177 '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. 178 'Cache-Control': 'no-cache' 179 180 If 'force' is False (the default), the following headers are checked: 181 'Etag', 'Last-Modified', 'Age', 'Expires'. If any are already present, 182 none of the "cache prevention" headers are set. 191 183 """ 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)): 184 185 if isinstance(secs, datetime.timedelta): 186 secs = (86400 * secs.days) + secs.seconds 187 expiry = http.HTTPDate(cherrypy.response.time + secs) 188 cptools.response_headers([("Expires", expiry)], force) 189 190 if secs == 0: 191 cacheable = False 192 if not force: 200 193 # 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 194 for indicator in ('Etag', 'Last-Modified', 'Age', 'Expires'): 195 if indicator in cherrypy.response.headers: 196 cacheable = True 197 break 198 if not cacheable: 199 cptools.response_headers([("Pragma", "no-cache")], force) 200 if cherrypy.request.version >= (1, 1): 201 cptools.response_headers([("Cache-Control", "no-cache")], force) trunk/cherrypy/lib/cptools.py
r1171 r1203 63 63 64 64 def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For'): 65 """Change the base URL (scheme://host[:port] ).65 """Change the base URL (scheme://host[:port][/path]). 66 66 67 67 Useful when running a CP server behind Apache. 68 69 If you want the new request.base to include path info (not just the host), 70 you must explicitly set base to the full base path, and ALSO set 'local' 71 to '', so that the X-Forwarded-Host request header (which never includes 72 path info) does not override it. 68 73 """ 69 74 trunk/cherrypy/test/test_caching.py
r1202 r1203 6 6 7 7 import cherrypy 8 import datetime9 import time10 8 11 9 12 10 def setup_server(): 11 13 12 class Root: 14 13 … … 31 30 } 32 31 33 def gentle(self):34 self._cp_config['tools.expires.force'] = False32 def force(self): 33 self._cp_config['tools.expires.force'] = True 35 34 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 35 force.exposed = True 42 36 43 37 def dynamic(self): … … 51 45 cacheable.exposed = True 52 46 53 expire_on = datetime.datetime(2006, 7, 17, 8, 55, 59, 171000)54 55 47 def specific(self): 56 48 return "I am being specific" 57 49 specific.exposed = True 58 specific._cp_config = {'tools.expires. e_time': expire_on}50 specific._cp_config = {'tools.expires.secs': 86400} 59 51 60 52 class Foo(object):pass … … 63 55 return "Woops" 64 56 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 57 wrongtype._cp_config = {'tools.expires.secs': Foo()} 58 73 59 cherrypy.tree.mount(Root()) 74 60 cherrypy.tree.mount(UnCached(), "/expires") … … 105 91 106 92 def testExpiresTool(self): 107 108 # test setting a specificexpires header93 94 # test setting an expires header 109 95 self.getPage("/expires/specific") 110 96 self.assertStatus("200 OK") 111 self.assertHeader("Expires" , "Mon, 17 Jul 2006 12:55:59 GMT")112 113 # test exceptions for bad e_time values97 self.assertHeader("Expires") 98 99 # test exceptions for bad time values 114 100 self.getPage("/expires/wrongtype") 115 101 self.assertStatus("500 Internal error") 116 102 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 103 139 104 # static content should not have "cache prevention" headers 140 105 self.getPage("/expires/index.html") … … 142 107 self.assertNoHeader("Pragma") 143 108 self.assertNoHeader("Cache-Control") 144 self.assertNoHeader("Expires") 145 109 146 110 # dynamic content that sets indicators should not have 147 111 # "cache prevention" headers … … 150 114 self.assertNoHeader("Pragma") 151 115 self.assertNoHeader("Cache-Control") 152 self.assertNoHeader("Expires") 153 154 # configure the tool to ignore indicators 155 self.getPage("/expires/ignorant") 116 117 self.getPage('/expires/dynamic') 118 self.assertBody("D-d-d-dynamic!") 119 # the Cache-Control header should be untouched 120 self.assertHeader("Cache-Control", "private") 121 122 # configure the tool to ignore indicators and replace existing headers 123 self.getPage("/expires/force") 156 124 self.assertStatus("200 OK") 157 125 158 126 # static content should now have "cache prevention" headers 159 127 self.getPage("/expires/index.html") … … 161 129 self.assertHeader("Pragma", "no-cache") 162 130 self.assertHeader("Cache-Control", "no-cache") 163 self.assertHeader("Expires", "0") 164 131 d = self.assertHeader("Date") 132 self.assertHeader("Expires", d) 133 165 134 # the cacheable handler should now have "cache prevention" headers 166 135 self.getPage("/expires/cacheable") … … 168 137 self.assertHeader("Pragma", "no-cache") 169 138 self.assertHeader("Cache-Control", "no-cache") 170 self.assertHeader("Expires", "0") 139 d = self.assertHeader("Date") 140 self.assertHeader("Expires", d) 141 142 self.getPage('/expires/dynamic') 143 self.assertBody("D-d-d-dynamic!") 144 # dynamic sets Cache-Control to private but it should be 145 # overwritten here ... 146 self.assertHeader("Pragma", "no-cache") 147 self.assertHeader("Cache-Control", "no-cache") 148 d = self.assertHeader("Date") 149 self.assertHeader("Expires", d) 171 150 172 151 if __name__ == '__main__':

