[CMF-checkins] SVN: CMF/branches/1.4/CMFCore/ - Backport fixes for IE caching bug, r38665:38666 from 1.5 branch

Sidnei da Silva sidnei at enfoldsystems.com
Thu Sep 29 13:07:04 EDT 2005


Log message for revision 38689:
  
  - Backport fixes for IE caching bug, r38665:38666 from 1.5 branch
  
  ------------------------------------------------------------------------
  r38666 | davisg | 2005-09-28 13:16:48 -0300 (Wed, 28 Sep 2005) | 9 lines
  
  Caching policies now let you turn off the last-modified header
  (this is to allow a workaround for an IE bug that causes content
  to be rendered from cache instead of from the latest request when
  a last-modified header is present).  Caching policies also let
  you set pre-check and post-check headers.  These are proprietary
  IE cache-control tokens without which IE can behave badly.  See
  http://msdn.microsoft.com/workshop/author/perf/perftips.asp .
  

Changed:
  U   CMF/branches/1.4/CMFCore/CachingPolicyManager.py
  U   CMF/branches/1.4/CMFCore/dtml/cachingPolicies.dtml
  U   CMF/branches/1.4/CMFCore/tests/test_CachingPolicyManager.py
  U   CMF/branches/1.4/CMFCore/utils.py

-=-
Modified: CMF/branches/1.4/CMFCore/CachingPolicyManager.py
===================================================================
--- CMF/branches/1.4/CMFCore/CachingPolicyManager.py	2005-09-29 16:33:36 UTC (rev 38688)
+++ CMF/branches/1.4/CMFCore/CachingPolicyManager.py	2005-09-29 17:07:04 UTC (rev 38689)
@@ -82,21 +82,22 @@
             'content' -- the content object itself
 
             'view' -- the name of the view method
-           
+
             'keywords' -- keywords passed to the request
-           
+
             'request' -- the REQUEST object itself
-           
+
             'member' -- the authenticated member, or None if anonymous
-           
+
             'modules' -- usual TALES access-with-import
-           
+
             'nothing' -- None
 
-          - The "Last-modified" HTTP response header will be set using
-            'mtime_func', which is another TALES expression evaluated
+          - mtime_func is used to set the "Last-modified" HTTP response
+            header, which is another TALES expression evaluated
             against the same namespace.  If not specified explicitly,
-            uses 'content/modified'.
+            uses 'object/modified'.  mtime_func is also used in responding
+            to conditional GETs.
 
           - The "Expires" HTTP response header and the "max-age" token of
             the "Cache-control" header will be set using 'max_age_secs',
@@ -106,17 +107,17 @@
             set using 's_max_age_secs', if passed;  it should be an integer
             value in seconds.
 
-          - The "Vary" HTTP response headers will be set if a value is 
+          - The "Vary" HTTP response headers will be set if a value is
             provided. The Vary header is described in RFC 2616. In essence,
             it instructs caches that respect this header (such as Squid
             after version 2.4) to distinguish between requests not just by
             the request URL, but also by values found in the headers showing
-            in the Vary tag. "Vary: Cookie" would force Squid to also take 
-            Cookie headers into account when deciding what cached object to 
+            in the Vary tag. "Vary: Cookie" would force Squid to also take
+            Cookie headers into account when deciding what cached object to
             choose and serve in response to a request.
 
           - The "ETag" HTTP response header will be set if a value is
-            provided. The value is a TALES expression and the result 
+            provided. The value is a TALES expression and the result
             after evaluation will be used as the ETag header value.
 
           - Other tokens will be added to the "Cache-control" HTTP response
@@ -129,12 +130,31 @@
              'must_revalidate=1' argument => "must-revalidate" token
 
              'proxy_revalidate=1' argument => "proxy-revalidate" token
-             
+
              'public=1' argument => "public" token
-             
+
              'private=1' argument => "private" token
 
              'no_transform=1' argument => "no-transform" token
