This file is indexed.

/usr/share/doc/quixote1-doc/session-mgmt.html is in quixote1-doc 1.2-6.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
<?xml version="1.0" encoding="us-ascii" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
<title>Quixote Session Management</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head>
<body>
<div class="document" id="quixote-session-management">
<h1 class="title">Quixote Session Management</h1>
<p>HTTP was originally designed as a stateless protocol, meaning that every
request for a document or image was conducted in a separate TCP
connection, and that there was no way for a web server to tell if two
separate requests actually come from the same user.  It's no longer
necessarily true that every request is conducted in a separate TCP
connection, but HTTP is still fundamentally stateless.  However, there
are many applications where it is desirable or even essential to
establish a &quot;session&quot; for each user, ie. where all requests performed by
that user are somehow tied together on the server.</p>
<p>HTTP cookies were invented to address this requirement, and they are
still the best solution for establishing sessions on top of HTTP.  Thus,
Quixote's session management mechanism is cookie-based.  (The most
common alternative is to generate long, complicated URLs with an
embedded session identifier.  Since Quixote views the URL as a
fundamental part of the web user interface, a URL-based session
management scheme would be un-Quixotic.)</p>
<p>For further reading: the standard for cookies that is approximately
implemented by most current browsers is RFC 2109; the latest version of
the standard is RFC 2965.  Those RFCs can be found here:</p>
<blockquote>
<p><a class="reference" href="ftp://ftp.isi.edu/in-notes/rfc2109.txt">ftp://ftp.isi.edu/in-notes/rfc2109.txt</a></p>
<p><a class="reference" href="ftp://ftp.isi.edu/in-notes/rfc2965.txt">ftp://ftp.isi.edu/in-notes/rfc2965.txt</a></p>
</blockquote>
<p>In a nutshell, session management with Quixote works like this:</p>
<ul>
<li><p class="first">when a user-agent first requests a page from a Quixote application
that implements session management, Quixote creates a Session object
and generates a session ID (a random 64-bit number).  The Session
object is attached to the current HTTPRequest object, so that
application code involved in processing this request has access to
the Session object.</p>
</li>
<li><p class="first">if, at the end of processing that request, the application code has
stored any information in the Session object, Quixote saves the
session in its SessionManager object for use by future requests and
sends a session cookie, called <tt class="literal"><span class="pre">QX_session</span></tt> by default, to the user.
The session cookie contains the session ID encoded as a hexadecimal
string, and is included in the response headers, eg.</p>
<pre class="literal-block">
Set-Cookie: QX_session=&quot;928F82A9B8FA92FD&quot;
</pre>
<p>(You can instruct Quixote to specify the domain and path for
URLs to which this cookie should be sent.)</p>
</li>
<li><p class="first">the user agent stores this cookie for future requests</p>
</li>
<li><p class="first">the next time the user agent requests a resource that matches the
cookie's domain and path, it includes the <tt class="literal"><span class="pre">QX_session</span></tt> cookie
previously generated by Quixote in the request headers, eg.:</p>
<pre class="literal-block">
Cookie: QX_session=&quot;928F82A9B8FA92FD&quot;
</pre>
</li>
<li><p class="first">while processing the request, Quixote decodes the session ID and
looks up the corresponding Session object in its SessionManager.  If
there is no such session, the session cookie is bogus or
out-of-date, so Quixote raises SessionError; ultimately the user
gets an error page.  Otherwise, the Session object is attached to
the HTTPRequest object that is available to all application code
used to process the request.</p>
</li>
</ul>
<p>There are two caveats to keep in mind before proceeding, one major and
one minor:</p>
<ul class="simple">
<li>Quixote's standard Session and SessionManager class do not
implement any sort of persistence, meaning that all sessions
disappear when the process handling web requests terminates.
Thus, session management is completely useless with a plain
CGI driver script unless you add some persistence to the mix;
see &quot;Session persistence&quot; below for information.</li>
<li>Quixote never expires sessions; if you want user sessions to
be cleaned up after a period of inactivity, you will have to
write code to do it yourself.</li>
</ul>
<div class="section" id="session-management-demo">
<h1><a name="session-management-demo">Session management demo</a></h1>
<p>There's a simple demo of Quixote's session management in
<tt class="literal"><span class="pre">demo/session_demo.cgi</span></tt> and <tt class="literal"><span class="pre">demo/session.ptl</span></tt>.  The demo implements
a simple session persistence scheme (each session is written to a
separate pickle file in <tt class="literal"><span class="pre">/tmp/quixote-session-demo</span></tt>), so running it
through CGI is just fine.</p>
<p>I'll assume that you've added a rewrite rule so that requests for
<tt class="literal"><span class="pre">/qsdemo/</span></tt> are handled by <tt class="literal"><span class="pre">session_demo.cgi</span></tt>, similar to the
rewriting for <tt class="literal"><span class="pre">/qdemo/</span></tt> described in web-server.txt.  Once that's
done, point your browser at</p>
<pre class="literal-block">
http://&lt;hostname&gt;/qsdemo/
</pre>
<p>and play around.</p>
<p>This particular application uses sessions to keep track of just two
things: the user's identity and the number of requests made in this
session.  The first is addressed by Quixote's standard Session class --
every Session object has a <tt class="literal"><span class="pre">user</span></tt> attribute, which you can use for
anything you like.  In the session demo, we simply store a string, the
user's name, which is entered by the user.</p>
<p>Tracking the number of requests is a bit more interesting: from the
DemoSession class in session_demo.cgi:</p>
<pre class="literal-block">
def __init__ (self, request, id):
    Session.__init__(self, request, id)
    self.num_requests = 0

