[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