[z3-checkins] r38084 - z3/deliverance/branches/cache_aware/deliverance

ltucker at codespeak.net ltucker at codespeak.net
Wed Feb 7 18:58:49 CET 2007


Author: ltucker
Date: Wed Feb  7 18:58:45 2007
New Revision: 38084

Modified:
   z3/deliverance/branches/cache_aware/deliverance/cache_utils.py
   z3/deliverance/branches/cache_aware/deliverance/resource_fetcher.py
   z3/deliverance/branches/cache_aware/deliverance/wsgimiddleware.py
Log:
adds last-modified handling, corrects elimination of validation headers during subrequests, makes cache-control header merging optional and off by default

Modified: z3/deliverance/branches/cache_aware/deliverance/cache_utils.py
==============================================================================
--- z3/deliverance/branches/cache_aware/deliverance/cache_utils.py	(original)
+++ z3/deliverance/branches/cache_aware/deliverance/cache_utils.py	Wed Feb  7 18:58:45 2007
@@ -1,6 +1,6 @@
 import re
 from paste.response import header_value, replace_header
-from paste.httpheaders import EXPIRES 
+from paste.httpheaders import EXPIRES, LAST_MODIFIED
 from time import time as now
 from sets import Set
 
@@ -13,30 +13,31 @@
 there is probably a good amount of work in here that Paste could simplify 
 tests that depend on set ordering 
 
-TODO: 
-handle last-modified
 """
 
 
-def merge_cache_headers(self, response_info, new_headers): 
+def merge_cache_headers(self, response_info, new_headers, merge_cache_control=True): 
     """
     replaces cache related headers in new_headers 
     with caching info calculated cache_info 
     (a map of urls to wsgi response triples) 
+    if merge_cache_control is False, the cache-control header is 
+    not calculated and only etags, last-modified and vary headers are merged. 
     """
 
     headers_map = {}
     for uri, response in response_info.items(): 
         headers_map[uri] = response[1]
         
-    cache_control_map = merge_cache_control(headers_map.values(), upgrade_expires=True)
-    if len(cache_control_map):         
-        replace_header(new_headers, 'cache-control', 
-                       flatten_directive_map(cache_control_map))
-        # provide an Expires header if there is a cache-control max-age 
-        if 'max-age' in cache_control_map: 
-            expire_delta = int(new_cache_ctl['max-age'])
-            EXPIRES.update(new_headers, delta=expire_delta)
+    if merge_cache_control: 
+        cache_control_map = merge_cache_control(headers_map.values(), upgrade_expires=True)
+        if len(cache_control_map):         
+            replace_header(new_headers, 'cache-control', 
+                           flatten_directive_map(cache_control_map))
+            # provide an Expires header if there is a cache-control max-age 
+            if 'max-age' in cache_control_map: 
+                expire_delta = int(new_cache_ctl['max-age'])
+                EXPIRES.update(new_headers, delta=expire_delta)
 
     etag = merge_etags_from_headers(headers_map)
     if etag is not None: 
@@ -45,6 +46,10 @@
     vary = merge_vary_from_headers(headers_map)
     if vary is not None: 
         replace_header(new_headers, 'vary', vary)
+    
+    last_mod = merge_last_modified_from_headers(headers_map)
+    if last_mod is not None: 
+        replace_header(new_headers, 'last-modified', last_mod)
 
 
 
@@ -112,6 +117,30 @@
 
     return new_cache_ctl 
 
+
+def merge_last_modified_from_headers(headers_map): 
+    """
+    accepts a map from uris to wsgi-style header lists 
+    returns the value for the last-modified header
+    representing the latest modification date present 
+    in any of the header lists 
+    """
+    latest_mod = None 
+    for uri, headers in headers_map.items(): 
+        last_mod = header_value(headers,'last-modified')
+        if last_mod is not None: 
+            mod_secs = LAST_MODIFIED.parse(last_mod)
+            if latest_mod is None: 
+                latest_mod = mod_secs
+            elif mod_secs > latest_mod: 
+                latest_mod = mod_secs
+    if latest_mod is not None: 
+        tmp = []
+        LAST_MODIFIED.update(tmp, time=latest_mod)
+        return tmp[0][1]
+    else: 
+        return None
+                
     
 def merge_etags_from_headers(headers_map): 
     """

Modified: z3/deliverance/branches/cache_aware/deliverance/resource_fetcher.py
==============================================================================
--- z3/deliverance/branches/cache_aware/deliverance/resource_fetcher.py	(original)
+++ z3/deliverance/branches/cache_aware/deliverance/resource_fetcher.py	Wed Feb  7 18:58:45 2007
@@ -53,8 +53,7 @@
         if 'paste.recursive.include' in self.environ: 
             # Try to do the redirect this way...
             includer = self.environ['paste.recursive.include']
-            res = includer(self.uri, self.environ)
-            return (res.status, res.headers, res.body)
+            return intercept_output(self.environ, includer.application)
         else: 
             status, headers, body = intercept_output(self.environ, self.app)
             return (status, headers, body)

Modified: z3/deliverance/branches/cache_aware/deliverance/wsgimiddleware.py
==============================================================================
--- z3/deliverance/branches/cache_aware/deliverance/wsgimiddleware.py	(original)
+++ z3/deliverance/branches/cache_aware/deliverance/wsgimiddleware.py	Wed Feb  7 18:58:45 2007
@@ -1,3 +1,5 @@
+
+
 """
 Deliverance theming as WSGI middleware
 """