def start_request (self, request):
    Session.start_request(self, request)
    self.num_requests += 1
</pre>
<p>When the session is created, we initialize the request counter; and when
we start processing each request, we increment it.</p>
<p>Using the session information in the application code is simple.  For
example, here's the PTL code that checks if the user has logged in
(identified herself) yet, and generates a login form if not:</p>
<pre class="literal-block">
session = request.session
if session.user is None:
    '''
    &lt;p&gt;You haven\'t introduced yourself yet.&lt;br&gt;
    Please tell me your name:
    '''
    login_form()
</pre>
<p>(The <tt class="literal"><span class="pre">login_form()</span></tt> template just emits a simple HTML form -- see
<tt class="literal"><span class="pre">demo/session.ptl</span></tt> for full source.)</p>
<p>If the user has already identified herself, then she doesn't need to do
so again -- so the other branch of that <tt class="literal"><span class="pre">if</span></tt> statement simply prints a
friendly greeting:</p>
<pre class="literal-block">
else:
    ('&lt;p&gt;Hello, %s.  Good to see you again.&lt;/p&gt;\n' 
     % html_quote(session.user))
</pre>
<p>Note that we must quote the user's name, because they are free to enter
anything they please, including special HTML characters like <tt class="literal"><span class="pre">&amp;</span></tt> or
<tt class="literal"><span class="pre">&lt;</span></tt>.</p>
<p>Of course, <tt class="literal"><span class="pre">session.user</span></tt> will never be set if we don't set it
ourselves.  The code that processes the login form is just this (from
<tt class="literal"><span class="pre">login()</span></tt> in <tt class="literal"><span class="pre">demo/session.ptl</span></tt>):</p>
<pre class="literal-block">
if request.form:
    user = request.form.get(&quot;name&quot;)
    if not user:
        raise QueryError(&quot;no user name supplied&quot;)

    session.user = user