+
+          - The last_modified argument is used to determine whether to add a
+            Last-Modified header.  last_modified=1 by default.  There appears
+            to be a bug in IE 6 (and possibly other versions) that uses the
+            Last-Modified header plus some heuristics rather than the other
+            explicit caching headers to determine whether to render content
+            from the cache.  If you set, say, max-age=0, must-revalidate and
+            have a Last-Modified header some time in the past, IE will
+            recognize that the page in cache is stale and will request an
+            update from the server BUT if you have a Last-Modified header
+            with an older date, will then ignore the update and render from
+            the cache, so you may want to disable the Last-Modified header
+            when controlling caching using Cache-Control headers.
+
+          - The pre-check and post-check Cache-Control tokens are Microsoft
+            proprietary tokens added to IE 5+.  Documentation can be found
+            here: http://msdn.microsoft.com/workshop/author/perf/perftips.asp
+            Unfortunately these are needed to make IE behave correctly.
+
     """
 
     def __init__( self
@@ -153,6 +173,9 @@
                 , private=0
                 , no_transform=0
                 , enable_304s=0
+                , last_modified=1
+                , pre_check=None
+                , post_check=None
                 ):
 
         if not predicate:
@@ -162,15 +185,33 @@
             mtime_func = 'content/modified'
 
         if max_age_secs is not None:
-            max_age_secs = int( max_age_secs )
+            if str(max_age_secs).strip() == '':
+                max_age_secs = None
+            else:
+                max_age_secs = int( max_age_secs )
 
         if s_max_age_secs is not None:
-            s_max_age_secs = int( s_max_age_secs )
+            if str(s_max_age_secs).strip() == '':
+                s_max_age_secs = None
+            else:
+                s_max_age_secs = int( s_max_age_secs )
 
         etag_expr = None
         if etag_func:
             etag_expr = Expression( text=etag_func )
 
+        if pre_check is not None:
+            if str(pre_check).strip() == '':
+                pre_check = None
+            else:
+                pre_check = int(pre_check)
+
+        if post_check is not None:
+            if str(post_check).strip() == '':
+                post_check = None
+            else:
+                post_check = int(post_check)
+
         self._policy_id = policy_id
         self._predicate = Expression( text=predicate )
         self._mtime_func = Expression( text=mtime_func )
@@ -186,6 +227,9 @@
         self._vary = vary
         self._etag_func = etag_expr
         self._enable_304s = int ( enable_304s )
+        self._last_modified = int( last_modified )
+        self._pre_check = pre_check
+        self._post_check = post_check
 
     def getPolicyId( self ):
         """
@@ -268,6 +312,20 @@
         """
         return getattr(self, '_enable_304s', 0)
 
+    def getLastModified(self):
+        """Should we set the last modified header?"""
+        return getattr(self, '_last_modified', 1)
+
+    def getPreCheck(self):
+        """
+        """
+        return getattr(self, '_pre_check', None)
+
+    def getPostCheck(self):
+        """
+        """
+        return getattr(self, '_post_check', None)
+
     def testPredicate(self, expr_context):
         """ Does this request match our predicate?"""
         return self._predicate(expr_context)
@@ -282,16 +340,14 @@
 
         if self.testPredicate( expr_context ):
 
-            mtime = self._mtime_func( expr_context )
+            if self.getLastModified():
+                mtime = self._mtime_func( expr_context )
+                if type( mtime ) is type( '' ):
+                    mtime = DateTime( mtime )
+                if mtime is not None:
+                    mtime_str = rfc1123_date(mtime.timeTime())
+                    headers.append( ( 'Last-modified', mtime_str ) )
 
-            if type( mtime ) is type( '' ):
-                mtime = DateTime( mtime )
-
-            if mtime is not None:
-                mtime_flt = mtime.timeTime()
-                mtime_str = rfc1123_date(mtime_flt)
-                headers.append( ( 'Last-modified', mtime_str ) )
-
             control = []
 
             if self.getMaxAgeSecs() is not None:
@@ -299,7 +355,7 @@
                 exp_time_str = rfc1123_date(now.timeTime() + self._max_age_secs)
                 headers.append( ( 'Expires', exp_time_str ) )
                 control.append( 'max-age=%d' % self._max_age_secs )
-                
+
             if self.getSMaxAgeSecs() is not None:
                 control.append( 's-maxage=%d' % self._s_max_age_secs )
 
@@ -326,6 +382,14 @@
             if self.getNoTransform():
                 control.append( 'no-transform' )
 
+            pre_check = self.getPreCheck()
+            if pre_check is not None:
+                control.append('pre-check=%d' % pre_check)
+
+            post_check = self.getPostCheck()
+            if post_check is not None:
+                control.append('post-check=%d' % post_check)
+
             if control:
                 headers.append( ( 'Cache-control', ', '.join( control ) ) )
 
@@ -375,7 +439,7 @@
         """
             Return a sequence of tuples,
             '( policy_id, ( policy, typeObjectName ) )'
