Home | Trees | Indices | Help |
|
---|
|
1 import datetime 2 import gzip 3 from itertools import count 4 import os 5 curdir = os.path.join(os.getcwd(), os.path.dirname(__file__)) 6 import sys 7 import threading 8 import time 9 import urllib 10 11 import cherrypy 12 from cherrypy._cpcompat import next, ntob, quote, xrange 13 from cherrypy.lib import httputil 14 15 gif_bytes = ntob( 16 'GIF89a\x01\x00\x01\x00\x82\x00\x01\x99"\x1e\x00\x00\x00\x00\x00' 17 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 18 '\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x02\x03\x02\x08\t\x00;' 19 ) 20 21 22 from cherrypy.test import helper 23 242642 index.exposed = True 43 44 def control(self): 45 self.control_counter += 1 46 return "visit #%s" % self.control_counter 47 control.exposed = True 48 49 def a_gif(self): 50 cherrypy.response.headers[ 51 'Last-Modified'] = httputil.HTTPDate() 52 return gif_bytes 53 a_gif.exposed = True 54 55 def long_process(self, seconds='1'): 56 try: 57 self.longlock.acquire() 58 time.sleep(float(seconds)) 59 finally: 60 self.longlock.release() 61 return 'success!' 62 long_process.exposed = True 63 64 def clear_cache(self, path): 65 cherrypy._cache.store[cherrypy.request.base + path].clear() 66 clear_cache.exposed = True 67 68 class VaryHeaderCachingServer(object): 69 70 _cp_config = { 71 'tools.caching.on': True, 72 'tools.response_headers.on': True, 73 'tools.response_headers.headers': [ 74 ('Vary', 'Our-Varying-Header') 75 ], 76 } 77 78 def __init__(self): 79 self.counter = count(1) 80 81 def index(self): 82 return "visit #%s" % next(self.counter) 83 index.exposed = True 84 85 class UnCached(object): 86 _cp_config = {'tools.expires.on': True, 87 'tools.expires.secs': 60, 88 'tools.staticdir.on': True, 89 'tools.staticdir.dir': 'static', 90 'tools.staticdir.root': curdir, 91 } 92 93 def force(self): 94 cherrypy.response.headers['Etag'] = 'bibbitybobbityboo' 95 self._cp_config['tools.expires.force'] = True 96 self._cp_config['tools.expires.secs'] = 0 97 return "being forceful" 98 force.exposed = True 99 force._cp_config = {'tools.expires.secs': 0} 100 101 def dynamic(self): 102 cherrypy.response.headers['Etag'] = 'bibbitybobbityboo' 103 cherrypy.response.headers['Cache-Control'] = 'private' 104 return "D-d-d-dynamic!" 105 dynamic.exposed = True 106 107 def cacheable(self): 108 cherrypy.response.headers['Etag'] = 'bibbitybobbityboo' 109 return "Hi, I'm cacheable." 110 cacheable.exposed = True 111 112 def specific(self): 113 cherrypy.response.headers[ 114 'Etag'] = 'need_this_to_make_me_cacheable' 115 return "I am being specific" 116 specific.exposed = True 117 specific._cp_config = {'tools.expires.secs': 86400} 118 119 class Foo(object): 120 pass 121 122 def wrongtype(self): 123 cherrypy.response.headers[ 124 'Etag'] = 'need_this_to_make_me_cacheable' 125 return "Woops" 126 wrongtype.exposed = True 127 wrongtype._cp_config = {'tools.expires.secs': Foo()} 128 129 cherrypy.tree.mount(Root()) 130 cherrypy.tree.mount(UnCached(), "/expires") 131 cherrypy.tree.mount(VaryHeaderCachingServer(), "/varying_headers") 132 cherrypy.config.update({'tools.gzip.on': True}) 133 setup_server = staticmethod(setup_server) 13428 29 class Root: 30 31 _cp_config = {'tools.caching.on': True} 32 33 def __init__(self): 34 self.counter = 0 35 self.control_counter = 0 36 self.longlock = threading.Lock()37 38 def index(self): 39 self.counter += 1 40 msg = "visit #%s" % self.counter 41 return msg136 elapsed = 0.0 137 for trial in range(10): 138 self.getPage("/") 139 # The response should be the same every time, 140 # except for the Age response header. 141 self.assertBody('visit #1') 142 if trial != 0: 143 age = int(self.assertHeader("Age")) 144 self.assert_(age >= elapsed) 145 elapsed = age 146 147 # POST, PUT, DELETE should not be cached. 148 self.getPage("/", method="POST") 149 self.assertBody('visit #2') 150 # Because gzip is turned on, the Vary header should always Vary for 151 # content-encoding 152 self.assertHeader('Vary', 'Accept-Encoding') 153 # The previous request should have invalidated the cache, 154 # so this request will recalc the response. 155 self.getPage("/", method="GET") 156 self.assertBody('visit #3') 157 # ...but this request should get the cached copy. 158 self.getPage("/", method="GET") 159 self.assertBody('visit #3') 160 self.getPage("/", method="DELETE") 161 self.assertBody('visit #4') 162 163 # The previous request should have invalidated the cache, 164 # so this request will recalc the response. 165 self.getPage("/", method="GET", headers=[('Accept-Encoding', 'gzip')]) 166 self.assertHeader('Content-Encoding', 'gzip') 167 self.assertHeader('Vary') 168 self.assertEqual( 169 cherrypy.lib.encoding.decompress(self.body), ntob("visit #5")) 170 171 # Now check that a second request gets the gzip header and gzipped body 172 # This also tests a bug in 3.0 to 3.0.2 whereby the cached, gzipped 173 # response body was being gzipped a second time. 174 self.getPage("/", method="GET", headers=[('Accept-Encoding', 'gzip')]) 175 self.assertHeader('Content-Encoding', 'gzip') 176 self.assertEqual( 177 cherrypy.lib.encoding.decompress(self.body), ntob("visit #5")) 178 179 # Now check that a third request that doesn't accept gzip 180 # skips the cache (because the 'Vary' header denies it). 181 self.getPage("/", method="GET") 182 self.assertNoHeader('Content-Encoding') 183 self.assertBody('visit #6')184186 self.getPage("/varying_headers/") 187 self.assertStatus("200 OK") 188 self.assertHeaderItemValue('Vary', 'Our-Varying-Header') 189 self.assertBody('visit #1') 190 191 # Now check that different 'Vary'-fields don't evict each other. 192 # This test creates 2 requests with different 'Our-Varying-Header' 193 # and then tests if the first one still exists. 194 self.getPage("/varying_headers/", 195 headers=[('Our-Varying-Header', 'request 2')]) 196 self.assertStatus("200 OK") 197 self.assertBody('visit #2') 198 199 self.getPage("/varying_headers/", 200 headers=[('Our-Varying-Header', 'request 2')]) 201 self.assertStatus("200 OK") 202 self.assertBody('visit #2') 203 204 self.getPage("/varying_headers/") 205 self.assertStatus("200 OK") 206 self.assertBody('visit #1')207209 # test setting an expires header 210 self.getPage("/expires/specific") 211 self.assertStatus("200 OK") 212 self.assertHeader("Expires") 213 214 # test exceptions for bad time values 215 self.getPage("/expires/wrongtype") 216 self.assertStatus(500) 217 self.assertInBody("TypeError") 218 219 # static content should not have "cache prevention" headers 220 self.getPage("/expires/index.html") 221 self.assertStatus("200 OK") 222 self.assertNoHeader("Pragma") 223 self.assertNoHeader("Cache-Control") 224 self.assertHeader("Expires") 225 226 # dynamic content that sets indicators should not have 227 # "cache prevention" headers 228 self.getPage("/expires/cacheable") 229 self.assertStatus("200 OK") 230 self.assertNoHeader("Pragma") 231 self.assertNoHeader("Cache-Control") 232 self.assertHeader("Expires") 233 234 self.getPage('/expires/dynamic') 235 self.assertBody("D-d-d-dynamic!") 236 # the Cache-Control header should be untouched 237 self.assertHeader("Cache-Control", "private") 238 self.assertHeader("Expires") 239 240 # configure the tool to ignore indicators and replace existing headers 241 self.getPage("/expires/force") 242 self.assertStatus("200 OK") 243 # This also gives us a chance to test 0 expiry with no other headers 244 self.assertHeader("Pragma", "no-cache") 245 if cherrypy.server.protocol_version == "HTTP/1.1": 246 self.assertHeader("Cache-Control", "no-cache, must-revalidate") 247 self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT") 248 249 # static content should now have "cache prevention" headers 250 self.getPage("/expires/index.html") 251 self.assertStatus("200 OK") 252 self.assertHeader("Pragma", "no-cache") 253 if cherrypy.server.protocol_version == "HTTP/1.1": 254 self.assertHeader("Cache-Control", "no-cache, must-revalidate") 255 self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT") 256 257 # the cacheable handler should now have "cache prevention" headers 258 self.getPage("/expires/cacheable") 259 self.assertStatus("200 OK") 260 self.assertHeader("Pragma", "no-cache") 261 if cherrypy.server.protocol_version == "HTTP/1.1": 262 self.assertHeader("Cache-Control", "no-cache, must-revalidate") 263 self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT") 264 265 self.getPage('/expires/dynamic') 266 self.assertBody("D-d-d-dynamic!") 267 # dynamic sets Cache-Control to private but it should be 268 # overwritten here ... 269 self.assertHeader("Pragma", "no-cache") 270 if cherrypy.server.protocol_version == "HTTP/1.1": 271 self.assertHeader("Cache-Control", "no-cache, must-revalidate") 272 self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")273275 self.getPage("/a.gif") 276 self.assertStatus(200) 277 self.assertBody(gif_bytes) 278 lm1 = self.assertHeader("Last-Modified") 279 280 # this request should get the cached copy. 281 self.getPage("/a.gif") 282 self.assertStatus(200) 283 self.assertBody(gif_bytes) 284 self.assertHeader("Age") 285 lm2 = self.assertHeader("Last-Modified") 286 self.assertEqual(lm1, lm2) 287 288 # this request should match the cached copy, but raise 304. 289 self.getPage("/a.gif", [('If-Modified-Since', lm1)]) 290 self.assertStatus(304) 291 self.assertNoHeader("Last-Modified") 292 if not getattr(cherrypy.server, "using_apache", False): 293 self.assertHeader("Age")294296 SECONDS = 4 297 # We MUST make an initial synchronous request in order to create the 298 # AntiStampedeCache object, and populate its selecting_headers, 299 # before the actual stampede. 300 self.getPage("/long_process?seconds=%d" % SECONDS) 301 self.assertBody('success!') 302 self.getPage("/clear_cache?path=" + 303 quote('/long_process?seconds=%d' % SECONDS, safe='')) 304 self.assertStatus(200) 305 306 start = datetime.datetime.now() 307 308 def run(): 309 self.getPage("/long_process?seconds=%d" % SECONDS) 310 # The response should be the same every time 311 self.assertBody('success!')312 ts = [threading.Thread(target=run) for i in xrange(100)] 313 for t in ts: 314 t.start() 315 for t in ts: 316 t.join() 317 self.assertEqualDates(start, datetime.datetime.now(), 318 # Allow a second (two, for slow hosts) 319 # for our thread/TCP overhead etc. 320 seconds=SECONDS + 2) 321323 self.getPage("/control") 324 self.assertBody('visit #1') 325 self.getPage("/control") 326 self.assertBody('visit #1') 327 328 self.getPage("/control", headers=[('Cache-Control', 'no-cache')]) 329 self.assertBody('visit #2') 330 self.getPage("/control") 331 self.assertBody('visit #2') 332 333 self.getPage("/control", headers=[('Pragma', 'no-cache')]) 334 self.assertBody('visit #3') 335 self.getPage("/control") 336 self.assertBody('visit #3') 337 338 time.sleep(1) 339 self.getPage("/control", headers=[('Cache-Control', 'max-age=0')]) 340 self.assertBody('visit #4') 341 self.getPage("/control") 342 self.assertBody('visit #4')343
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Fri Jun 20 20:23:41 2014 | http://epydoc.sourceforge.net |