</pre>
<p>This is obviously a very simple application -- we're not doing any
verification of the user's input.  We have no user database, no
passwords, and no limitations on what constitutes a &quot;user name&quot;.  A real
application would have all of these, as well as a way for users to add
themselves to the user database -- ie. register with your web site.</p>
</div>
<div class="section" id="configuring-the-session-cookie">
<h1><a name="configuring-the-session-cookie">Configuring the session cookie</a></h1>
<p>Quixote allows you to configure several aspects of the session cookie
that it exchanges with clients.  First, you can set the name of the
cookie; this is important if you have multiple independent Quixote
applications running on the same server.  For example, the config file
for the first application might have</p>
<pre class="literal-block">
SESSION_COOKIE_NAME = &quot;foo_session&quot;
</pre>
<p>and the second application might have</p>
<pre class="literal-block">
SESSION_COOKIE_NAME = &quot;bar_session&quot;
</pre>
<p>Next, you can use <tt class="literal"><span class="pre">SESSION_COOKIE_DOMAIN</span></tt> and <tt class="literal"><span class="pre">SESSION_COOKIE_PATH</span></tt>
to set the cookie attributes that control which requests the cookie is
included with.  By default, these are both <tt class="literal"><span class="pre">None</span></tt>, which instructs
Quixote to send the cookie without <tt class="literal"><span class="pre">Domain</span></tt> or <tt class="literal"><span class="pre">Path</span></tt> qualifiers.
For example, if the client requests <tt class="literal"><span class="pre">/foo/bar/</span></tt> from
www.example.com, and Quixote decides that it must set the session
cookie in the response to that request, then the server would send</p>
<pre class="literal-block">
Set-Cookie: QX_session=&quot;928F82A9B8FA92FD&quot;
</pre>
<p>in the response headers.  Since no domain or path were specified with
that cookie, the browser will only include the cookie with requests to
www.example.com for URIs that start with <tt class="literal"><span class="pre">/foo/bar/</span></tt>.</p>
<p>If you want to ensure that your session cookie is included with all
requests to www.example.com, you should set <tt class="literal"><span class="pre">SESSION_COOKIE_PATH</span></tt> in your
config file:</p>
<pre class="literal-block">
SESSION_COOKIE_PATH = &quot;/&quot;
</pre>
<p>which will cause Quixote to set the cookie like this:</p>
<pre class="literal-block">
Set-Cookie: QX_session=&quot;928F82A9B8FA92FD&quot;; Path=&quot;/&quot;
</pre>
<p>which will instruct the browser to include that cookie with <em>all</em>
requests to www.example.com.</p>
<p>However, think carefully about what you set <tt class="literal"><span class="pre">SESSION_COOKIE_PATH</span></tt> to
-- eg. if you set it to &quot;/&quot;, but all of your Quixote code is under &quot;/q/&quot;
in your server's URL-space, then your user's session cookies could be
unnecessarily exposed.  On shared servers where you don't control all of
the code, this is especially dangerous; be sure to use (eg.)</p>
<pre class="literal-block">
SESSION_COOKIE_PATH = &quot;/q/&quot;
</pre>
<p>on such servers.  The trailing slash is important; without it, your
session cookies will be sent to URIs like <tt class="literal"><span class="pre">/qux</span></tt> and <tt class="literal"><span class="pre">/qix</span></tt>, even if
you don't control those URIs.</p>
<p>If you want to share the cookie across servers in your domain,
eg. www1.example.com and www2.example.com, you'll also need to set
<tt class="literal"><span class="pre">SESSION_COOKIE_DOMAIN</span></tt>:</p>
<blockquote>
SESSION_COOKIE_DOMAIN = &quot;.example.com&quot;</blockquote>
<p>Finally, note that the <tt class="literal"><span class="pre">SESSION_COOKIE_*</span></tt> configuration variables
<em>only</em> affect Quixote's session cookie; if you set your own cookies
using the <tt class="literal"><span class="pre">HTTPResponse.set_cookie()</span></tt> method, then the cookie sent to
the client is completely determined by that <tt class="literal"><span class="pre">set_cookie()</span></tt> call.</p>
<p>See RFCs 2109 and 2965 for more information on the rules browsers are
supposed to follow for including cookies with HTTP requests.</p>
</div>
<div class="section" id="writing-the-session-class">
<h1><a name="writing-the-session-class">Writing the session class</a></h1>
<p>You will almost certainly have to write a custom session class for your
application by subclassing Quixote's standard Session class.  Every
custom session class has two essential responsibilities:</p>
<ul class="simple">
<li>initialize the attributes that will be used by your application</li>
<li>override the <tt class="literal"><span class="pre">has_info()</span></tt> method, so the session manager knows when
it must save your session object</li>
</ul>
<p>The first one is fairly obvious and just good practice.  The second is
essential, and not at all obvious.  The has_info() method exists because
SessionManager does not automatically hang on to all session objects;
this is a defence against clients that ignore cookies, making your
session manager create lots of session objects that are just used once.
As long as those session objects are not saved, the burden imposed by
these clients is not too bad -- at least they aren't sucking up your
memory, or bogging down the database that you save session data to.
Thus, the session manager uses has_info() to know if it should hang on
to a session object or not: if a session has information that must be
saved, the session manager saves it and sends a session cookie to the
client.</p>
<p>For development/testing work, it's fine to say that your session objects
should always be saved:</p>
<pre class="literal-block">
def has_info (self):
    return 1
