Enhanced SMI-enabled HTTP Caching of Silva Objects
The silva-caching metadata set shouldn't map make associations with Cache Managers, where the cache policy is set through the ZMI. Instead, the silva-caching set should contain properties for the cache policy and there should be one http_cache_manager for _all_ silva objects.
It should be possible to have a cache policy for each base Silva interface:
ISilvaObject (e.g. a 'global' policy)
IContainer
IAsset
IContent
IVersionedContent
The cache policy for each interface is the number of seconds to cache (-1 disables caching).
Additionally, there will be a "Notify URLs (via PURGE)" property, to support purging intermediate (e.g. SQUID) caching proxies.
The custom Silva HTTP Cache Manager I wrote will be adjusted to look at the metadata settings for the appropriate interface.
There is still the problem that some types of content (especially versionedcontent, e.g. SilvaDocuments) shouldn't be cached at all. An example is a SilvaDocument with a dynamic code source. There is already an 'is_cacheable" method defined on ISilvaObject, but this method's symantics seem to indicate 'in-memory' caching. HTTP Caching is different, in that the cache isn't stored in memory on the server (to reduce rendering time), but the result is stored in a user-agent's cache or a caching proxy.
I propose a new method is_http_cacheable be added to ISilvaObject. This method could by default fallback to returning the value if is_cacheable. But, this isn't always what we want...SilvaDocuments with non-cacheable external sources perhaps should be cacheable in the browser...I think more thought will need to go into this part.
TuomasT on IRC implemented the following, to set the Last-Modified and Cache-Control headers:
if hasattr(obj,'is_cacheable'):
if obj.is_cacheable():
if obj.implements_versioned_content():
dt = DateTime.toZone(obj.get_public_version_publication_datetime(),'GMT')
request.RESPONSE.setHeader('Last-Modified', '%s, %02d %s %04d %02d:%02d:%02d GMT' % (dt.Day()[:3], dt.day(), dt.Month()[:3], dt.year(),dt.hour(), dt.minute(), dt.second()))
else:
dt = DateTime.toZone(obj.get_modification_datetime(),'GMT')
request.RESPONSE.setHeader('Last-Modified', '%s, %02d %s %04d %02d:%02d:%02d GMT' % (dt.Day()[:3], dt.day(), dt.Month()[:3], dt.year(),dt.hour(), dt.minute(), dt.second()))
request.RESPONSE.setHeader('Cache-Control','public')
Note that 'Last-Modified' is set to the publication (or modification) date, and cache-control is set to public (rather than the silva default of 300 seconds), enabling client caches to keep their cache much longer.
This may be only be half of his solution, as Silva also needs to understand and handle the 'if-modified-since' http request header.