Anonymous avatar Anonymous committed 1ce4dbf

Backport of [1187] to 2.x branch. (Age header in cached responses). See #567.

Comments (0)

Files changed (2)

cherrypy/filters/cachefilter.py

             return
         
         request = cherrypy.request
+        response = cherrypy.response
         
         # POST, PUT, DELETE should invalidate (delete) the cached copy.
         # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10.
         if cacheData:
             # found a hit! check the if-modified-since request header
             expirationTime, lastModified, obj = cacheData
-            s, h, b = obj
+            s, h, b, create_time = obj
             modifiedSince = request.headers.get('If-Modified-Since', None)
             if modifiedSince is not None and modifiedSince == lastModified:
                 cherrypy._cache.totNonModified += 1
-                cherrypy.response.status = "304 Not Modified"
-                ct = h.get("Content-Type")
+                response.status = "304 Not Modified"
+                ct = h.get('Content-Type', None)
                 if ct:
-                    cherrypy.response.header_list["Content-Type"] = ct
-                cherrypy.response.body = None
+                    response.headers['Content-Type'] = ct
+                response.body = None
             else:
                 # serve it & get out from the request
                 response = cherrypy.response
-                response.status, response.header_list, response.body = s, h, b
-            raise cherrypy.RequestHandled()
+                response.status, response.headers, response.body = s, h, b
+            response.headers['Age'] = str(int(time.time() - create_time))
+            request.execute_main = False
         else:
             request.cacheable = True
     
             lastModified = response.headers.get('Last-Modified', None)
             # save the cache data
             body = ''.join([chunk for chunk in response._cachefilter_tee])
+            create_time = time.time()
             cherrypy._cache.put(lastModified, (response.status,
-                                               response.header_list,
-                                               body))
+                                               response.headers,
+                                               body,
+                                               create_time))
 
 
 def percentual(n,d):

cherrypy/test/test_cache_filter.py

 
 import cherrypy
 from cherrypy.filters.cachefilter import expires
+from cherrypy.lib.httptools import HTTPDate
 
 
 def setup_server():
             msg = "visit #%s" % cherrypy.counter
             return msg
         index.exposed = True
-    
+        
+        def textplain(self):
+            cherrypy.response.headers['Content-type'] = 'text/plain'
+            cherrypy.response.headers['Last-Modified'] = HTTPDate()
+            return self.index()
+        textplain.exposed = True
+        
     class UnCached(object):
         
         use_force = False
 class CacheFilterTest(helper.CPWebCase):
     
     def testCaching(self):
+        elapsed = 0.0
         for trial in xrange(10):
             self.getPage("/")
-            # The response should be the same every time!
+            # The response should be the same every time,
+            # except for the Age response header.
             self.assertBody('visit #1')
+            if trial != 0:
+                age = int(self.assertHeader("Age"))
+                self.assert_(age >= elapsed)
+                elapsed = age
         
         # POST, PUT, DELETE should not be cached.
         self.getPage("/", method="POST")
         # so this request will recalc the response.
         self.getPage("/", method="GET")
         self.assertBody('visit #5')
-    
+
+        # make sure that custom set Content-types get passed through on 304s
+        self.getPage("/textplain")
+        self.assertHeader("Content-type", "text/plain")
+        self.assertStatus("200 OK")
+        self.assertBody('visit #6')
+        date = self.assertHeader("Last-Modified")
+        self.getPage("/textplain", [("If-Modified-Since", date)])
+        self.assertHeader("Content-type", "text/plain")
+        self.assertStatus("304 Not Modified")
+        
     def testExpiresTool(self):
         
         # test setting an expires header
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.