-            for all policies in the registry 
+            for all policies in the registry
         """
         result = []
         for policy_id in self._policy_ids:
@@ -400,15 +464,33 @@
                  , private=0           # boolean (def. 0)
                  , no_transform=0      # boolean (def. 0)
                  , enable_304s=0       # boolean (def. 0)
+                 , last_modified=1     # boolean (def. 1)
+                 , pre_check=None      # integer, default None
+                 , post_check=None     # integer, default None
                  ):
         """
             Add a caching policy.
         """
+        if max_age_secs is None or str(max_age_secs).strip() == '':
+            max_age_secs = None
+        else:
+            max_age_secs = int(max_age_secs)
+
         if s_max_age_secs is None or str(s_max_age_secs).strip() == '':
             s_max_age_secs = None
         else:
             s_max_age_secs = int(s_max_age_secs)
 
+        if pre_check is None or str(pre_check).strip() == '':
+            pre_check = None
+        else:
+            pre_check = int(pre_check)
+
+        if post_check is None or str(post_check).strip() == '':
+            post_check = None
+        else:
+            post_check = int(post_check)
+
         self._addPolicy( policy_id
                        , predicate
                        , mtime_func
@@ -424,8 +506,11 @@
                        , private
                        , no_transform
                        , enable_304s
+                       , last_modified
+                       , pre_check
+                       , post_check
                        )
-        if REQUEST is not None: 
+        if REQUEST is not None:
             REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
                                           + '/manage_cachingPolicies'
                                           + '?manage_tabs_message='
@@ -450,15 +535,33 @@
                     , private=0           # boolean (def. 0)
                     , no_transform=0      # boolean (def. 0)
                     , enable_304s=0       # boolean (def. 0)
+                    , last_modified=1     # boolean (def. 1)
+                    , pre_check=0         # integer, default=None
+                    , post_check=0        # integer, default=None
                     ):
         """
             Update a caching policy.
         """
+        if max_age_secs is None or str(max_age_secs).strip() == '':
+            max_age_secs = None
+        else:
+            max_age_secs = int(max_age_secs)
+
         if s_max_age_secs is None or str(s_max_age_secs).strip() == '':
             s_max_age_secs = None
         else:
             s_max_age_secs = int(s_max_age_secs)
 
+        if pre_check is None or str(pre_check).strip() == '':
+            pre_check = None
+        else:
+            pre_check = int(pre_check)
+
+        if post_check is None or str(post_check).strip() == '':
+            post_check = None
+        else:
+            post_check = int(post_check)
+
         self._updatePolicy( policy_id
                           , predicate
                           , mtime_func
@@ -474,8 +577,11 @@
                           , private
                           , no_transform
                           , enable_304s
+                          , last_modified
+                          , pre_check
+                          , post_check
                           )
-        if REQUEST is not None: 
+        if REQUEST is not None:
             REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
                                           + '/manage_cachingPolicies'
                                           + '?manage_tabs_message='
@@ -550,6 +656,9 @@
                   , private=0
                   , no_transform=0
                   , enable_304s=0
+                  , last_modified=1
+                  , pre_check=None
+                  , post_check=None
                   ):
         """
             Add a policy to our registry.
@@ -577,6 +686,9 @@
                                                    , private
                                                    , no_transform
                                                    , enable_304s
+                                                   , last_modified
+                                                   , pre_check
+                                                   , post_check
                                                    )
         idlist = list( self._policy_ids )
         idlist.append( policy_id )
@@ -599,6 +711,9 @@
                      , private=0
                      , no_transform=0
                      , enable_304s=0
+                     , last_modified=1
+                     , pre_check=None
+                     , post_check=None
                      ):
         """
             Update a policy in our registry.
@@ -621,6 +736,9 @@
                                                    , private
                                                    , no_transform
                                                    , enable_304s
+                                                   , last_modified
+                                                   , pre_check
+                                                   , post_check
                                                    )
 
     security.declarePrivate( '_reorderPolicy' )
@@ -676,23 +794,24 @@
     security.declareProtected( View, 'getModTimeAndETag' )
     def getModTimeAndETag( self, content, view_method, keywords, time=None):
         """ Return the modification time and ETag for the content object,
