web/httpcache.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
--- a/web/httpcache.py	Mon Jan 04 18:40:30 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""HTTP cache managers"""
-
-__docformat__ = "restructuredtext en"
-
-from time import mktime
-from datetime import datetime
-
-class NoHTTPCacheManager(object):
-    """default cache manager: set no-cache cache control policy"""
-    def __init__(self, view):
-        self.view = view
-        self.req = view._cw
-        self.cw_rset = view.cw_rset
-
-    def set_headers(self):
-        self.req.set_header('Cache-control', 'no-cache')
-        self.req.set_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
-
-
-class MaxAgeHTTPCacheManager(NoHTTPCacheManager):
-    """max-age cache manager: set max-age cache control policy, with max-age
-    specified with the `cache_max_age` attribute of the view
-    """
-    def set_headers(self):
-        self.req.set_header('Cache-control',
-                            'max-age=%s' % self.view.cache_max_age)
-
-
-class EtagHTTPCacheManager(NoHTTPCacheManager):
-    """etag based cache manager for startup views
-
-    * etag is generated using the view name and the user's groups
-    * set policy to 'must-revalidate' and expires to the current time to force
-      revalidation on each request
-    """
-
-    def etag(self):
-        if not self.req.cnx: # session without established connection to the repo
-            return self.view.__regid__
-        return self.view.__regid__ + '/' + ','.join(sorted(self.req.user.groups))
-
-    def max_age(self):
-        # 0 to actually force revalidation
-        return 0
-
-    def last_modified(self):
-        """return view's last modified GMT time"""
-        return self.view.last_modified()
-
-    def set_headers(self):
-        req = self.req
-        try:
-            req.set_header('Etag', '"%s"' % self.etag())
-        except NoEtag:
-            super(EtagHTTPCacheManager, self).set_headers()
-            return
-        req.set_header('Cache-control',
-                       'must-revalidate,max-age=%s' % self.max_age())
-        mdate = self.last_modified()
-        # use a timestamp, not a formatted raw header, and let
-        # the front-end correctly generate it
-        # ("%a, %d %b %Y %H:%M:%S GMT" return localized date that
-        # twisted don't parse correctly)
-        req.set_header('Last-modified', mktime(mdate.timetuple()), raw=False)
-
-
-class EntityHTTPCacheManager(EtagHTTPCacheManager):
-    """etag based cache manager for view displaying a single entity
-
-    * etag is generated using entity's eid, the view name and the user's groups
-    * get last modified time from the entity definition (this may not be the
-      entity's modification time since a view may include some related entities
-      with a modification time to consider) using the `last_modified` method
-    """
-    def etag(self):
-        if self.cw_rset is None or len(self.cw_rset) == 0: # entity startup view for instance
-            return super(EntityHTTPCacheManager, self).etag()
-        if len(self.cw_rset) > 1:
-            raise NoEtag()
-        etag = super(EntityHTTPCacheManager, self).etag()
-        eid = self.cw_rset[0][0]
-        if self.req.user.owns(eid):
-            etag += ',owners'
-        return str(eid) + '/' + etag
-
-
-class NoEtag(Exception):
-    """an etag can't be generated"""
-
-__all__ = ('GMTOFFSET',
-           'NoHTTPCacheManager', 'MaxAgeHTTPCacheManager',
-           'EtagHTTPCacheManager', 'EntityHTTPCacheManager')
-
-# monkey patching, so view doesn't depends on this module and we have all
-# http cache related logic here
-
-from cubicweb import view as viewmod
-
-def set_http_cache_headers(self):
-    self.http_cache_manager(self).set_headers()
-viewmod.View.set_http_cache_headers = set_http_cache_headers
-
-
-def last_modified(self):
-    """return the date/time where this view should be considered as
-    modified. Take care of possible related objects modifications.
-
-    /!\ must return GMT time /!\
-    """
-    # XXX check view module's file modification time in dev mod ?
-    ctime = datetime.utcnow()
-    if self.cache_max_age:
-        mtime = self._cw.header_if_modified_since()
-        if mtime:
-            tdelta = (ctime - mtime)
-            if tdelta.days * 24*60*60 + tdelta.seconds <= self.cache_max_age:
-                return mtime
-    # mtime = ctime will force page rerendering
-    return ctime
-viewmod.View.last_modified = last_modified
-
-
-# configure default caching
-viewmod.View.http_cache_manager = NoHTTPCacheManager
-# max-age=0 to actually force revalidation when needed
-viewmod.View.cache_max_age = 0
-
-viewmod.StartupView.http_cache_manager = MaxAgeHTTPCacheManager
-viewmod.StartupView.cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
-
-
-### HTTP Cache validator ############################################
-
-
-
-def get_validators(headers_in):
-    """return a list of http condition validator relevant to this request
-    """
-    result = []
-    for header, func in VALIDATORS:
-        value = headers_in.getHeader(header)
-        if value is not None:
-            result.append((func, value))
-    return result
-
-
-def if_modified_since(ref_date, headers_out):
-    last_modified = headers_out.getHeader('last-modified')
-    if last_modified is None:
-        return True
-    return ref_date < last_modified
-
-def if_none_match(tags, headers_out):
-    etag = headers_out.getHeader('etag')
-    if etag is None:
-        return True
-    return not ((etag in tags) or ('*' in tags))
-
-VALIDATORS = [
-    ('if-modified-since', if_modified_since),
-    #('if-unmodified-since', if_unmodified_since),
-    ('if-none-match', if_none_match),
-    #('if-modified-since', if_modified_since),
-]