@@ -36,7 +38,7 @@
     tranformation as a WSGI middleware component. 
     """
 
-    def __init__(self, app, theme_uri, rule_uri, renderer='py'):
+    def __init__(self, app, theme_uri, rule_uri, renderer='py', merge_cache_control=False):
         """
         initializer
         
@@ -46,10 +48,15 @@
         renderer: selects deliverance render class to utilize when 
           performing transformations, may be 'py' or 'xslt' or a
           Renderer class
+        merge_cache_control: if set to True, the cache-control header will 
+        be calculated from the cache-control headers of all component pages 
+        during rendering. If set to False, the requested content's 
+        cache-control headers will be used. (does not affect etag merging)
         """
         self.app = app
         self.theme_uri = theme_uri
         self.rule_uri = rule_uri
+        self.merge_cache_control = merge_cache_control
 
         if renderer == 'py':
             import interpreter
@@ -153,15 +160,16 @@
         environ[DELIVERANCE_CACHE] = {} 
         notheme = 'notheme' in qs
         if notheme:
+	    environ['QUERY_STRING'] = '' # XXX
             return self.app(environ, start_response)
         
         # unsupported 
         if 'HTTP_ACCEPT_ENCODING' in environ:
             environ['HTTP_ACCEPT_ENCODING'] = '' 
         if 'HTTP_IF_MATCH' in environ: 
-            environ['HTTP_IF_MATCH'] = '' 
+            del environ['HTTP_IF_MATCH'] 
         if 'HTTP_IF_UNMODIFIED_SINCE' in environ: 
-            environ['HTTP_IF_UNMODIFIED_SINCE'] = '' 
+            del environ['HTTP_IF_UNMODIFIED_SINCE'] 
             
         status, headers, body = self.rebuild_check(environ, start_response)
 
@@ -177,7 +185,8 @@
 
         cache_utils.merge_cache_headers(environ, 
                                         environ[DELIVERANCE_CACHE], 
-                                        headers)
+                                        headers, 
+                                        self.merge_cache_control)
 
         start_response(status, headers)
         return [body]
@@ -210,7 +219,13 @@
         etag_map = {}
         if 'HTTP_IF_NONE_MATCH' in environ: 
             etag_map = cache_utils.parse_merged_etag(environ['HTTP_IF_NONE_MATCH'])
-            environ['HTTP_IF_NONE_MATCH'] = etag_map.get(content_url,None)
+	    tag = etag_map.get(content_url, None)
+	    environ['HTTP_IF_NONE_MATCH'] = tag
+	    if tag: 
+                environ['HTTP_IF_NONE_MATCH'] = tag
+            else:
+	        del environ['HTTP_IF_NONE_MATCH']
+
 
         status, headers, body = intercept_output(environ, self.app,
                                                  self.should_intercept,
@@ -245,9 +260,9 @@
             # something changed, 
             # get the content explicitly and give it back 
             if 'HTTP_IF_MODIFIED_SINCE' in environ: 
-                environ['HTTP_IF_MODIFIED_SINCE'] = ''
+                del environ['HTTP_IF_MODIFIED_SINCE']
             if 'HTTP_IF_NONE_MATCH' in environ: 
-                environ['HTTP_IF_NONE_MATCH'] = '' 
+                del environ['HTTP_IF_NONE_MATCH'] 
             environ['CACHE-CONTROL'] = 'no-cache'
 
             status, headers, body = intercept_output(environ, self.app)
@@ -263,7 +278,8 @@
         # nothing was modified, give back a 304 
         cache_utils.merge_cache_headers(environ, 
                                         environ[DELIVERANCE_CACHE], 
-                                        headers)
+                                        headers, 
+                                        self.merge_cache_control)
         start_response('304 Not Modified', headers)
 
         return (None,None,[])
@@ -303,14 +319,16 @@
                 return response[2]
 
         fetcher = self.get_fetcher(environ, uri)
-        
+         
+
         # eliminate validation headers, we want the content 
         if 'HTTP_IF_MODIFIED_SINCE' in fetcher.environ: 
-            fetcher.environ['HTTP_IF_MODIFIED_SINCE'] = ''
+            del fetcher.environ['HTTP_IF_MODIFIED_SINCE']
         if 'HTTP_IF_NONE_MATCH' in fetcher.environ: 
-            fetcher.environ['HTTP_IF_NONE_MATCH'] = '' 
+            del fetcher.environ['HTTP_IF_NONE_MATCH'] 
         fetcher.environ['CACHE-CONTROL'] = 'no-cache'
         
+
         status, headers, body = fetcher.wsgi_get()         
         
         if not status.startswith('200'): 
@@ -368,25 +386,24 @@
 
         """
 
-
         fetcher = self.get_fetcher(environ, uri)
         
         if httpdate_since: 
             fetcher.environ['HTTP_IF_MODIFIED_SINCE'] = httpdate_since 
         else: 
-            fetcher.environ['HTTP_IF_MODIFIED_SINCE'] = ''
+            del fetcher.environ['HTTP_IF_MODIFIED_SINCE']
         
 
         if etag: 
             fetcher.environ['HTTP_IF_NONE_MATCH'] = etag
         else: 
-            fetcher.environ['HTTP_IF_NONE_MATCH'] = ''
+            del fetcher.environ['HTTP_IF_NONE_MATCH']
 
 
         status, headers, body = fetcher.wsgi_get()
         environ[DELIVERANCE_CACHE][uri] = (status, headers, body)
 
-        if status.startswith('304'): # Not Modified 
+        if status.startswith('304'): # Not Modified             
             return False 
 
         return True


More information about the z3-checkins mailing list