source: products/vendor/Products.CacheSetup/current/Products/CacheSetup/cmf_utils.py @ 3296

Last change on this file since 3296 was 3296, checked in by fenix, 13 years ago

Load Products.CacheSetup?-1.2.1 into vendor/Products.CacheSetup?/current.

  • Property svn:eol-style set to native
File size: 5.7 KB
Line 
1from DateTime import DateTime
2from Products.CMFCore.utils import getToolByName
3from Products.CMFCore.utils import parse_etags
4# ^^^ There was a whole bunch of code here for plone versions that
5# didn't have a recent enough version of CMF to include above
6# parse_etags method. I removed it. If needed it can be
7# resurrected. [Reinout]   
8from Products.CacheSetup.config import CACHE_TOOL_ID
9
10#from AccessControl import ModuleSecurityInfo
11#security = ModuleSecurityInfo( 'Products.CMFCore.utils' )
12#security.declarePrivate('_setCacheHeaders')
13
14def _setCacheHeaders(obj, extra_context, rule=None, header_set=None,
15                     expr_context=None): 
16    """Set cache headers according to cache policy manager for the obj."""
17    REQUEST = getattr(obj, 'REQUEST', None)
18    if REQUEST is None:
19        return
20    if rule is None:
21        pcs = getToolByName(obj, CACHE_TOOL_ID, None)
22        object = obj.getParentNode()
23        view = obj.getId()
24        member = pcs.getMember()
25        (rule, header_set) = pcs.getRuleAndHeaderSet(REQUEST, object,
26                                                     view, member)
27        if header_set is None:
28            return
29        expr_context = rule._getExpressionContext(REQUEST, object,
30                                                  view, member,
31                                                  keywords=extra_context)
32    elif header_set is None:
33        return
34    RESPONSE = REQUEST.RESPONSE
35    (headers_to_add, 
36     headers_to_remove) = header_set.getHeaders(expr_context)
37    for h in headers_to_remove:
38        if RESPONSE.headers.has_key(h.lower()):
39            del RESPONSE.headers[h.lower()]
40        elif RESPONSE.headers.has_key(h):
41            del RESPONSE.headers[h]
42    for key, value in headers_to_add:
43        if key == 'ETag':
44            RESPONSE.setHeader(key, value, literal=1)
45        else:
46            RESPONSE.setHeader(key, value)
47
48#security.declarePrivate('_checkConditionalGET')
49def _checkConditionalGET(obj, extra_context, rule, header_set,
50                         expr_context):
51    """A conditional GET is done using one or both of the request
52       headers:
53
54       If-Modified-Since: Date
55       If-None-Match: list ETags (comma delimited, sometimes quoted)
56
57       If both conditions are present, both must be satisfied.
58       
59       This method checks the caching policy manager to see if
60       a content object's Last-modified date and ETag satisfy
61       the conditional GET headers.
62
63       Returns the tuple (last_modified, etag) if the conditional
64       GET requirements are met and None if not.
65
66       It is possible for one of the tuple elements to be None.
67       For example, if there is no If-None-Match header and
68       the caching policy does not specify an ETag, we will
69       just return (last_modified, None).
70       """
71
72    if header_set is None:
73        return False
74
75    # 304s not enabled
76    if not header_set.getEnable304s():
77        return False
78
79    REQUEST = getattr(obj, 'REQUEST', None)
80    if REQUEST is None:
81        return False
82
83    if_modified_since = REQUEST.get_header('If-Modified-Since', None)
84    if_none_match = REQUEST.get_header('If-None-Match', None)
85
86    if if_modified_since is None and if_none_match is None:
87        # not a conditional GET
88        return False
89
90    etag_matched = False
91
92    # handle if-none-match
93    if if_none_match:
94        if not header_set.getEtag():
95            # no etag available
96            return False
97
98        content_etag = header_set.getEtagValue(expr_context)
99        # ETag not available
100        if content_etag is None:
101            return False
102               
103        client_etags = parse_etags(if_none_match)
104        if not client_etags:
105            # bad if-none-match
106            return False
107
108        # is the current etag in the list of client-side etags?
109        if (content_etag not in client_etags and '*' not in client_etags):
110            return False
111
112        etag_matched = True
113
114    # handle if-modified-since
115    if if_modified_since:
116        if header_set.getLastModified() != 'yes':
117            # no modification time available for content object
118            return False
119       
120        # from CMFCore/FSFile.py:
121        if_modified_since = if_modified_since.split(';')[0]
122        # Some proxies seem to send invalid date strings for this
123        # header. If the date string is not valid, we ignore it
124        # rather than raise an error to be generally consistent
125        # with common servers such as Apache (which can usually
126        # understand the screwy date string as a lucky side effect
127        # of the way they parse it).
128        try:
129            if_modified_since=long(DateTime(if_modified_since).timeTime())
130        except:
131            # bad if-modified-since header - bail
132            return False
133       
134        if if_modified_since < 0: # bad header
135            return False
136
137        content_mod_time = header_set.getLastModifiedValue(expr_context)
138        if not content_mod_time:
139            # if-modified-since but no content modification time available - bail
140            return False
141        content_mod_time = long(content_mod_time.timeTime())
142        if content_mod_time < 0: # bogus modification time
143            return False
144
145        # has content been modified since the if-modified-since time?
146        if content_mod_time > if_modified_since:
147            return False
148
149        # If we generate an ETag, don't validate the conditional GET unless
150        # the client supplies an ETag.  This may be more conservative than the
151        # spec requires.
152        if header_set.getEtag():
153            if not etag_matched:
154                return False
155
156    _setCacheHeaders(obj, extra_context, rule, header_set, expr_context)
157    REQUEST.RESPONSE.setStatus(304)           
158    return True
Note: See TracBrowser for help on using the repository browser.