</pre>
<p>The opposite extreme is to forget to override <tt class="literal"><span class="pre">has_info()</span></tt> altogether,
in which case session management most likely won't work: unless you
tickle the Session object such that the base <tt class="literal"><span class="pre">has_info()</span></tt> method
returns true, the session manager won't save the sessions that it
creates, and Quixote will never drop a session cookie on the client.</p>
<p>In a real application, you need to think carefully about what data to
store in your sessions, and how <tt class="literal"><span class="pre">has_info()</span></tt> should react to the
presence of that data.  If you try and track something about every
single visitor to your site, sooner or later one of those a
broken/malicious client that ignores cookies and <tt class="literal"><span class="pre">robots.txt</span></tt> will
come along and crawl your entire site, wreaking havoc on your Quixote
application (or the database underlying it).</p>
</div>
<div class="section" id="session-persistence">
<h1><a name="session-persistence">Session persistence</a></h1>
<p>Keeping session data across requests is all very nice, but in the real
world you want that data to survive across process termination.  With
CGI, this is essential, since each process serves exactly one request
and then terminates.  With other execution mechanisms, though, it's
still important -- you don't want to lose all your session data just
because your long-lived server process was restarted, or your server
machine was rebooted.</p>
<p>However, every application is different, so Quixote doesn't provide any
built-in mechanism for session persistence.  Instead, it provides a
number of hooks, most in the SessionManager class, that let you plug in
your preferred persistence mechanism.</p>
<p>The first and most important hook is in the SessionManager constructor:
you can provide an alternate mapping object that SessionManager will use
to store session objects in.  By default, SessionManager uses an
ordinary dictionary; if you provide a mapping object that implements
persistence, then your session data will automatically persist across
processes.  For example, you might use the standard 'shelve' module,
which provides a mapping object on top of a DBM or Berkeley DB file:</p>
<pre class="literal-block">
import shelve
sessions = shelve.open(&quot;/tmp/quixote-sessions&quot;)
session_mgr = SessionManager(session_mapping=sessions)
</pre>
<p>For a persistent mapping implementation that doesn't require any
external libraries, see the DirMapping class in
<tt class="literal"><span class="pre">demo/session_demo.cgi</span></tt>.</p>
<p>If you use one of these relatively simple persistent mapping types,
you'll also need to override <tt class="literal"><span class="pre">is_dirty()</span></tt> in your Session class.
That's in addition to overriding <tt class="literal"><span class="pre">has_info()</span></tt>, which determines if a
session object is <em>ever</em> saved; <tt class="literal"><span class="pre">is_dirty()</span></tt> is only called on
sessions that have already been added to the session mapping, to see if
they need to be &quot;re-added&quot;.  The default implementation always returns
false, because once an object has been added to a normal dictionary,
there's no need to add it again.  However, with simple persistent
mapping types like shelve and DirMapping, you need to store the object
again each time it changes.  Thus, <tt class="literal"><span class="pre">is_dirty()</span></tt> should return true if
the session object needs to be re-written.  For a simple, naive, but
inefficient implementation, making is_dirty an alias for <tt class="literal"><span class="pre">has_info()</span></tt>
will work -- that just means that once the session has been written
once, it will be re-written on every request.  (This is what DemoSession
in <tt class="literal"><span class="pre">demo/session_demo.cgi</span></tt> does.)</p>
<p>The third and final part of the persistence interface only applies if
you are using a transactional persistence mechanism, such as ZODB or an
industrial-strength relational database.  In that case, you need a place
to commit or abort the transaction that contains pending changes to the
current session.  SessionManager provides two methods for you to
override: <tt class="literal"><span class="pre">abort_changes()</span></tt> and <tt class="literal"><span class="pre">commit_changes()</span></tt>.
<tt class="literal"><span class="pre">abort_changes()</span></tt> is called by SessionPublisher whenever a request
crashes, ie. whenever your application raises an exception other than
PublishError.  <tt class="literal"><span class="pre">commit_changes()</span></tt> is called for requests that complete
successfully, or that raise a PublishError exception.  They are defined
as follows:</p>
<pre class="literal-block">
def abort_changes (self, session):
    &quot;&quot;&quot;abort_changes(session : Session)&quot;&quot;&quot;

def commit_changes (self, session):
    &quot;&quot;&quot;commit_changes(session : Session)&quot;&quot;&quot;
</pre>
<p>Obviously, you'll have to write your own SessionManager subclass if you
need to take advantage of these hooks for transactional session
persistence.</p>
<p>$Id: session-mgmt.txt 20217 2003-01-16 20:51:53Z akuchlin $</p>
</div>
</div>
</body>
</html>