-            view method, and keywords as the tuple (modification_time, etag)
-            (where modification_time is a DateTime) or None.
+            view method, and keywords as the tuple (modification_time, etag,
+            set_last_modified_header), where modification_time is a DateTime,
+            or None.
         """
         context = createCPContext( content, view_method, keywords, time=time )
         for policy_id, policy in self.listPolicies():
             if policy.getEnable304s() and policy.testPredicate(context):
-                headers = policy.getHeaders(context)
-                if headers:
-                    content_etag = None
-                    content_mod_time = None
-                    for key, value in headers:
-                        lk = key.lower()
-                        if lk == 'etag':
-                            content_etag = value
-                        elif lk == 'last-modified':
-                            last_modified = DateTime(value)
-                    return (last_modified, content_etag)
+
+                last_modified = policy._mtime_func(context)
+                if type(last_modified) is type(''):
+                    last_modified = DateTime(last_modified)
+
+                content_etag = None
+                if policy.getETagFunc():
+                    content_etag = policy._etag_func(context)
+
+                return (last_modified, content_etag, policy.getLastModified())
+
         return None
 
 

Modified: CMF/branches/1.4/CMFCore/dtml/cachingPolicies.dtml
===================================================================
--- CMF/branches/1.4/CMFCore/dtml/cachingPolicies.dtml	2005-09-29 16:33:36 UTC (rev 38688)
+++ CMF/branches/1.4/CMFCore/dtml/cachingPolicies.dtml	2005-09-29 17:07:04 UTC (rev 38689)
@@ -24,7 +24,10 @@
                 priv_checked="getPrivate() and 'checked' or ''"
                 nt_checked="getNoTransform() and 'checked' or ''"
                 e304_checked="getEnable304s() and 'checked' or ''"
-                s_max_age_secs="getSMaxAgeSecs() is not None and getSMaxAgeSecs() or ''">
+                s_max_age_secs="getSMaxAgeSecs() is not None and getSMaxAgeSecs() or ''"
+                last_modified_checked="getLastModified() and 'checked' or ''"
+                pre_check="test(getPreCheck() is None, '', getPreCheck())"
+                post_check="test(getPostCheck() is None, '', getPostCheck())">
 
        <input type="hidden" name="policy_id" value="&dtml-getPolicyId;">
        <input type="hidden" name="no_cache:default:int" value="0">
@@ -35,6 +38,7 @@
        <input type="hidden" name="private:default:int" value="0">
        <input type="hidden" name="no_transform:default:int" value="0">
        <input type="hidden" name="enable_304s:default:int" value="0">
+       <input type="hidden" name="last_modified:default:int" value="0">
 
        <table>
 
@@ -54,11 +58,7 @@
                 size="40">
        </td>
 
-       <th align="right"> No-cache? </th>
-       <td>
-         <input type="checkbox" name="no_cache:int"
-                                value="1" &dtml-nc_checked;>
-       </td>
+       <td colspan="2">&nbsp;</td>
        </tr>
 
        <tr valign="top">
@@ -70,6 +70,21 @@
                 size="40">
        </td>
 
+       <th align="right">Last-Modified?</th>
+       <td>
+         <input type="checkbox"
+                name="last_modified:int"
+                value="1" &dtml-last_modified_checked;>
+       </td>
+       </tr>
+
+       <tr valign="top">
+       <th align="right"> No-cache? </th>
+       <td>
+         <input type="checkbox" name="no_cache:int"
+                                value="1" &dtml-nc_checked;>
+       </td>
+
        <th align="right"> No-store? </th>
        <td>
          <input type="checkbox" name="no_store:int"
@@ -108,6 +123,22 @@
        </tr>
 
        <tr valign="top">
+       <th align="right"> Pre-check (secs) </th>
+       <td>
+         <input type="text"
+                name="pre_check"
+                value="&dtml-pre_check;">
+       </td>
+
+       <th align="right"> Post-check (secs) </th>
+       <td>
+         <input type="text"
+                name="post_check"
+                value="&dtml-post_check;">
+       </td>
+       </tr>
+
+       <tr valign="top">
        <th align="right"> Vary </th>
        <td>
          <input type="text"
@@ -185,6 +216,7 @@
   <td align="left">
     <form action="&dtml-absolute_url;">
 
+       <input type="hidden" name="last_modified:default:int" value="0">
        <input type="hidden" name="no_cache:default:int" value="0">
        <input type="hidden" name="no_store:default:int" value="0">
        <input type="hidden" name="must_revalidate:default:int" value="0">
@@ -209,10 +241,7 @@
          <input type="text" name="predicate" size="40">
        </td>
 
-       <th align="right"> No-cache? </th>
-       <td>
-         <input type="checkbox" name="no_cache:int" value="1">
-       </td>
+       <td colspan="2">&nbsp;</td>
        </tr>
 
        <tr valign="top">
@@ -221,6 +250,18 @@
          <input type="text" name="mtime_func" size="40">
        </td>
 
+       <th align="right"> Last-Modified? </th>
+       <td>
+         <input type="checkbox" name="last_modified:int" value="1">
+       </td>
+       </tr>
+
+       <tr>
+       <th align="right"> No-cache? </th>
+       <td>
+         <input type="checkbox" name="no_cache:int" value="1">
+       </td>
+
        <th align="right"> No-store? </th>
        <td>
          <input type="checkbox" name="no_store:int" value="1">
@@ -256,6 +297,22 @@
        </tr>
 
        <tr valign="top">
+       <th align="right"> Pre-check (secs) </th>
+       <td>
+         <input type="text"
+                name="pre_check"
+                value="">
+       </td>
+
+       <th align="right"> Post-check (secs) </th>
+       <td>
+         <input type="text"
+                name="post_check"
+                value="">
+       </td>
+       </tr>
+
+       <tr valign="top">
        <th align="right"> Vary </th>
        <td>
          <input type="text" name="vary" size="40">

Modified: CMF/branches/1.4/CMFCore/tests/test_CachingPolicyManager.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/test_CachingPolicyManager.py	2005-09-29 16:33:36 UTC (rev 38688)
+++ CMF/branches/1.4/CMFCore/tests/test_CachingPolicyManager.py	2005-09-29 17:07:04 UTC (rev 38689)
@@ -49,13 +49,13 @@
     __allow_access_to_unprotected_subobjects__ = 1
 
     def __init__(self, modified ):
-        self.modified = modified 
+        self.modified = modified
 
     def Type( self ):
         return 'Dummy'
 
     def modified( self ):
-        return self.modified 
+        return self.modified
 
 
 class CachingPolicyTests( TestCase ):
@@ -73,9 +73,8 @@
         from Products.CMFCore.CachingPolicyManager import createCPContext
         return createCPContext( DummyContent2(self._epoch)
                               , 'foo_view', kw, self._epoch )
-        
+
     def test_empty( self ):
-
         policy = self._makePolicy( 'empty' )
         context = self._makeContext()
         headers = policy.getHeaders( context )
@@ -159,7 +158,7 @@
         headers = policy.getHeaders( context )
 
         self.assertEqual( len( headers ), 0 )
-        
+
     def test_mtimeFunc( self ):
 
         policy = self._makePolicy( 'mtimeFunc'
@@ -171,7 +170,7 @@
         self.assertEqual( headers[0][0], 'Last-modified' )
         self.assertEqual( headers[0][1]
                         , rfc1123_date(ACCLARK.timeTime()) )
-        
+
     def test_mtimeFuncNone( self ):
 
         policy = self._makePolicy( 'mtimeFuncNone'
@@ -180,7 +179,7 @@
         headers = policy.getHeaders( context )
 
         self.assertEqual( len( headers ), 0 )
-        
+
     def test_maxAge( self ):
 
         policy = self._makePolicy( 'aged', max_age_secs=86400 )
@@ -196,7 +195,7 @@
                         , rfc1123_date((self._epoch+1).timeTime()) )
         self.assertEqual( headers[2][0].lower() , 'cache-control' )
         self.assertEqual( headers[2][1] , 'max-age=86400' )
-        
+
     def test_sMaxAge( self ):
 
         policy = self._makePolicy( 's_aged', s_max_age_secs=86400 )
@@ -238,7 +237,7 @@
                         , rfc1123_date(self._epoch.timeTime()) )
         self.assertEqual( headers[1][0].lower() , 'cache-control' )
         self.assertEqual( headers[1][1] , 'no-store' )
-        
+
     def test_mustRevalidate( self ):
 
         policy = self._makePolicy( 'mustRevalidate', must_revalidate=1 )
@@ -308,6 +307,45 @@
         self.assertEqual( headers[1][1] , 'no-transform' )
         self.assertEqual(policy.getNoTransform(), 1)
 
+    def test_lastModified( self ):
+
+        policy = self._makePolicy( 'lastModified', last_modified=0 )
+        context = self._makeContext()
+        headers = policy.getHeaders( context )
+
+        self.assertEqual( len( headers ), 0 )
+        self.assertEqual(policy.getLastModified(), 0)
+
+    def test_preCheck( self ):
+
+        policy = self._makePolicy( 'preCheck', pre_check=1 )
+        context = self._makeContext()
+        headers = policy.getHeaders( context )
+
+        self.assertEqual( len( headers ), 2 )
+        self.assertEqual( headers[0][0].lower() , 'last-modified' )
+        self.assertEqual( headers[0][1]
+                        , rfc1123_date(self._epoch.timeTime()) )
+        self.assertEqual( headers[1][0].lower() , 'cache-control' )
+        self.assertEqual( headers[1][1] , 'pre-check=1' )
+        self.assertEqual(policy.getPreCheck(), 1)
+        self.assertEqual(policy.getPostCheck(), None)
+
+    def test_postCheck( self ):
+
+        policy = self._makePolicy( 'postCheck', post_check=1 )
+        context = self._makeContext()
+        headers = policy.getHeaders( context )
+
+        self.assertEqual( len( headers ), 2 )
+        self.assertEqual( headers[0][0].lower() , 'last-modified' )
+        self.assertEqual( headers[0][1]
+                        , rfc1123_date(self._epoch.timeTime()) )
+        self.assertEqual( headers[1][0].lower() , 'cache-control' )
+        self.assertEqual( headers[1][1] , 'post-check=1' )
+        self.assertEqual(policy.getPostCheck(), 1)
+        self.assertEqual(policy.getPreCheck(), None)
+
     def test_ETag( self ):
 
         # With an empty etag_func, no ETag should be produced
@@ -388,12 +426,12 @@
                          , 'xyzzy', None, None, None, None, None, None, '', '', None, None, None, None, None )
         self.assertRaises( KeyError, mgr._removePolicy, 'xyzzy' )
         self.assertRaises( KeyError, mgr._reorderPolicy, 'xyzzy', -1 )
-    
+
     def test_addAndUpdatePolicy( self ):
 
         mgr = self._makeOne()
         mgr.addPolicy( 'first', 'python:1', 'mtime', 1, 0, 1, 0, 'vary',
-                       'etag', None, 2, 1, 0, 1, 0 )
+                       'etag', None, 2, 1, 0, 1, 0, 1, 0, 2, 3 )
         p = mgr._policies['first']
         self.assertEqual(p.getPolicyId(), 'first')
         self.assertEqual(p.getPredicate(), 'python:1')
@@ -409,8 +447,13 @@
         self.assertEqual(p.getPublic(), 0)
         self.assertEqual(p.getPrivate(), 1)
         self.assertEqual(p.getNoTransform(), 0)
-        
-        mgr.updatePolicy( 'first', 'python:0', 'mtime2', 2, 1, 0, 1, 'vary2', 'etag2', None, 1, 0, 1, 0, 1 )
+        self.assertEqual(p.getEnable304s(), 1)
+        self.assertEqual(p.getLastModified(), 0)
+        self.assertEqual(p.getPreCheck(), 2)
+        self.assertEqual(p.getPostCheck(), 3)
+
+        mgr.updatePolicy( 'first', 'python:0', 'mtime2', 2, 1, 0, 1, 'vary2',
+                          'etag2', None, 1, 0, 1, 0, 1, 0, 1, 3, 2 )
         p = mgr._policies['first']
         self.assertEqual(p.getPolicyId(), 'first')
         self.assertEqual(p.getPredicate(), 'python:0')
@@ -426,6 +469,10 @@
         self.assertEqual(p.getPublic(), 1)
         self.assertEqual(p.getPrivate(), 0)
         self.assertEqual(p.getNoTransform(), 1)
+        self.assertEqual(p.getEnable304s(), 0)
+        self.assertEqual(p.getLastModified(), 1)
+        self.assertEqual(p.getPreCheck(), 3)
+        self.assertEqual(p.getPostCheck(), 2)
 
     def test_reorder( self ):
 
@@ -561,7 +608,7 @@
         self.portal = DummySite(id='portal').__of__(self.root)
         self.portal._setObject('portal_types', DummyTool())
 
-        # This is a FSPageTemplate that will be used as the View for 
+        # This is a FSPageTemplate that will be used as the View for
         # our content objects. It doesn't matter what it returns.
         path = os.path.join(self.skin_path_name, 'testPT2.pt')
         self.portal._setObject('dummy_view', FSPageTemplate('dummy_view', path))
@@ -575,7 +622,7 @@
         newSecurityManager(None, user)
         owner_auth = '%s:%s' % (portal_owner, password)
         self.auth_header = "Basic %s" % base64.encodestring(owner_auth)
-        
+
         self.portal._setObject('doc1', DummyContent('doc1'))
         self.portal._setObject('doc2', DummyContent('doc2'))
         self.portal._setObject('doc3', DummyContent('doc3'))
@@ -599,7 +646,7 @@
                       etag_func = '',
                       enable_304s = 1)
 
-        # This policy only applies to doc2. It will emit an ETag with 
+        # This policy only applies to doc2. It will emit an ETag with
         # the constant value "abc" and also enable if-modified-since handling.
         cpm.addPolicy(policy_id = 'policy_etag',
                       predicate = 'python:object.getId()=="doc2"',
@@ -673,7 +720,7 @@
         # want the full rendering. This must return a 304 response.
         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc1.modified_date)
         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
-        doc1.dummy_view() 
+        doc1.dummy_view()
         self.assertEqual(response.getStatus(), 304)
         self._cleanup()
 
@@ -685,7 +732,7 @@
         self.assertEqual(response.getStatus(), 200)
         self._cleanup()
 
-        # We are asking for an ETag as well as modifications after doc2 has 
+        # We are asking for an ETag as well as modifications after doc2 has
         # been created. Both won't match and wwe get the full rendering.
         request.environ['IF_NONE_MATCH'] = '"123"'
         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc1.modified_date)
@@ -693,8 +740,8 @@
         doc1.dummy_view()
         self.assertEqual(response.getStatus(), 200)
         self._cleanup()
-        
 
+
     def testConditionalGETETag(self):
         yesterday = DateTime() - 1
         doc2 = self.portal.doc2
@@ -736,8 +783,8 @@
         doc2.dummy_view()
         self.assertEqual(response.getStatus(), 304)
         self._cleanup()
-        
-        # We specify an ETag and a modification time condition that dooes not 
+
+        # We specify an ETag and a modification time condition that dooes not
         # match, so we get the full rendering
         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc2.modified_date)
         request.environ['IF_NONE_MATCH'] = '"123"'
@@ -755,7 +802,7 @@
         doc2.dummy_view()
         self.assertEqual(response.getStatus(), 200)
         self._cleanup()
-        
+
         # Now we pass an ETag that matches the policy and a modified time
         # condition that is not fulfilled. It is safe to serve a 304.
         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc2.modified_date)
@@ -765,7 +812,7 @@
         self.assertEqual(response.getStatus(), 304)
         self._cleanup()
 
-        
+
     def testConditionalGETDisabled(self):
         yesterday = DateTime() - 1
         doc3 = self.portal.doc3
@@ -779,7 +826,7 @@
         doc3.dummy_view()
         self.assertEqual(response.getStatus(), 200)
         self._cleanup()
-        
+
         # Now both the ETag and the modified condition would trigger a 304
         # response *if* 304-handling was enabled. It is not in our policy, so
         # we get the full rendering again.
@@ -790,7 +837,7 @@
         self.assertEqual(response.getStatus(), 200)
         self._cleanup()
 
-        
+
 def test_suite():
     return TestSuite((
         makeSuite(CachingPolicyTests),

Modified: CMF/branches/1.4/CMFCore/utils.py
===================================================================
--- CMF/branches/1.4/CMFCore/utils.py	2005-09-29 16:33:36 UTC (rev 38688)
+++ CMF/branches/1.4/CMFCore/utils.py	2005-09-29 17:07:04 UTC (rev 38689)
@@ -398,9 +398,9 @@
         # no appropriate policy or 304s not enabled
         return False 
 
-    (content_mod_time, content_etag) = ret
+    (content_mod_time, content_etag, set_last_modified_header) = ret
     if content_mod_time:
-        mod_time_secs = content_mod_time.timeTime()
+        mod_time_secs = long(content_mod_time.timeTime())
     else:
         mod_time_secs = None
     
@@ -445,7 +445,7 @@
             return False
 
     response = REQUEST.RESPONSE
-    if content_mod_time:
+    if content_mod_time and set_last_modified_header:
         response.setHeader('Last-modified', str(content_mod_time))
     if content_etag:
         response.setHeader('ETag', content_etag, literal=1)



More information about the CMF-checkins mailing list