[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