Session storage
===============

Sessioning provides a persistant storage for arbitrary data between user
requests. Typically the user is sent a cookie containing a randomly generated
identification string in the response to the first request to the WSGI
application. This session cookie is used to identify the browser
session and Pesto makes a data store available in the variable
``request.session`` that is persistent between requests in the same session.

A middleware layer must be added to the application that deals with setting and
checking the session cookie.

As an alternative to using cookies the session identfier may also be passed
around in the querystring portion of the URL, however this requires special
care when writing the application to include the session identifier in every
URL generated by your application.

.. testsetup:: *

        from pesto.session.memorysessionmanager import MemorySessionManager
        from pesto.testing import TestApp
        import pesto
        from pesto import Response
        @pesto.to_wsgi
        def app(request):
                return Response(["<html>Whoa Nelly!</html>"])
        original_app = app

        def FakeFileSessionManager(path):
                return MemorySessionManager()


Here's an example application using sessions:

.. testcode::

    from pesto import dispatcher_app
    dispatcher = dispatcher_app()

    @dispatcher.match('/login', 'POST')
    def login(request):

        username = request.get('username')
        password = request.get('password')

        if is_valid(username, password):
            request.session['username'] = username
            request.session['logged_in'] = True


    @dispatcher.match('/secure-area', 'GET')
    def secure_area(request):

        if not request.session.get('logged_in'):
            return response.redirect(login.url())

        return ["Welcome to the secure area, %s" % request.session['username']]


    from pesto.session.filesessionmanager import FileSessionManager

    # Create a file based sessioning middleware, that runs a purge every 600s
    # for sessions older than 1800s..
    sessioning = pesto.session_middleware(
        FileSessionManager('/path/to/session/store'),
        auto_purge_every=600,
        auto_purge_olderthan=1800
    )

    application = dispatcher
    application = sessioning(application)

Sessioning needs some kind of data storage backend. The two backends available
in Pesto are memory and file backed storage.

File backend
------------

Synopsis:

.. testcode::

    from pesto.session.filesessionmanager import FileSessionManager
    app = pesto.session_middleware(FileSessionManager('/path/to/session/store'))(app)


This backend is the most generally useful storage backend. Session data is
stored in one file per session, using Python's ``pickle`` module to store and
retrieve data from disk. The application must have access to a writable
directory, which for security reasons should not be readable by other users.

Memory backend
----------------

Synopsis:

.. testcode::

    from pesto.session.memorysessionmanager import MemorySessionManager
    app = pesto.session_middleware(MemorySessionManager())(app)

This implementation stores data directly in a python data structure stored in
memory. Session data can't be shared between processes and is lost when the
process finishes, so it is not useful for CGI or architectures that do not use
long-running processes or architectures that split requests between multiple
processes. In general it is recommended to use the file backend which does not
suffer from these problems.

Preserving state between requests
====================================

There are two built in methods for associating a session with a user request,
using either HTTP cookies or the URI querystring.

Cookie persistence
------------------

This is the default method and is usually preferred as it is transparent both
to users and the application developer. However not all browsers accept cookies
and cookies cannot easily be passed between applications running on different
domains, for these reasons you may still prefer the querystring based approach.

Cookie based persistence is the default method so no extra code is required to
enable this:

.. testcode::

        app = pesto.session_middleware(FileSessionManager('/path/to/session/store'))(app)


Setting the cookie path and domain
```````````````````````````````````

When using cookie based persistence cookies are by default tied to the path
and domain on which the application is running. To override this, you can
specify the cookie path and domain when constructing the session middleware:

.. testcode::
        :hide:

        app = original_app
        FileSessionManager = FakeFileSessionManager

.. testcode::

        app = pesto.session_middleware(
                FileSessionManager('/path/to/session/store'),
                cookie_path='/',
                cookie_domain='.example.org',
        )(app)

.. testcode::
        :hide:

        response = TestApp(app).get('/foo', SCRIPT_NAME='/foo')
        print response.get_headers('Set-Cookie')

.. testoutput::
        :hide:

        ...Path=/;Domain=.example.org...

If you are using the ``filesessionmanager`` this could be used to share session
state between WSGI applications running on the same server mounted on different
subdomains or paths.


Querystring persistence
------------------------

To set up a pesto application with querystring based persistence:

.. testcode::
        :hide:

        app = original_app
        FileSessionManager = FakeFileSessionManager


.. testcode::

        app = pesto.session_middleware(
                FileSessionManager('/path/to/session/store'),
                persist='querystring'
        )(app)

.. testcode::
        :hide:

        response = TestApp(app).get('/foo', SCRIPT_NAME='/foo')
        print response.get_headers('Location')

.. testoutput::
        :hide:

        .../foo?pesto_session=...


When querystrings are used it is necessary for all links generated by the
application to contain a reference to the session id. Thus code to generate a
link would typically look like this (this example uses the `Genshi <http://genshi.edgewall.org/>`_ page template syntax)::

        <a href="/some-page?pesto_session=${request.session.session_id}">...</a>

Pesto doesn't contain any code to support rewriting links output by your
application, it is up to you to ensure the querystring is always present in
links.

Any ``GET`` requests that do not contain a session id will be redirected by the
session middleware to a URL containing a fresh session identifier if one is not
already present.

Session module API documention
-------------------------------

.. automodule:: pesto.session.base
        :members:

.. automodule:: pesto.session.memorysessionmanager
        :members:

.. automodule:: pesto.session.filesessionmanager
        :members:
