Policy HTTP Cache Manager
=========================
The idea behind the HTTP Cache Manager is that instead of having a
hardcoded 'Expires' header being set and getting the 'Last-Modified'
header wrong like the 'Accelerated HTTP Cache Manager', instead we do
delegate down to the 'Caching Policy Manager' which is in charge of
all policy.
Let's test this with several different objects to make sure we behave
accordingly here.
>>> from DateTime import DateTime
>>> from Products.CMFCore.utils import getToolByName
Make sure the 'caching_policy_manager' exists:
>>> cpm = getToolByName(self.portal, 'caching_policy_manager', None)
>>> if cpm is None:
... adding = self.portal.manage_addProduct['CMFCore']
... adding.manage_addCachingPolicyManager()
... cpm = getToolByName(self.portal, 'caching_policy_manager')
Create a Policy HTTP Cache Manager:
>>> adding = self.portal.manage_addProduct['PolicyHTTPCacheManager']
>>> adding.manage_addPolicyHTTPCacheManager('PolicyCache')
Create a property on the portal root that is a date and can be
acquired by the caching policy to ease our tests:
>>> def add_mod_date(ob, sec):
... setattr(ob, 'test_mod_date',
... DateTime('2001-01-01 00:00:%2d GMT' % sec))
>>> add_mod_date(self.portal, 0)
Setup a very simple caching policy that we can verify to have been
triggered later on:
>>> cpm.addPolicy(policy_id="test", predicate="python: True",
... mtime_func="content/test_mod_date", vary="",
... etag_func="", max_age_secs=42,
... s_max_age_secs=None, pre_check=None,
... post_check=None, last_modified=True,
... no_cache=False, no_store=False,
... must_revalidate=False,
... proxy_revalidate=False,
... no_transform=False, public=False,
... private=False, enable_304s=False)
ZODB-Based View Tests
---------------------
Page Template Tests
+++++++++++++++++++
Create a Page Template:
>>> adding = self.portal.manage_addProduct['PageTemplates']
>>> _ = adding.manage_addPageTemplate('test.pt',
... text="")
Make sure it's not assigned to any cache manager:
>>> pt = self.portal['test.pt']
>>> pt.ZCacheable_setManagerId(None)
>>> pt.ZCacheable_getCache()
Publish the Page Template:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % pt.absolute_url(1))
HTTP/1.1 200 OK
Content-Length: 4
Content-Type: text/html
Foo
Now associate the Page Template with our PolicyCache:
>>> pt.ZCacheable_setManagerId('PolicyCache')
Add a different modification date to the Page Template to make sure
`content` does not evaluate to the Page Template:
>>> add_mod_date(pt, 1)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % pt.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 4
Content-Type: text/html
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
Foo
DTML Document Tests
+++++++++++++++++++
Create a DTML Document:
>>> adding = self.portal.manage_addProduct['OFSP']
>>> _ = adding.manage_addDTMLDocument('test_document.dtml',
... file="Bar")
Make sure it's not assigned to any cache manager:
>>> doc = self.portal['test_document.dtml']
>>> doc.ZCacheable_setManagerId(None)
>>> doc.ZCacheable_getCache()
Publish the DTML Document:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % doc.absolute_url(1))
HTTP/1.1 200 OK
Content-Length: 3
Content-Type: text/plain
Bar
Now associate the DTML Document with our PolicyCache:
>>> doc.ZCacheable_setManagerId('PolicyCache')
Add a different modification date to the DTML Document to make sure
`content` does not evaluate to it:
>>> add_mod_date(doc, 3)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % doc.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 3
Content-Type: text/plain
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
Bar
DTML Method Tests
+++++++++++++++++
Create a DTML Method:
>>> adding = self.portal.manage_addProduct['OFSP']
>>> _ = adding.manage_addDTMLMethod('test_method.dtml',
... file="Bar")
Make sure it's not assigned to any cache manager:
>>> meth = self.portal['test_method.dtml']
>>> meth.ZCacheable_setManagerId(None)
>>> meth.ZCacheable_getCache()
Publish the DTML Method:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % meth.absolute_url(1))
HTTP/1.1 200 OK
Content-Length: 3
Content-Type: text/plain
Bar
Now associate the DTML Method with our PolicyCache:
>>> meth.ZCacheable_setManagerId('PolicyCache')
Add a different modification date to the DTML Method to make sure
`content` does not evaluate to it:
>>> add_mod_date(meth, 3)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % meth.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 3
Content-Type: text/plain
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
Bar
ZODB-Based Content Tests
------------------------
OFS.Image Tests
+++++++++++++++
Create a Image:
>>> adding = self.portal.manage_addProduct['OFSP']
>>> _ = adding.manage_addImage('test_image.png',
... file="Image")
Make sure it's not assigned to any cache manager:
>>> img = self.portal['test_image.png']
>>> img.ZCacheable_setManagerId(None)
>>> img.ZCacheable_getCache()
Publish the Image. Note Image 'forces' a ``Last-Modified`` header:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % img.absolute_url(1))
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 5
Content-Type: image/png
Last-Modified: ...
Image
Now associate the Image with our PolicyCache:
>>> img.ZCacheable_setManagerId('PolicyCache')
Add a different modification date to the Image to make sure
`content` *does* evaluate to it:
>>> add_mod_date(img, 4)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % img.absolute_url(1))
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=42
Content-Length: 5
Content-Type: image/png
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:04 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
Image
OFS.File Tests
++++++++++++++
Create a File:
>>> adding = self.portal.manage_addProduct['OFSP']
>>> _ = adding.manage_addFile('test_file.txt',
... file="File")
Make sure it's not assigned to any cache manager:
>>> fl = self.portal['test_file.txt']
>>> fl.ZCacheable_setManagerId(None)
>>> fl.ZCacheable_getCache()
Publish the File. Note File 'forces' a ``Last-Modified`` header:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % fl.absolute_url(1))
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 4
Content-Type: text/plain
Last-Modified: ...
File
Now associate the File with our PolicyCache:
>>> fl.ZCacheable_setManagerId('PolicyCache')
Add a different modification date to the File to make sure
`content` *does* evaluate to it:
>>> add_mod_date(fl, 4)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % fl.absolute_url(1))
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=42
Content-Length: 4
Content-Type: text/plain
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:04 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
File
CMF Content Tests
-----------------
Now let's try something mixed. A view is usually applied to CMF
content, so we are actually testing here that the 'default view' for a
content object gets it's caching headers set correctly.
CMF Document Tests
++++++++++++++++++
Create a CMF Document:
>>> adding = self.portal.manage_addProduct['CMFDefault']
>>> _ = adding.addDocument('test_document.txt',
... text="Document")
>>> cmf_doc = self.portal['test_document.txt']
>>> cmf_doc._setPortalTypeName('Document')
Publish the CMF Document, actually publishes the 'default view' for
the CMF Document.:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % cmf_doc.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: text/html
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
Add a different modification date to the CMF Document to make sure
`content` *does* evaluate to it:
>>> add_mod_date(cmf_doc, 6)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % cmf_doc.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: text/html
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:06 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
CMF News Item Tests
+++++++++++++++++++
Create a CMF News Item:
>>> adding = self.portal.manage_addProduct['CMFDefault']
>>> _ = adding.addNewsItem('test_news.txt',
... text="News Item")
>>> cmf_news = self.portal['test_news.txt']
>>> cmf_news._setPortalTypeName('News Item')
Publish the CMF News Item, actually publishes the 'default view' for
the CMF News Item.:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % cmf_news.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: text/html
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
Add a different modification date to the CMF News Item to make sure
`content` *does* evaluate to it:
>>> add_mod_date(cmf_news, 6)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % cmf_news.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: text/html
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:06 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
CMF File Tests
++++++++++++++
Create a CMF File:
>>> adding = self.portal.manage_addProduct['CMFDefault']
>>> _ = adding.addFile('test_cmf_file.txt',
... file="File")
>>> cmf_file = self.portal['test_cmf_file.txt']
>>> cmf_file._setPortalTypeName('File')
Publish the CMF File, actually publishes the 'default view' for
the CMF File, which works just like a normal File:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % cmf_file.absolute_url(1))
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 4
Content-Type: text/plain
Last-Modified: ...
File
Now associate the CMF File with our PolicyCache. This can be only done
with CMF File and CMF Image:
>>> cmf_file.ZCacheable_setManagerId('PolicyCache')
Add a different modification date to the CMF File to make sure
`content` *does* evaluate to it:
>>> add_mod_date(cmf_file, 6)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % cmf_file.absolute_url(1))
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=42
Content-Length: 4
Content-Type: text/plain
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:06 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
File
CMF Image Tests
+++++++++++++++
Create a CMF Image:
>>> adding = self.portal.manage_addProduct['CMFDefault']
>>> _ = adding.addImage('test_cmf_image.png',
... file="Image")
>>> cmf_image = self.portal['test_cmf_image.png']
>>> cmf_image._setPortalTypeName('Image')
Publish the CMF Image, actually publishes the 'default view' for
the CMF Image, which works just like a normal Image:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % cmf_image.absolute_url(1))
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 5
Content-Type: image/png
Last-Modified: ...
Image
Now associate the CMF Image with our PolicyCache. This can be only done
with CMF Image and CMF Image:
>>> cmf_image.ZCacheable_setManagerId('PolicyCache')
Add a different modification date to the CMF Image to make sure
`content` *does* evaluate to it:
>>> add_mod_date(cmf_image, 6)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % cmf_image.absolute_url(1))
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=42
Content-Length: 5
Content-Type: image/png
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:06 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
Image
Filesystem-Based View Tests
---------------------------
Setup a Filesystem Directory View for testing FSPageTemplate and
FSDTMLMethod:
>>> from Products.CMFCore.tests.base.testcase import _prefix
>>> from Products.CMFCore.DirectoryView import registerDirectory
>>> from Products.CMFCore.DirectoryView import addDirectoryViews
>>> registerDirectory('fake_skins', _prefix)
>>> addDirectoryViews(self.portal, 'fake_skins', _prefix)
Filesystem Page Template Tests
++++++++++++++++++++++++++++++
Get a FSPageTemplate from the Directory View and make sure it's not
associated with any Cache Manager:
>>> pt = self.portal.fake_skin['testPT']
>>> pt.ZCacheable_setManagerId(None)
>>> pt.ZCacheable_getCache()
Publish the FSPageTemplate, note that even though it's not associated
with any Cache Manager, it calls `_setCacheHeaders` on it's own and
that's why the headers get set:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % pt.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 0
Content-Type: text/html
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
Now associate the Page Template with our PolicyCache:
>>> pt.ZCacheable_setManagerId('PolicyCache')
No difference:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % pt.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 0
Content-Type: text/html
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
Add a different modification date to the Page Template to make sure
`content` does not evaluate to the Page Template:
>>> add_mod_date(pt, 3)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % pt.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 0
Content-Type: text/html
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
Filesystem DTML Method Tests
++++++++++++++++++++++++++++++
Get a FSDTMLMethod from the Directory View and make sure it's not
associated with any Cache Manager:
>>> meth = self.portal.fake_skin['test_dtml']
>>> meth.ZCacheable_setManagerId(None)
>>> meth.ZCacheable_getCache()
Publish the FSDTMLMethod. It will forcibly call `_setCacheHeaders`
even though it's not associated with a Cache Manager when called
``TTW`` like this:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % meth.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 22
Content-Type: text/plain
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
This is a dtml method
Now associate the FSDTMLMethod with our PolicyCache:
>>> meth.ZCacheable_setManagerId('PolicyCache')
No difference:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % meth.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 22
Content-Type: text/plain
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
This is a dtml method
Add a different modification date to the FSDTMLMethod to make sure
`content` does not evaluate to the FSDTMLMethod:
>>> add_mod_date(meth, 3)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % meth.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: 22
Content-Type: text/plain
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
This is a dtml method
Filesystem Image Tests
++++++++++++++++++++++
Get a FSImage from the Directory View and make sure it's not
associated with any Cache Manager:
>>> image = self.portal.fake_skin['test_image.gif']
>>> image.ZCacheable_setManagerId(None)
>>> image.ZCacheable_getCache()
Publish the FSImage. It will forcibly call `_setCacheHeaders`
even though it's not associated with a Cache Manager:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % image.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: image/gif
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
Now associate the FSImage with our PolicyCache:
>>> image.ZCacheable_setManagerId('PolicyCache')
No difference:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % image.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: image/gif
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
Add a different modification date to the FSImage to make sure
`content` *does* evaluate to the FSImage:
>>> add_mod_date(image, 8)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % image.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: image/gif
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:08 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
Filesystem File Tests
++++++++++++++++++++++
Get a FSFile from the Directory View and make sure it's not
associated with any Cache Manager:
>>> fl = self.portal.fake_skin['test_file.swf']
>>> fl.ZCacheable_setManagerId(None)
>>> fl.ZCacheable_getCache()
Publish the FSFile. It will forcibly call `_setCacheHeaders`
even though it's not associated with a Cache Manager:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % fl.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: application/x-shockwave-flash
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
Now associate the FSFile with our PolicyCache:
>>> fl.ZCacheable_setManagerId('PolicyCache')
No difference:
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % fl.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: application/x-shockwave-flash
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:00 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...
Add a different modification date to the FSFile to make sure
`content` *does* evaluate to the FSFile:
>>> add_mod_date(fl, 8)
>>> print http(r"""
... GET /%s/ HTTP/1.1
... """ % fl.absolute_url(1))
HTTP/1.1 200 OK
Cache-Control: max-age=42
Content-Length: ...
Content-Type: application/x-shockwave-flash
Expires: ...
Last-Modified: Mon, 01 Jan 2001 00:00:08 GMT
X-Cache-Headers-Set-By: CachingPolicyManager: /cmf/caching_policy_manager
...