web/httpcache.py
changeset 4436 294e084f1263
parent 4252 6c4f109c2b03
parent 4424 5a5cd7591706
child 5155 1dea6e0fdfc1
child 5421 8167de96c523
equal deleted inserted replaced
4434:101344a6ff9b 4436:294e084f1263
     6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     7 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     7 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     8 """
     8 """
     9 __docformat__ = "restructuredtext en"
     9 __docformat__ = "restructuredtext en"
    10 
    10 
       
    11 from time import mktime
    11 from datetime import datetime
    12 from datetime import datetime
    12 
    13 
    13 # time delta usable to convert localized time to GMT time
    14 # time delta usable to convert localized time to GMT time
    14 GMTOFFSET = - (datetime.now() - datetime.utcnow())
    15 GMTOFFSET = - (datetime.now() - datetime.utcnow())
    15 
    16 
    21         self.cw_rset = view.cw_rset
    22         self.cw_rset = view.cw_rset
    22 
    23 
    23     def set_headers(self):
    24     def set_headers(self):
    24         self.req.set_header('Cache-control', 'no-cache')
    25         self.req.set_header('Cache-control', 'no-cache')
    25 
    26 
       
    27 
    26 class MaxAgeHTTPCacheManager(NoHTTPCacheManager):
    28 class MaxAgeHTTPCacheManager(NoHTTPCacheManager):
    27     """max-age cache manager: set max-age cache control policy, with max-age
    29     """max-age cache manager: set max-age cache control policy, with max-age
    28     specified with the `cache_max_age` attribute of the view
    30     specified with the `cache_max_age` attribute of the view
    29     """
    31     """
    30     def set_headers(self):
    32     def set_headers(self):
    31         self.req.set_header('Cache-control',
    33         self.req.set_header('Cache-control',
    32                             'max-age=%s' % self.view.cache_max_age)
    34                             'max-age=%s' % self.view.cache_max_age)
    33 
    35 
       
    36 
    34 class EtagHTTPCacheManager(NoHTTPCacheManager):
    37 class EtagHTTPCacheManager(NoHTTPCacheManager):
    35     """etag based cache manager for startup views
    38     """etag based cache manager for startup views
    36 
    39 
    37     * etag is generated using the view name and the user's groups
    40     * etag is generated using the view name and the user's groups
    38     * set policy to 'must-revalidate' and expires to the current time to force
    41     * set policy to 'must-revalidate' and expires to the current time to force
    39       revalidation on each request
    42       revalidation on each request
    40     """
    43     """
    41     # GMT time required
       
    42     date_format = "%a, %d %b %Y %H:%M:%S GMT"
       
    43 
    44 
    44     def etag(self):
    45     def etag(self):
    45         return self.view.__regid__ + '/' + ','.join(sorted(self.req.user.groups))
    46         return self.view.__regid__ + '/' + ','.join(sorted(self.req.user.groups))
    46 
    47 
    47     def max_age(self):
    48     def max_age(self):
    48         # 0 to actually force revalidation
    49         # 0 to actually force revalidation
    49         return 0
    50         return 0
    50 
    51 
    51     def last_modified(self):
    52     def last_modified(self):
       
    53         """return view's last modified GMT time"""
    52         return self.view.last_modified()
    54         return self.view.last_modified()
    53 
    55 
    54     def set_headers(self):
    56     def set_headers(self):
    55         req = self.req
    57         req = self.req
    56         try:
    58         try:
    59             self.req.set_header('Cache-control', 'no-cache')
    61             self.req.set_header('Cache-control', 'no-cache')
    60             return
    62             return
    61         req.set_header('Cache-control',
    63         req.set_header('Cache-control',
    62                        'must-revalidate;max-age=%s' % self.max_age())
    64                        'must-revalidate;max-age=%s' % self.max_age())
    63         mdate = self.last_modified()
    65         mdate = self.last_modified()
    64         req.set_header('Last-modified', mdate.strftime(self.date_format))
    66         # use a timestamp, not a formatted raw header, and let
       
    67         # the front-end correctly generate it
       
    68         # ("%a, %d %b %Y %H:%M:%S GMT" return localized date that
       
    69         # twisted don't parse correctly)
       
    70         req.set_header('Last-modified', mktime(mdate.timetuple()), raw=False)
       
    71 
    65 
    72 
    66 class EntityHTTPCacheManager(EtagHTTPCacheManager):
    73 class EntityHTTPCacheManager(EtagHTTPCacheManager):
    67     """etag based cache manager for view displaying a single entity
    74     """etag based cache manager for view displaying a single entity
    68 
    75 
    69     * etag is generated using entity's eid, the view name and the user's groups
    76     * etag is generated using entity's eid, the view name and the user's groups
    97 
   104 
    98 def set_http_cache_headers(self):
   105 def set_http_cache_headers(self):
    99     self.http_cache_manager(self).set_headers()
   106     self.http_cache_manager(self).set_headers()
   100 viewmod.View.set_http_cache_headers = set_http_cache_headers
   107 viewmod.View.set_http_cache_headers = set_http_cache_headers
   101 
   108 
       
   109 
   102 def last_modified(self):
   110 def last_modified(self):
   103     """return the date/time where this view should be considered as
   111     """return the date/time where this view should be considered as
   104     modified. Take care of possible related objects modifications.
   112     modified. Take care of possible related objects modifications.
   105 
   113 
   106     /!\ must return GMT time /!\
   114     /!\ must return GMT time /!\
   109     ctime = datetime.utcnow()
   117     ctime = datetime.utcnow()
   110     if self.cache_max_age:
   118     if self.cache_max_age:
   111         mtime = self._cw.header_if_modified_since()
   119         mtime = self._cw.header_if_modified_since()
   112         if mtime:
   120         if mtime:
   113             tdelta = (ctime - mtime)
   121             tdelta = (ctime - mtime)
   114             if tdelta.days * 24*60*60 + tdelta.seconds > self.cache_max_age:
   122             if tdelta.days * 24*60*60 + tdelta.seconds <= self.cache_max_age:
   115                 mtime = ctime
   123                 return mtime
   116         else:
       
   117             mtime = ctime
       
   118     else:
       
   119         mtime = ctime
       
   120     # mtime = ctime will force page rerendering
   124     # mtime = ctime will force page rerendering
   121     return mtime
   125     return ctime
   122 viewmod.View.last_modified = last_modified
   126 viewmod.View.last_modified = last_modified
       
   127 
   123 
   128 
   124 # configure default caching
   129 # configure default caching
   125 viewmod.View.http_cache_manager = NoHTTPCacheManager
   130 viewmod.View.http_cache_manager = NoHTTPCacheManager
   126 # max-age=0 to actually force revalidation when needed
   131 # max-age=0 to actually force revalidation when needed
   127 viewmod.View.cache_max_age = 0
   132 viewmod.View.cache_max_age = 0