[Checkins] SVN: z3ext.cacheheaders/ initial import
Nikolay Kim
fafhrd at datacom.kz
Fri Mar 28 06:25:24 EDT 2008
Log message for revision 84985:
initial import
Changed:
A z3ext.cacheheaders/
A z3ext.cacheheaders/branches/
A z3ext.cacheheaders/tags/
A z3ext.cacheheaders/trunk/
A z3ext.cacheheaders/trunk/AUTHOR.txt
A z3ext.cacheheaders/trunk/CHANGES.txt
A z3ext.cacheheaders/trunk/LICENSE.txt
A z3ext.cacheheaders/trunk/bootstrap.py
A z3ext.cacheheaders/trunk/buildout.cfg
A z3ext.cacheheaders/trunk/setup.py
A z3ext.cacheheaders/trunk/src/
A z3ext.cacheheaders/trunk/src/z3ext/
A z3ext.cacheheaders/trunk/src/z3ext/__init__.py
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/README.txt
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/__init__.py
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/configure.zcml
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/default.py
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/dynamiccache.py
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/interfaces.py
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/publication.py
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/siteuid.py
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/staticcache.py
A z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/tests.py
-=-
Property changes on: z3ext.cacheheaders
___________________________________________________________________
Name: svn:ignore
+ bin
develop-eggs
eggs
parts
coverage
.installed.cfg
Property changes on: z3ext.cacheheaders/trunk
___________________________________________________________________
Name: svn:ignore
+ bin
develop-eggs
eggs
parts
coverage
.installed.cfg
Added: z3ext.cacheheaders/trunk/AUTHOR.txt
===================================================================
--- z3ext.cacheheaders/trunk/AUTHOR.txt (rev 0)
+++ z3ext.cacheheaders/trunk/AUTHOR.txt 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1 @@
+Nikolay Kim (fafhrd91 <at> gmail <dot> com)
Added: z3ext.cacheheaders/trunk/CHANGES.txt
===================================================================
--- z3ext.cacheheaders/trunk/CHANGES.txt (rev 0)
+++ z3ext.cacheheaders/trunk/CHANGES.txt 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,16 @@
+=======
+CHANGES
+=======
+
+1.0.1 (2008-03-28)
+------------------
+
+- Added buldout config for tests
+
+- Code moved to svn.zope.org
+
+
+1.0.0 (2008-02-02)
+------------------
+
+- Initial release
Added: z3ext.cacheheaders/trunk/LICENSE.txt
===================================================================
--- z3ext.cacheheaders/trunk/LICENSE.txt (rev 0)
+++ z3ext.cacheheaders/trunk/LICENSE.txt 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,54 @@
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+ accompanying copyright notice, this list of conditions,
+ and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+ copyright notice, this list of conditions, and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+ endorse or promote products derived from this software
+ without prior written permission from the copyright
+ holders.
+
+4. The right to distribute this software or to use it for
+ any purpose does not give you the right to use
+ Servicemarks (sm) or Trademarks (tm) of the copyright
+ holders. Use of them is covered by separate agreement
+ with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+ files to carry prominent notices stating that you changed
+ the files and the date of any change.
+
+Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
+ AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
Added: z3ext.cacheheaders/trunk/bootstrap.py
===================================================================
--- z3ext.cacheheaders/trunk/bootstrap.py (rev 0)
+++ z3ext.cacheheaders/trunk/bootstrap.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Property changes on: z3ext.cacheheaders/trunk/bootstrap.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/buildout.cfg
===================================================================
--- z3ext.cacheheaders/trunk/buildout.cfg (rev 0)
+++ z3ext.cacheheaders/trunk/buildout.cfg 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,18 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3ext.cacheheaders [test]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = z3ext.cacheheaders [test]
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
Added: z3ext.cacheheaders/trunk/setup.py
===================================================================
--- z3ext.cacheheaders/trunk/setup.py (rev 0)
+++ z3ext.cacheheaders/trunk/setup.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup for z3ext.cacheheaders package
+
+$Id$
+"""
+import sys, os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+version = '1.0.1dev'
+
+setup(name='z3ext.cacheheaders',
+ version=version,
+ description="Cache headers manager",
+ long_description=(
+ 'Detailed Dcoumentation\n' +
+ '======================\n'
+ + '\n\n' +
+ read('src', 'z3ext', 'cacheheaders', 'README.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ author='Nikolay Kim',
+ author_email='fafhrd91 at gmail.com',
+ url='http://z3ext.net/',
+ license='ZPL 2.1',
+ packages=find_packages('src'),
+ package_dir = {'':'src'},
+ namespace_packages=['z3ext'],
+ install_requires = ['setuptools',
+ 'zope.proxy',
+ 'zope.event',
+ 'zope.schema',
+ 'zope.datetime',
+ 'zope.component',
+ 'zope.interface',
+ 'zope.publisher',
+ 'zope.dublincore',
+ 'zope.cachedescriptors',
+ 'zope.app.publication',
+ ],
+ extras_require = dict(test=['zope.app.testing',
+ 'zope.testing',
+ 'zope.traversing',
+ 'zope.security',
+ 'zope.app.security',
+ ]),
+ include_package_data = True,
+ zip_safe = False
+ )
Property changes on: z3ext.cacheheaders/trunk/setup.py
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/__init__.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/__init__.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/__init__.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,6 @@
+# namespace package boilerplate
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError, e:
+ from pkgutil import extend_path
+ __path__ = extend_path(__path__, __name__)
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/README.txt
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/README.txt (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/README.txt 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,164 @@
+=======================
+Browser caching headers
+=======================
+
+`Publication` with support `304` response code
+
+ >>> import time, datetime, pytz
+ >>> from zope import component, interface
+ >>> from z3ext.cacheheaders import interfaces
+
+ >>> from z3ext.cacheheaders.publication import \
+ ... BrowserFactory, BrowserPublication
+
+ >>> factory = BrowserFactory()
+ >>> factory.canHandle({})
+ True
+
+ >>> rclass, pclass = factory()
+ >>> pclass == BrowserPublication
+ True
+
+ >>> pub = BrowserPublication(None)
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> from zope.publisher.interfaces.http import IHTTPRequest
+
+ >>> request = TestRequest()
+ >>> response = request.response
+
+ >>> request.setPublication(pub)
+
+We need object
+
+ >>> class IContent(interface.Interface):
+ ... pass
+
+ >>> class Content(object):
+ ... interface.implements(IContent)
+ ... marker = 0
+ ...
+ ... def __call__(self):
+ ... self.marker = 1
+ ... return 'Content body'
+
+ >>> ob = Content()
+
+ >>> print pub.callObject(request, ob)
+ Content body
+
+
+If we want control caching headers we need define ICacheStrategy adapter.
+this package predefine some cache strategies. Simplest is StatusCache, it's
+base on object modification date (IModificationInfo interface)
+So to use static cache fist we need IModificationInfo adapter
+
+ >>> from z3ext.cacheheaders.staticcache import getStaticCache
+ >>> getStaticCache(ob) is None
+ True
+
+ >>> class ModInfo(object):
+ ... interface.implements(interfaces.IModificationInfo)
+ ... component.adapts(IContent)
+ ...
+ ... dt = datetime.datetime(2007, 10, 10, 0, 0, 0, 0, tzinfo=pytz.utc)
+ ...
+ ... def __init__(self, context):
+ ... self.context = context
+ ...
+ ... def modified(self):
+ ... return long(time.mktime(self.dt.utctimetuple()))
+
+ >>> component.provideAdapter(ModInfo)
+
+ >>> strategy = getStaticCache(ob).__bind__(request)
+ >>> interfaces.ICacheStrategy.providedBy(strategy)
+ True
+
+This strategy uses 'If-Modified-Since' request header.
+
+ >>> request._environ['IF_MODIFIED_SINCE'] = 'Mon, 10 Dec 2007 00:00:00 GMT'
+ >>> strategy.isModified()
+ False
+
+ >>> request._environ['IF_MODIFIED_SINCE'] = 'Mon, 10 Sep 2007 00:00:00 GMT'
+ >>> strategy.isModified()
+ True
+
+If format is wrong isModified is always True
+
+ >>> request._environ['IF_MODIFIED_SINCE'] = 'wrong format'
+ >>> strategy.isModified()
+ True
+
+ >>> request._environ['IF_MODIFIED_SINCE'] = 'Mon, 10 Dec 2007 00:00:00 GMT'
+
+
+Static Cache set 'Cache-Control', 'Expires', 'Last-Modified' headers for browser
+
+ >>> strategy.setCacheHeaders()
+
+ >>> response.getHeader('Cache-Control')
+ 'public,max-age=86400'
+
+ >>> response.getHeader('Expires')
+ '..., ... GMT'
+
+ >>> response.getHeader('Last-Modified')
+ 'Wed, 10 Oct 2007 00:00:00 GMT'
+
+It also should automaticly convert modified date to utc timezone
+
+ >>> ModInfo.dt = datetime.datetime(2007, 10, 10, 0, 0, 0, 0)
+
+ >>> strategy = getStaticCache(ob).__bind__(request)
+ >>> strategy.setCacheHeaders()
+ >>> response.getHeader('Last-Modified')
+ 'Wed, 10 Oct 2007 00:00:00 GMT'
+
+ >>> ModInfo.dt = datetime.datetime(
+ ... 2007, 10, 9, 18, 0, 0, 0, pytz.timezone('Asia/Almaty'))
+
+ >>> strategy = getStaticCache(ob).__bind__(request)
+ >>> strategy.setCacheHeaders()
+ >>> response.getHeader('Last-Modified')
+ 'Wed, 10 Oct 2007 00:00:00 GMT'
+
+ >>> ModInfo.dt = datetime.datetime(2007, 10, 10, 0, 0, 0, 0, tzinfo=pytz.utc)
+
+To use this cache we have to register adater
+
+ >>> component.provideAdapter(getStaticCache, (IContent,))
+
+Now we can use this strategy in publication.
+If content doesn't changed we should get just 304 response code
+
+ >>> print pub.callObject(request, ob)
+ <BLANKLINE>
+
+ >>> response.getStatus()
+ 304
+
+Or content
+
+ >>> response.setStatus('200')
+ >>> request._environ['IF_MODIFIED_SINCE'] = 'Mon, 10 Sep 2007 00:00:00 GMT'
+
+ >>> print pub.callObject(request, ob)
+ Content body
+
+ >>> response.getStatus()
+ 200
+
+ >>> response.getHeader('Last-Modified')
+ 'Wed, 10 Oct 2007 00:00:00 GMT'
+
+
+Additional feature, calculating duration of call.
+
+ >>> pub.beforeTraversal(request)
+ >>> time.sleep(2)
+ >>> pub.afterCall(request, ob)
+
+ >>> response.getHeader('X-Generated-Time')
+ '... sec'
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/__init__.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/__init__.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/__init__.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/configure.zcml
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/configure.zcml (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/configure.zcml 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,33 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <include package="zope.app.publication" />
+
+ <publisher
+ name="BROWSER"
+ factory=".publication.BrowserFactory"
+ methods="GET HEAD"
+ mimetypes="*"
+ priority="9" />
+
+ <!-- cache -->
+ <utility
+ provides=".interfaces.ISiteUID"
+ factory=".siteuid.SiteUID" />
+
+ <class class=".siteuid.SiteUID">
+ <allow attributes="uid" />
+ </class>
+
+ <class class=".siteuid.PersistentSiteUID">
+ <allow attributes="uid" />
+ </class>
+
+ <adapter
+ for="zope.location.interfaces.ILocation"
+ factory=".default.ModificationInfo" />
+
+ <adapter
+ for="zope.component.interfaces.IView"
+ factory=".default.viewModificationInfo" />
+
+</configure>
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/default.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/default.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/default.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,43 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import time
+from zope import interface
+from zope.security.proxy import removeSecurityProxy
+from zope.dublincore.interfaces import ICMFDublinCore
+
+from interfaces import IModificationInfo
+
+
+class ModificationInfo(object):
+ interface.implements(IModificationInfo)
+
+ def __init__(self, context):
+ dc = ICMFDublinCore(context, None)
+ if dc is not None:
+ self.time = long(time.mktime(dc.modified.utctimetuple()))
+ else:
+ context = removeSecurityProxy(context)
+ mtime = getattr(context, '_p_mtime', 0)
+ self.time = long(mtime)
+
+ def modified(self, default=long(0)):
+ return self.time
+
+ at interface.implementer(IModificationInfo)
+def viewModificationInfo(view):
+ return IModificationInfo(view.context)
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/default.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/dynamiccache.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/dynamiccache.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/dynamiccache.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,112 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import time
+from zope import interface
+from zope.component import getUtility
+from zope.datetime import time as timeFromDateTimeString
+
+from interfaces import ISiteUID
+from interfaces import IModificationInfo
+from interfaces import IETagCacheStrategy
+
+
+class ETagBased(object):
+ interface.implements(IETagCacheStrategy)
+
+ def __init__(self, context):
+ self.context = context
+
+ def __bind__(self, request):
+ self.request = request
+ self.response = request.response
+ self.buildETag()
+ return self
+
+ def buildETag(self):
+ raise NotImplemented
+
+ def isModified(self):
+ request = self.request
+ header = request.getHeader('HTTP_IF_NONE_MATCH', None, True)
+ if header is not None:
+ if self.etag == header:
+ return False
+
+ return True
+
+ def setNotModifiedHeaders(self):
+ response = self.response
+
+ secs = 86400
+ response.setHeader('Cache-Control', 'public,max-age=%s' % secs)
+ response.setHeader('ETag', self.etag)
+
+ def setCacheHeaders(self):
+ response = self.response
+
+ secs = 86400
+ response.setHeader('Cache-Control', 'public,max-age=%s' % secs)
+ t = time.time() + secs
+ response.setHeader('Expires',
+ time.strftime("%a, %d %b %Y %H:%M:%S GMT",
+ time.gmtime(t)))
+ response.setHeader('ETag', self.etag)
+
+
+class ETagWithSiteUID(ETagBased):
+
+ def buildETag(self):
+ """ etag format: {Last modified}|{site uid}"""
+ context = self.context
+ siteuid = getUtility(ISiteUID)
+
+ modinfo = IModificationInfo(context, None)
+ if modinfo is not None:
+ self.etag = 'W/"%d|%s"'%(modinfo.modified(), siteuid.uid)
+ else:
+ self.etag = 'W/"%s|%s"'%(context.__name__, siteuid.uid)
+
+
+class ETagForPrincipal(ETagBased):
+
+ def buildETag(self):
+ """ etag format: {Last modified}|{site uid}"""
+ context = self.context
+ siteuid = getUtility(ISiteUID)
+
+ pid = self.request.principal.id
+
+ modinfo = IModificationInfo(context, None)
+ if modinfo is not None:
+ self.etag = 'W/"%s|%d|%s"'%(pid, modinfo.modified(), siteuid.uid)
+ else:
+ self.etag = 'W/"%s|%s|%s"'%(pid, context.__name__, siteuid.uid)
+
+ def setNotModifiedHeaders(self):
+ response = self.response
+
+ response.setHeader('ETag', self.etag)
+ response.setHeader('Cache-Control', 'public,must-revalidate')
+ response.setHeader('Vary', 'Accept-Encoding, Accept-Language')
+
+ def setCacheHeaders(self):
+ response = self.response
+
+ response.setHeader('ETag', self.etag)
+ response.setHeader('Cache-Control', 'public,must-revalidate')
+ response.setHeader('Vary', 'Accept-Encoding, Accept-Language')
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/dynamiccache.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/interfaces.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/interfaces.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/interfaces.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,67 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from zope import schema, interface
+
+
+class IModificationInfo(interface.Interface):
+
+ def modified():
+ """modification date (long)"""
+
+
+class ISiteUID(interface.Interface):
+
+ uid = schema.TextLine(
+ title = u'UID',
+ readonly = True)
+
+ def generate():
+ """ regenate uid """
+
+
+class ICacheStrategy(interface.Interface):
+ """ cache strategy """
+
+ def __bind__(request):
+ """ bind strategry for specific request """
+
+ def isModified():
+ """ check view modification """
+
+ def setCacheHeaders():
+ """ add cache headers to response """
+
+ def setNotModifiedHeaders():
+ """ add cache headers for not modified content """
+
+
+class IStaticCacheStrategy(ICacheStrategy):
+ """ caching for static resources,
+ based on Last-Modified header """
+
+
+class IETagCacheStrategy(ICacheStrategy):
+ """ caching based on ETag """
+
+ etag = schema.TextLine(
+ title = u'ETag',
+ description = u'ETag for context.',
+ required = True)
+
+ def buildETag():
+ """ build etag for current context """
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/interfaces.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/publication.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/publication.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/publication.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,81 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from types import MethodType
+from datetime import datetime
+
+from zope import interface
+from zope.event import notify
+from zope.component import queryUtility
+from zope.proxy import removeAllProxies
+
+from zope.publisher.publish import mapply
+from zope.publisher.browser import BrowserRequest
+
+from zope.app.publication import browser
+from zope.app.publication.interfaces import IBrowserRequestFactory
+from zope.app.publication.interfaces import IRequestPublicationFactory
+
+from interfaces import ICacheStrategy
+
+
+class BrowserPublication(browser.BrowserPublication):
+
+ def beforeTraversal(self, request):
+ self.dt = datetime.now()
+ super(BrowserPublication, self).beforeTraversal(request)
+
+ def callObject(self, request, ob):
+ if request.method == 'GET':
+ orig = removeAllProxies(ob)
+ if type(orig) is MethodType:
+ strategy = ICacheStrategy(orig.im_self, None)
+ else:
+ strategy = ICacheStrategy(orig, None)
+
+ if strategy is not None:
+ strategy = strategy.__bind__(request)
+ if not strategy.isModified():
+ request.response.setStatus(304)
+ strategy.setNotModifiedHeaders()
+ return ''
+
+ result = mapply(ob, request.getPositionalArguments(), request)
+ strategy.setCacheHeaders()
+ return result
+
+ return mapply(ob, request.getPositionalArguments(), request)
+
+ def afterCall(self, request, ob):
+ td = datetime.now() - self.dt
+ secs = (td.days * 86400 + td.seconds) + (0.000001 * td.microseconds)
+
+ request.response.setHeader('X-Generated-Time', '%0.5f sec'%secs)
+
+ return super(BrowserPublication, self).afterCall(request, ob)
+
+
+class BrowserFactory(object):
+ interface.implements(IRequestPublicationFactory)
+
+ def canHandle(self, environment):
+ return True
+
+ def __call__(self):
+ request_class = queryUtility(
+ IBrowserRequestFactory, default=BrowserRequest)
+ return request_class, BrowserPublication
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/publication.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/siteuid.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/siteuid.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/siteuid.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import md5
+import time
+import persistent
+from zope import interface
+from zope.cachedescriptors.property import Lazy
+
+from interfaces import ISiteUID
+
+
+class SiteUID(object):
+ interface.implements(ISiteUID)
+
+ @Lazy
+ def uid(self):
+ return self.generate()
+
+ def generate(self):
+ self.uid = md5.md5(time.ctime()).hexdigest()
+
+
+class PersistentSiteUID(persistent.Persistent, SiteUID):
+ """ persistent site uid """
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/siteuid.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/staticcache.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/staticcache.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/staticcache.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" static cache
+
+$Id$
+"""
+import time
+from pytz import utc
+from datetime import datetime
+
+from zope import interface
+from zope.datetime import rfc1123_date, parseDatetimetz
+from zope.datetime import time as timeFromDateTimeString
+from zope.datetime import weekday_abbr, monthname
+
+from interfaces import IModificationInfo
+from interfaces import IStaticCacheStrategy
+
+
+ at interface.implementer(IStaticCacheStrategy)
+def getStaticCache(context):
+ info = IModificationInfo(context, None)
+ if info is not None:
+ return StaticCache(context, info)
+ else:
+ return None
+
+
+class StaticCache(object):
+ interface.implements(IStaticCacheStrategy)
+
+ maxage = 86400
+
+ def __init__(self, context, info):
+ self.context = context
+ self.info = info
+ self.modified = info.modified()
+
+ def __bind__(self, request):
+ self.request = request
+ self.response = request.response
+ return self
+
+ def isModified(self):
+ if self.modified == 0:
+ return True
+
+ request = self.request
+ header = request.getHeader('IF_MODIFIED_SINCE', None, True)
+
+ if header is not None:
+ header = header.split(';')[0]
+ try: mod_since=long(timeFromDateTimeString(header))
+ except: mod_since=None
+
+ if mod_since is not None:
+ lmt = self.info.modified()
+ if lmt > 0 and lmt <= mod_since:
+ return False
+
+ return True
+
+ def setNotModifiedHeaders(self):
+ pass
+
+ def setCacheHeaders(self):
+ modified = self.modified
+ if modified == 0:
+ return
+
+ response = self.response
+
+ response.setHeader('Cache-Control', 'public,max-age=%s' % self.maxage)
+
+ t = time.time() + self.maxage
+
+ response.setHeader(
+ 'Expires', time.strftime("%a, %d %b %Y %H:%M:%S GMT",
+ time.gmtime(t)))
+
+ if not response.getHeader('Last-Modified', None):
+ year, month, day, hh, mm, ss, wd, y, z = time.localtime(modified)
+
+ lmod = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (weekday_abbr[wd],
+ day, monthname[month],
+ year, hh, mm, ss)
+ response.setHeader('Last-Modified', lmod)
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/staticcache.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/tests.py
===================================================================
--- z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/tests.py (rev 0)
+++ z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/tests.py 2008-03-28 10:25:24 UTC (rev 84985)
@@ -0,0 +1,51 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""test setup
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import doctest, unittest
+from zope import component
+from zope.traversing import testing
+from zope.app.testing import placelesssetup
+from zope.traversing.interfaces import ITraversable
+from zope.app.security import principalregistry
+from zope.security.management import endInteraction
+from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import IFallbackUnauthenticatedPrincipal
+
+def setUp(test):
+ placelesssetup.setUp()
+ testing.setUp()
+
+ endInteraction()
+
+ principal = principalregistry.UnauthenticatedPrincipal('anon','anon','')
+ component.provideUtility(
+ principal, IFallbackUnauthenticatedPrincipal)
+ component.provideUtility(
+ principalregistry.principalRegistry, IAuthentication)
+
+def tearDown(test):
+ placelesssetup.tearDown()
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite(
+ 'README.txt',
+ setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+ ))
Property changes on: z3ext.cacheheaders/trunk/src/z3ext/cacheheaders/tests.py
___________________________________________________________________
Name: svn:keywords
+ Id
More information about the Checkins
mailing list