[Zope3-checkins] SVN: Zope3/branches/stub-session/src/zope/app/session/ Recover from screwed svn commit wierdness

Stuart Bishop zen at shangri-la.dropbear.id.au
Mon Jul 5 18:10:57 EDT 2004


Log message for revision 26103:
Recover from screwed svn commit wierdness


-=-
Deleted: Zope3/branches/stub-session/src/zope/app/session/README.txt
===================================================================
--- Zope3/branches/stub-session/src/zope/app/session/README.txt	2004-07-05 21:58:09 UTC (rev 26102)
+++ Zope3/branches/stub-session/src/zope/app/session/README.txt	2004-07-05 22:10:57 UTC (rev 26103)
@@ -1,148 +0,0 @@
-Sessions
-========
-
-Sessions provide a way to temporarily associate information with a
-client without requiring the authentication of a principal.  We
-associate an identifier with a particular client. Whenever we get a
-request from that client, we compute the identifier and use the
-identifier to look up associated information, which is stored on the
-server. 
-
-A major disadvantage of sessions is that they require management of
-information on the server. This can have major implications for
-scalability.  It is possible for a framework to make use of session
-data very easy for the developer.  This is great if scalability is not
-an issue, otherwise, it is a booby trap.
-
-Design Issues
--------------
-
-Sessions introduce a number of issues to be considered:
-
-- Clients have to be identified. A number of approaches are possible,
-  including: 
-
-  o Using HTTP cookies. The application assigns a client identifier,
-    which is stored in a cookie.  This technique is the most
-    straightforward, but can be defeated if the client does not
-    support HTTP cookies (usually because the feature has been
-    disabled).
-
-  o Using URLs.  The application assigns a client identifier, which is
-    stored in the URL.  This makes URLs a bit uglier and requires some
-    care. If people copy URLs and send them to others, then you could
-    end up with multiple clients with the same session
-    identifier. There are a number of ways to reduce the risk of
-    accidental reuse of session identifiers:
-
-    - Embed the client IP address in the identifier
-
-    - Expire the identifier
-
-  o Use hidden form variables.  This complicates applications. It
-    requires all requests to be POST requests and requires the
-    maintenance of the hidden variables.
-
-  o Use the client IP address
-
-    This doesn't work very well, because an IP address may be shared by
-    many clients.
-
-- Data storage
-
-  Data can be simply stored in the object database. This provides lots
-  of flexibility. You can store pretty much anything you want as long
-  as it is persistent. You get the full benefit of the object database,
-  such as transactions, transparency, clustering, and so on.  Using
-  the object database is especially useful when:
-
-  - Writes are infrequent
-
-  - Data are complex
-
-  If writes are frequent, then the object database introduces
-  scalability problems.  Really, any transactional database is likely
-  to introduce problems with frequent writes. If you are tempted to
-  update session data on every request, think very hard about it.  You
-  are creating a scalability problem.
-
-  If you know that scalability is not (and never will be) an issue,
-  you can just use the object database.
- 
-  If you have client data that needs to be updated often (as in every
-  request), consider storing the data on the client.  (Like all data
-  received from a client, it may be tainted and, in most instances,
-  should not be trusted. Sensitive information that the user should
-  not see should likewise not be stored on the client, unless
-  encrypted with a key the client has no access to.)  If you can't
-  store it on the client, then consider some other storage mechanism,
-  like a fast database, possibly without transaction support.
-
-  You may be tempted to store session data in memory for speed.  This
-  doesn't turn out to work very well.  If you need scalability, then
-  you need to be able to use an application-server cluster and storage
-  of session data in memory defeats that.  You can use
-  "server-affinity" to assure that requests from a client always go
-  back to the same server, but not all load balancers support server
-  affinity, and, for those that do, enabling server affinity tends to
-  defeat load balancing.
-
-- Session expiration
-
-  You may wish to ensure that sessions terminate after some period of
-  time. This may be for security reasons, or to avoid accidental
-  sharing of a session among multiple clients.  The policy might be
-  expressed in terms of total session time, or maximum inactive time,
-  or some combination.
-
-  There are a number of ways to approach this.  You can expire client
-  ids. You can expire session data.
-
-- Data expiration
-
-  Because HTTP is a stateless protocol, you can't tell whether a user
-  is thinking about a task or has simply stopped working on it.  Some
-  means is needed to free server session storage that is no-longer needed.
-
-  The simplest strategy is to never remove data. This strategy has
-  some obvious disadvantages.  Other strategies can be viewed as
-  optimizations of the basic strategy.  It is important to realize that
-  a data expiration strategy can be informed by, but need not be
-  constrained by a session-expiration strategy.
-
-Application programming interface
----------------------------------
-
-Application code will merely adapt request objects to a session data
-interface.  Initially, we will define the session data interface
-`IPersistentSessionData'. `IPersistentSessionData` provides a mapping
-interface. Keys in the mapping are application identifiers. Values are
-persistent mapping objects.
-
-Application code that wants to get object session data for a request
-adapts the request to `IPersistentSessionData`::
-
-  data = IPersistentSessionData(request)[appkey]
-
-where `appkey` is a dotted name that identifies the application, such
-as ``zope.app.actionplan``.  Given the session data, the application
-can then store data in it::
-
-  data['original'] = original_actions
-  data['new'] = new_actions
-
-From ZPT, you can access session data using adapter syntax::
-
-  request*PersistentSession
-
-So, for example, to access the `old` key for the session data for the
-sample key above:
-
-  request*PersistentSession/zope.app.actionplan/old
-
-In this example, the data for an aplication key was a mapping object.
-The semantics of a session data for a particular application key are
-determined by the session data type interface.
-`IPersistentSessionData` defines the application data to be a mapping
-object by default.  Other data interfaces could specify different
-bahavior.  

Copied: Zope3/branches/stub-session/src/zope/app/session/api.txt (from rev 26102, Zope3/branches/stub-session/src/zope/app/session/session.stx)

Copied: Zope3/branches/stub-session/src/zope/app/session/design.txt (from rev 26102, Zope3/branches/stub-session/src/zope/app/session/README.txt)

Deleted: Zope3/branches/stub-session/src/zope/app/session/persist.py
===================================================================
--- Zope3/branches/stub-session/src/zope/app/session/persist.py	2004-07-05 21:58:09 UTC (rev 26102)
+++ Zope3/branches/stub-session/src/zope/app/session/persist.py	2004-07-05 22:10:57 UTC (rev 26103)
@@ -1,259 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-#
-##############################################################################
-"""
-Session implementation using cookies
-
-$Id$
-"""
-import sha, time, string, random, hmac, warnings, thread
-from UserDict import IterableUserDict
-from heapq import heapify, heappop
-
-from persistent import Persistent
-from zope.server.http.http_date import build_http_date
-from zope.interface import implements
-from zope.component import ComponentLookupError
-from zope.app.zapi import getUtility
-from BTrees.OOBTree import OOBTree
-from zope.app.utility.interfaces import ILocalUtility
-from zope.app.annotation.interfaces import IAttributeAnnotatable
-
-from interfaces import \
-        IBrowserIdManager, IBrowserId, ICookieBrowserIdManager, \
-        ISessionDataContainer, ISession, ISessionProductData, ISessionData
-
-import ZODB
-import ZODB.MappingStorage
-
-cookieSafeTrans = string.maketrans("+/", "-.")
-
-def digestEncode(s):
-    """Encode SHA digest for cookie."""
-    return s.encode("base64")[:-2].translate(cookieSafeTrans)
-
-
-class BrowserId(str):
-    """See zope.app.interfaces.utilities.session.IBrowserId"""
-    implements(IBrowserId)
-
-    def __new__(cls, request):
-        return str.__new__(
-                cls, getUtility(IBrowserIdManager).getBrowserId(request)
-                )
-
-
-class CookieBrowserIdManager(Persistent):
-    """Session service implemented using cookies."""
-
-    implements(IBrowserIdManager, ICookieBrowserIdManager,
-               ILocalUtility, IAttributeAnnotatable,
-               )
-
-    __parent__ = __name__ = None
-
-    def __init__(self):
-        self.namespace = "zope3_cs_%x" % (int(time.time()) - 1000000000)
-        self.secret = "%.20f" % random.random()
-        self.cookieLifetime = None
-
-    def generateUniqueId(self):
-        """Generate a new, random, unique id."""
-        data = "%.20f%.20f%.20f" % (random.random(), time.time(), time.clock())
-        digest = sha.sha(data).digest()
-        s = digestEncode(digest)
-        # we store a HMAC of the random value together with it, which makes
-        # our session ids unforgeable.
-        mac = hmac.new(s, self.secret, digestmod=sha).digest()
-        return s + digestEncode(mac)
-
-    def getRequestId(self, request):
-        """Return the browser id encoded in request as a string, 
-        or None if it's non-existent."""
-        # If there is an id set on the response, use that but don't trust it.
-        # We need to check the response in case there has already been a new
-        # session created during the course of this request.
-        response_cookie = request.response.getCookie(self.namespace)
-        if response_cookie:
-            sid = response_cookie['value']
-        else:
-            sid = request.cookies.get(self.namespace)
-        if sid is None or len(sid) != 54:
-            return None
-        s, mac = sid[:27], sid[27:]
-        if (digestEncode(hmac.new(s, self.secret, digestmod=sha).digest())
-            != mac):
-            return None
-        else:
-            return sid
-
-    def setRequestId(self, request, id):
-        """Set cookie with id on request."""
-        # XXX Currently, the path is the ApplicationURL. This is reasonable,
-        #     and will be adequate for most purposes.
-        #     A better path to use would be that of the folder that contains
-        #     the service-manager this service is registered within. However,
-        #     that would be expensive to look up on each request, and would
-        #     have to be altered to take virtual hosting into account.
-        #     Seeing as this service instance has a unique namespace for its
-        #     cookie, using ApplicationURL shouldn't be a problem.
-
-        if self.cookieLifetime is not None:
-            if self.cookieLifetime:
-                expires = build_http_date(time.time() + self.cookieLifetime)
-            else:
-                expires = 'Tue, 19 Jan 2038 00:00:00 GMT'
-            request.response.setCookie(
-                    self.namespace, id, expires=expires,
-                    path=request.getApplicationURL(path_only=True)
-                    )
-        else:
-            request.response.setCookie(
-                    self.namespace, id,
-                    path=request.getApplicationURL(path_only=True)
-                    )
-
-    def getBrowserId(self, request):
-        """See zope.app.session.interfaces.IBrowserIdManager"""
-        sid = self.getRequestId(request)
-        if sid is None:
-            sid = self.generateUniqueId()
-        self.setRequestId(request, sid)
-        return sid
-
-
-class PersistentSessionDataContainer(Persistent, IterableUserDict):
-    ''' A SessionDataContainer that stores data in the ZODB '''
-    __parent__ = __name__ = None
-
-    implements(ISessionDataContainer, ILocalUtility, IAttributeAnnotatable)
-
-    def __init__(self):
-        self.data = OOBTree()
-        self.sweepInterval = 5*60
-
-    def __getitem__(self, product_id):
-        rv = IterableUserDict.__getitem__(self, product_id)
-        now = time.time()
-        # Only update lastAccessTime once every few minutes, rather than
-        # every hit, to avoid ZODB bloat and conflicts
-        if rv.lastAccessTime + self.sweepInterval < now:
-            rv.lastAccessTime = int(now)
-            # XXX: When scheduler exists, this method should just schedule
-            # a sweep later since we are currently busy handling a request
-            # and may end up doing simultaneous sweeps
-            self.sweep()
-        return rv
-
-    def __setitem__(self, product_id, session_data):
-        session_data.lastAccessTime = int(time.time())
-        return IterableUserDict.__setitem__(self, product_id, session_data)
-
-    def sweep(self):
-        ''' Clean out stale data '''
-        expire_time = time.time() - self.sweepInterval
-        heap = [(v.lastAccessTime, k) for k,v in self.data.items()]
-        heapify(heap)
-        while heap:
-            lastAccessTime, key = heappop(heap)
-            if lastAccessTime < expire_time:
-                del self.data[key]
-            else:
-                return
-
-
-class RAMSessionDataContainer(PersistentSessionDataContainer):
-    ''' A SessionDataContainer that stores data in RAM. Currently session
-        data is not shared between Zope clients, so server affinity will
-        need to be maintained to use this in a ZEO cluster.
-    '''
-    def __init__(self):
-        self.sweepInterval = 5*60
-        self.key = sha.new(str(time.time() + random.random())).hexdigest()
-
-    _ram_storage = ZODB.MappingStorage.MappingStorage()
-    _ram_db = ZODB.DB(_ram_storage)
-    _conns = {}
-
-    def _getData(self):
-
-        # Open a connection to _ram_storage per thread
-        tid = thread.get_ident()
-        if not self._conns.has_key(tid):
-            self._conns[tid] = self._ram_db.open()
-
-        root = self._conns[tid].root()
-        if not root.has_key(self.key):
-            root[self.key] = OOBTree()
-        return root[self.key]
-
-    data = property(_getData, None)
-
-    def sweep(self):
-        super(RAMSessionDataContainer, self).sweep()
-        self._ram_db.pack(time.time())
-
-
-class Session:
-    """See zope.app.session.interfaces.ISession"""
-    implements(ISession)
-    __slots__ = ('browser_id',)
-    def __init__(self, request):
-        self.browser_id = str(IBrowserId(request))
-
-    def __getitem__(self, product_id):
-        """See zope.app.session.interfaces.ISession"""
-
-        # First locate the ISessionDataContainer by looking up
-        # the named Utility, and falling back to the unnamed one.
-        try:
-            sdc = getUtility(ISessionDataContainer, product_id)
-        except ComponentLookupError:
-            # XXX: Do we want this?
-            warnings.warn(
-                    'Unable to find ISessionDataContainer named %s. '
-                    'Using default' % repr(product_id),
-                    RuntimeWarning
-                    )
-            sdc = getUtility(ISessionDataContainer)
-
-        # The ISessionDataContainer contains two levels:
-        # ISessionDataContainer[product_id] == ISessionProductData
-        # ISessionDataContainer[product_id][browser_id] == ISessionData
-        try:
-            spd = sdc[product_id]
-        except KeyError:
-            sdc[product_id] = SessionProductData()
-            spd = sdc[product_id]
-
-        try:
-            return spd[self.browser_id]
-        except KeyError:
-            spd[self.browser_id] = SessionData()
-            return spd[self.browser_id]
-
-
-class SessionProductData(Persistent, IterableUserDict):
-    """See zope.app.session.interfaces.ISessionProductData"""
-    implements(ISessionProductData)
-    lastAccessTime = 0
-    def __init__(self):
-        self.data = OOBTree()
-
-
-class SessionData(Persistent, IterableUserDict):
-    """See zope.app.session.interfaces.ISessionData"""
-    implements(ISessionData)
-    def __init__(self):
-        self.data = OOBTree()
-

Deleted: Zope3/branches/stub-session/src/zope/app/session/session.stx
===================================================================
--- Zope3/branches/stub-session/src/zope/app/session/session.stx	2004-07-05 21:58:09 UTC (rev 26102)
+++ Zope3/branches/stub-session/src/zope/app/session/session.stx	2004-07-05 22:10:57 UTC (rev 26103)
@@ -1,108 +0,0 @@
-Zope3 Session Implementation
-============================
-
-Overview
---------
-
-Sessions allow us to fake state over a stateless protocol - HTTP. We do this
-by having a unique identifier stored across multiple HTTP requests, be it
-a cookie or some id mangled into the URL.
-
-The IClientIdManager Utility provides this unique id. It is responsible
-for propagating this id so that future requests from the client get
-the same id (eg. by setting an HTTP cookie). This utility is used 
-when we adapt the request to the unique client id:
-
-    >>> client_id = IClientId(request)
-
-The ISession adapter gives us a mapping that can be used to store 
-and retrieve session data. A unique key (the package id) is used
-to avoid namespace clashes:
-
-    >>> pkg_id = 'products.foo'
-    >>> session = ISession(request)[pkg_id]
-    >>> session['color'] = 'red'
-
-    >>> session2 = ISession(request)['products.bar']
-    >>> session2['color'] = 'blue'
-
-    >>> session['color']
-    'red'
-    >>> session2['color']
-    'blue'
-
-
-Data Storage
-------------
-   
-The actual data is stored in an ISessionDataContainer utility.
-ISession chooses which ISessionDataContainer should be used by
-looking up as a named utility using the package id. This allows
-the site administrator to configure where the session data is actually
-stored by adding a registration for desired ISessionDataContainer
-with the correct name.
-
-    >>> sdc = zapi.getUtility(ISessionDataContainer, pkg_id)
-    >>> sdc[client_id][pkg_id] is session
-    True
-    >>> sdc[client_id][pkg_id]['color']
-    'red'
-
-If no ISessionDataContainer utility can be located by name using the
-package id, then the unnamed ISessionDataContainer utility is used as
-a fallback. An unnamed ISessionDataContainer is automatically created
-for you, which may replaced with a different implementation if desired.
-
-    >>> ISession(request)['unknown'] \
-    ...     is zapi.getUtility(ISessionDataContainer)[client_id]['unknown']
-    True
-
-The ISessionDataContainer contains ISessionData objects, and ISessionData
-objects in turn contain ISessionPkgData objects. You should never need
-to know this unless you are writing administrative views for the session
-machinery.
-
-    >>> ISessionData.providedBy(sdc[client_id])
-    True
-    >>> ISessionPkgData.providedBy(sdc[client_id][pkg_id])
-    True
-
-The ISessionDataContainer is responsible for expiring session data.
-The expiry time can be configured by settings its 'timeout' attribute.
-
-    >>> sdc.timeout = 1200 # 1200 seconds or 20 minutes
-
-
-Restrictions
-------------
-
-Data stored in the session must be persistent or picklable.
-
-    >>> session['oops'] = open(__file__)
-    >>> import transaction
-    >>> transaction.commit()
-    Traceback (most recent call last):
-        [...]
-    TypeError: can't pickle file objects
-
-
-Page Templates
---------------
-
-    Session data may be accessed in page template documents using the
-    TALES adaptor syntax::
-
-        <span tal:content="request*Session/products.foo/color | default">
-            green
-        </span>
-
-        <div tal:define="session request*Session/products.foo">
-            <tal:x condition="not:exists:session/count">
-                <tal:x condition="python: session['count'] = 1" />
-            </tal:x>
-            <tal:x condition="exists:session/count">
-                <tal:x condition="python: session['count'] += 1" />
-            </tal:x>
-            <span content="session/count">6</span>
-        </div>
-

Modified: Zope3/branches/stub-session/src/zope/app/session/tests.py
===================================================================
--- Zope3/branches/stub-session/src/zope/app/session/tests.py	2004-07-05 21:58:09 UTC (rev 26102)
+++ Zope3/branches/stub-session/src/zope/app/session/tests.py	2004-07-05 22:10:57 UTC (rev 26103)
@@ -82,9 +82,7 @@
 
     >>> tearDown()
 
-    ''' % (open(
-        os.path.join(os.path.dirname(__file__), 'session.stx')
-        ).read(),)
+    ''' % (open(os.path.join(os.path.dirname(__file__), 'api.txt')).read(),)
 
 def test_suite():
     return unittest.TestSuite((



More information about the Zope3-Checkins mailing list