Revision: 4283
Author: edi
URL: http://bknr.net/trac/changeset/4283
Sessions
U trunk/thirdparty/hunchentoot/doc/index.xml
U trunk/thirdparty/hunchentoot/session.lisp
Modified: trunk/thirdparty/hunchentoot/doc/index.xml
===================================================================
--- trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 21:25:42 UTC (rev 4282)
+++ trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 22:12:41 UTC (rev 4283)
@@ -1136,16 +1136,6 @@
not <a href="#session-too-old-p">too old</a>. Old sessions
are <a href="#session-gc">automatically removed</a>.
</p>
-<p>
-For everyday session usage, you will probably just
-use <clix:ref>START-SESSION</clix:ref>,
-<clix:ref>SESSION-VALUE</clix:ref>,
-maybe <clix:ref>DELETE-SESSION-VALUE</clix:ref>
-and <clix:ref>*SESSION*</clix:ref>. However, there are two ways to
-customize the way Hunchentoot maintains sessions:
-</p>
-<p>
-</p>
<clix:class name='session'>
<clix:description><clix:ref>SESSION</clix:ref> objects are
@@ -1218,6 +1208,29 @@
</clix:description>
</clix:special-variable>
+ <clix:function name='remove-session'>
+ <clix:lambda-list>session
+ </clix:lambda-list>
+ <clix:returns>|
+ </clix:returns>
+ <clix:description>Completely removes
+the <clix:ref>SESSION</clix:ref> object <clix:arg>session</clix:arg>
+from Hunchentoot's internal <a href="#session-db">session
+database</a>.
+ </clix:description>
+ </clix:function>
+
+
+ <clix:function name='reset-sessions'>
+ <clix:lambda-list>
+ </clix:lambda-list>
+ <clix:returns>|
+ </clix:returns>
+ <clix:description>Removes <em>all</em> stored sessions.
+ </clix:description>
+ </clix:function>
+
+
<clix:special-variable name='*rewrite-for-session-urls*'>
<clix:description>Whether HTML pages should possibly be rewritten for cookie-less
session-management.
@@ -1231,38 +1244,7 @@
</clix:description>
</clix:special-variable>
- <clix:special-variable name='*session-gc-frequency*'>
- <clix:description>A session GC (see function <clix:ref>SESSION-GC</clix:ref>) will happen every
-<clix:ref>*SESSION-GC-FREQUENCY*</clix:ref> requests (counting only
-requests which create a new session) if this variable is
-not <code>NIL</code>. See <clix:ref>SESSION-CREATED</clix:ref>.
- </clix:description>
- </clix:special-variable>
- <clix:special-variable name='*session-max-time*'>
- <clix:description>The default time (in seconds) after which a session times out.
- </clix:description>
- </clix:special-variable>
-
- <clix:special-variable name='*session-removal-hook*'>
- <clix:description>A function of one argument
-(a <clix:ref>SESSION</clix:ref> object) which is called whenever a
-session is <a href="#session-gc">garbage-collected</a>.
- </clix:description>
- </clix:special-variable>
-
- <clix:special-variable name='*session-secret*'>
- <clix:description>A random ASCII string that's used to encode
-the public session data. This variable is initially unbound and will
-be set (using <clix:ref>RESET-SESSION-SECRET</clix:ref>) the first
-time a session is created, if necessary. You can prevent this from
-happening if you set the value yourself before
-starting <a href="#acceptors">acceptors</a>.
- </clix:description>
- </clix:special-variable>
-
-
-
<clix:special-variable name='*use-remote-addr-for-sessions*'>
<clix:description>Whether the client's remote IP (as returned by <clix:ref>REAL-REMOTE-ADDR</clix:ref>)
should be encoded into the session string. If this value is true, a
@@ -1274,97 +1256,208 @@
</clix:description>
</clix:special-variable>
+ <clix:function generic='true' name='session-remote-addr'>
+ <clix:lambda-list>session
+ </clix:lambda-list>
+ <clix:returns>remote-addr
+ </clix:returns>
+ <clix:description>
+The remote IP address of the client when this session was started as
+returned by <clix:ref>REAL-REMOTE-ADDR</clix:ref>.
+ </clix:description>
+ </clix:function>
+
+
<clix:special-variable name='*use-user-agent-for-sessions*'>
- <clix:description>Whether the 'User-Agent' header should be encoded into the session
-string. If this value is true, a session will cease to be accessible
-if the client sends a different 'User-Agent' header.
+ <clix:description>Whether the 'User-Agent' header should
+be encoded into the session string. If this value is true, a session
+will cease to be accessible if the client sends a different
+'User-Agent' header.
</clix:description>
</clix:special-variable>
-
- <clix:function generic='true' name='next-session-id'>
- <clix:lambda-list>acceptor
+ <clix:function generic='true' name='session-user-agent'>
+ <clix:lambda-list>session
</clix:lambda-list>
- <clix:returns>id
+ <clix:returns>user-agent
</clix:returns>
- <clix:description>Returns the next sequential session ID, an
-integer, which should be unique per session. The default method uses
-a simple global counter and isn't guarded by a lock. For a
-high-performance production environment you might consider to use a
-more robust implementation.
+ <clix:description>
+The incoming 'User-Agent' header that
+was sent when this session was created.
</clix:description>
</clix:function>
- <clix:function name='remove-session'>
+ <clix:accessor generic='true' name='session-max-time'>
<clix:lambda-list>session
</clix:lambda-list>
+ <clix:returns>max-time
+ </clix:returns>
+ <clix:description>
+Gets or sets the time (in seconds) after
+which <clix:arg>session</clix:arg> expires if it's not used.
+ </clix:description>
+ </clix:accessor>
+
+
+ <clix:special-variable name='*session-max-time*'>
+ <clix:description>The default time (in seconds) after which a session times out.
+ </clix:description>
+ </clix:special-variable>
+
+ <clix:special-variable name='*session-gc-frequency*'>
+ <clix:description>A session GC (see function <clix:ref>SESSION-GC</clix:ref>) will happen every
+<clix:ref>*SESSION-GC-FREQUENCY*</clix:ref> requests (counting only
+requests which create a new session) if this variable is
+not <code>NIL</code>. See <clix:ref>SESSION-CREATED</clix:ref>.
+ </clix:description>
+ </clix:special-variable>
+
+ <clix:function name='session-gc'>
+ <clix:lambda-list>
+ </clix:lambda-list>
<clix:returns>|
</clix:returns>
- <clix:description>Completely removes the SESSION object SESSION from Hunchentoot's
-internal session database.
+ <clix:description>Removes sessions from the current session database which are too
+old - see <clix:ref>SESSION-TOO-OLD-P</clix:ref>.
</clix:description>
</clix:function>
- <clix:function name='reset-session-secret'>
- <clix:lambda-list>
+ <clix:function name='session-too-old-p'>
+ <clix:lambda-list>session
</clix:lambda-list>
- <clix:returns>secret
+ <clix:returns>generalized-boolean
</clix:returns>
- <clix:description>Sets *SESSION-SECRET* to a new random value. All old sessions will
-cease to be valid.
+ <clix:description>Returns true if the <clix:ref>SESSION</clix:ref> object <clix:arg>session</clix:arg> has not been active in
+the last <code>(session-max-time session)</code> seconds.
</clix:description>
</clix:function>
- <clix:function name='reset-sessions'>
+
+ <clix:special-variable name='*session-removal-hook*'>
+ <clix:description>A function of one argument
+(a <clix:ref>SESSION</clix:ref> object) which is called whenever a
+session is <a href="#session-gc">garbage-collected</a>.
+ </clix:description>
+ </clix:special-variable>
+
+
+ </clix:subchapter>
+
+
+ <clix:subchapter name="session-behaviour" title="Customizing session behaviour">
+
+For everyday session usage, you will probably just
+use <clix:ref>START-SESSION</clix:ref>,
+<clix:ref>SESSION-VALUE</clix:ref>,
+maybe <clix:ref>DELETE-SESSION-VALUE</clix:ref>
+and <clix:ref>*SESSION*</clix:ref>. However, there are two ways to
+customize the way Hunchentoot maintains sessions.
+<p>
+One way is to mostly leave the session mechanism intact but to tweak
+it a bit:
+<ul>
+<li>The publicly visible part of session is encoded using a
+<a href="#*session-secret*">secret</a> which you can set yourself.</li>
+<li>And it is stored using a cookie (or GET
+parameter) <a href="#session-cookie-name">name</a> that you can
+override.</li>
+<li>Each session receives a <a href="#next-session-id">new ID</a> when
+it is created and you can implement a more robust way to do that.</li>
+<li>You can arrange to be called whenever a session
+is <a href="#session-created">created</a> to trigger some action. You
+might also do this to invent your own
+session <a href="#session-gc">garbage collection</a>.</li>
+<li>By default, all sessions are stored in a global alist in memory.
+You can't change the alist part, but you can distribute your sessions
+amongst different <a href="#session-db">"databases"</a>.</li>
+<li>By default, every operation which modifies sessions or one of the
+session databases is guarded by a global lock, but you can arrange to
+<a href="#session-db-lock">provide</a> different locks for this.</li>
+</ul>
+</p>
+<p>
+The other way to customize Hunchentoot's sessions is to completely
+replace them. This is actually pretty easy: Create your own class to
+store state (which doesn't have to and probably shouldn't inherit
+from <clix:ref>SESSION</clix:ref>) and implement methods for
+<clix:ref>SESSION-VERIFY</clix:ref>
+and <clix:ref>SESSION-COOKIE-VALUE</clix:ref> - that's it.
+Hunchentoot will continue to use cookies and/or to rewrite URLs to
+keep track of session state and it will store "the current session"
+(whatever that is in your implementation)
+in <clix:ref>*SESSION*</clix:ref>. Everything else (like persisting
+sessions, GC, getting and setting values) you'll have to take care of
+yourself and the other session functions
+(like <clix:ref>START-SESSION</clix:ref> or
+<clix:ref>SESSION-VALUE</clix:ref>) won't work anymore. (Almost)
+total freedom, but a lot of responsibility as well... :)
+</p>
+
+ <clix:special-variable name='*session-secret*'>
+ <clix:description>A random ASCII string that's used to encode
+the public session data. This variable is initially unbound and will
+be set (using <clix:ref>RESET-SESSION-SECRET</clix:ref>) the first
+time a session is created, if necessary. You can prevent this from
+happening if you set the value yourself before
+starting <a href="#acceptors">acceptors</a>.
+ </clix:description>
+ </clix:special-variable>
+
+ <clix:function name='reset-session-secret'>
<clix:lambda-list>
</clix:lambda-list>
- <clix:returns>|
+ <clix:returns>secret
</clix:returns>
- <clix:description>Removes ALL stored sessions.
+ <clix:description>Sets <clix:ref>*SESSION-SECRET*</clix:ref> to a
+new random value. All old sessions will cease to be valid.
</clix:description>
</clix:function>
+
+
<clix:function generic='true' name='session-cookie-name'>
<clix:lambda-list>acceptor
</clix:lambda-list>
<clix:returns>name
</clix:returns>
- <clix:description>Returns the name (a string) of the cookie (or the
-GET parameter) which is used to store a session on the client side.
-The default is to use the string "hunchentoot-session", but you can
+ <clix:description>Returns the name (a string) of the cookie (or
+the GET parameter) which is used to store a session on the client
+side. The default is to use the
+string <code>"hunchentoot-session"</code>, but you can
specialize this function if you want another name.
</clix:description>
</clix:function>
- <clix:function generic='true' name='session-cookie-value'>
- <clix:lambda-list>session
- </clix:lambda-list>
- <clix:returns>string
- </clix:returns>
- <clix:description>Returns a string which can be used to safely
-restore the session SESSION if as session has already been
-established. This is used as the value stored in the session cookie
-or in the corresponding GET parameter. A default method is provided
-and there's no reason to change it unless you want to use your own
-session objects.
- </clix:description>
- </clix:function>
-
<clix:function generic='true' name='session-created'>
<clix:lambda-list>acceptor new-session
</clix:lambda-list>
<clix:returns>result
</clix:returns>
- <clix:description>This function is called whenever a new session has
-been created. There's a default method which might trigger a session
-GC based on the value of *SESSION-GC-FREQUENCY*.
+ <clix:description>This function is called whenever a new session
+has been created. There's a default method which might trigger
+a <a href="#session-gc">session GC</a> based on the value of
+<clix:ref>*SESSION-GC-FREQUENCY*</clix:ref>.
<p>
The return value is ignored.
</p>
</clix:description>
</clix:function>
+
+ <clix:function generic='true' name='next-session-id'>
+ <clix:lambda-list>acceptor
+ </clix:lambda-list>
+ <clix:returns>id
+ </clix:returns>
+ <clix:description>Returns the next sequential session ID, an
+integer, which should be unique per session. The default method uses
+a simple global counter and isn't guarded by a lock. For a
+high-performance production environment you might consider to use a
+more robust implementation.
+ </clix:description>
+ </clix:function>
+
<clix:accessor generic='true' name='session-db'>
<clix:lambda-list>acceptor
</clix:lambda-list>
@@ -1372,8 +1465,8 @@
</clix:returns>
<clix:description>Returns the current session database which is an
alist where each car is a session's ID and the cdr is the
-corresponding SESSION object itself. The default is to use a global
-list for all acceptors.
+corresponding <clix:ref>SESSION</clix:ref> object itself. The default
+is to use a global list for all acceptors.
</clix:description>
</clix:accessor>
@@ -1384,67 +1477,23 @@
</clix:lambda-list>
<clix:returns>lock
</clix:returns>
- <clix:description>A function which returns a lock that will be used
-to prevent concurrent access to sessions. The first argument will be
-the acceptor that handles the current request, the second argument is
-true if the whole (current) session database is modified. If it is
-NIL, only one existing session in the database is modified.
-
-This function can return NIL which means that sessions or session
-databases will be modified without a lock held (for example for
-single-threaded environments). The default is to always return a
-global lock (ignoring the ACCEPTOR argument) for Lisps that support
-threads and NIL otherwise.
+ <clix:description>A function which returns a lock that will be
+used to prevent concurrent access to sessions. The first argument
+will be the <a href="#acceptors">acceptor</a> that handles the
+current <a href="#requests">request</a>, the second argument is true
+if the whole (current) session database is modified. If it
+is <code>NIL</code>, only one existing session in the database is
+modified.
+<p>
+This function can return <code>NIL</code> which means that sessions or
+session databases will be modified without a lock held (for example
+for single-threaded environments). The default is to always return a
+global lock (ignoring the <clix:arg>acceptor</clix:arg> argument) for
+Lisps that support threads and <code>NIL</code> otherwise.
+</p>
</clix:description>
</clix:function>
- <clix:function name='session-gc'>
- <clix:lambda-list>
- </clix:lambda-list>
- <clix:returns>|
- </clix:returns>
- <clix:description>Removes sessions from the current session database which are too
-old - see SESSION-TOO-OLD-P.
- </clix:description>
- </clix:function>
-
- <clix:accessor generic='true' name='session-max-time'>
- <clix:lambda-list>session
- </clix:lambda-list>
- <clix:returns>max-time
- </clix:returns>
- <clix:description>
- </clix:description>
- </clix:accessor>
-
- <clix:function generic='true' name='session-remote-addr'>
- <clix:lambda-list>session
- </clix:lambda-list>
- <clix:returns>remote-addr
- </clix:returns>
- <clix:description>
- </clix:description>
- </clix:function>
-
- <clix:function name='session-too-old-p'>
- <clix:lambda-list>session
- </clix:lambda-list>
- <clix:returns>generalized-boolean
- </clix:returns>
- <clix:description>Returns true if the SESSION object SESSION has not been active in
-the last (SESSION-MAX-TIME SESSION) seconds.
- </clix:description>
- </clix:function>
-
- <clix:function generic='true' name='session-user-agent'>
- <clix:lambda-list>session
- </clix:lambda-list>
- <clix:returns>user-agent
- </clix:returns>
- <clix:description>
- </clix:description>
- </clix:function>
-
<clix:function generic='true' name='session-verify'>
<clix:lambda-list>request
</clix:lambda-list>
@@ -1452,16 +1501,31 @@
</clix:returns>
<clix:description>Tries to get a session identifier from the cookies
(or alternatively from the GET parameters) sent by the client. This
-identifier is then checked for validity against the REQUEST object
-REQUEST. On success the corresponding session object (if not too
-old) is returned (and updated). Otherwise NIL is returned.
-
+identifier is then checked for validity against the <clix:ref>REQUEST</clix:ref> object
+<clix:arg>request</clix:arg>. On success the corresponding session object (if not too
+old) is returned (and updated). Otherwise <code>NIL</code> is returned.
+<p>
A default method is provided and you only need to write your own one
if you want to maintain your own sessions.
+</p>
</clix:description>
</clix:function>
+ <clix:function generic='true' name='session-cookie-value'>
+ <clix:lambda-list>session
+ </clix:lambda-list>
+ <clix:returns>string
+ </clix:returns>
+ <clix:description>Returns a string which can be used to safely
+restore the session <clix:arg>session</clix:arg> if as session has
+already been established. This is used as the value stored in the
+session cookie or in the corresponding GET parameter. A default
+method is provided and there's no reason to change it unless you
+want to use your own session objects.
+ </clix:description>
+ </clix:function>
+
</clix:subchapter>
<clix:subchapter name="handlers" title="Handlers">
Modified: trunk/thirdparty/hunchentoot/session.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/session.lisp 2009-02-18 21:25:42 UTC (rev 4282)
+++ trunk/thirdparty/hunchentoot/session.lisp 2009-02-18 22:12:41 UTC (rev 4283)
@@ -96,8 +96,8 @@
was sent when this session was created.")
(remote-addr :initform (real-remote-addr *request*)
:reader session-remote-addr
- :documentation "The remote IP address of the client when
-this sessions was started as returned by REAL-REMOTE-ADDR.")
+ :documentation "The remote IP address of the client
+when this session was started as returned by REAL-REMOTE-ADDR.")
(session-start :initform (get-universal-time)
:reader session-start
:documentation "The time this session was started.")
Revision: 4282
Author: edi
URL: http://bknr.net/trac/changeset/4282
Checkpoint
U trunk/thirdparty/hunchentoot/doc/index.xml
U trunk/thirdparty/hunchentoot/packages.lisp
U trunk/thirdparty/hunchentoot/session.lisp
U trunk/thirdparty/hunchentoot/specials.lisp
Change set too large, please see URL above
Revision: 4281
Author: edi
URL: http://bknr.net/trac/changeset/4281
Document logging
U trunk/thirdparty/hunchentoot/acceptor.lisp
U trunk/thirdparty/hunchentoot/doc/index.xml
U trunk/thirdparty/hunchentoot/log.lisp
U trunk/thirdparty/hunchentoot/request.lisp
U trunk/thirdparty/hunchentoot/specials.lisp
Modified: trunk/thirdparty/hunchentoot/acceptor.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 19:58:57 UTC (rev 4280)
+++ trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 20:28:27 UTC (rev 4281)
@@ -139,9 +139,9 @@
:accessor acceptor-message-logger
:documentation "Designator for a function to call
to log messages by the acceptor. It must accept a severity level for
-the message, which will be one of :NOTICE, :INFO, or :WARNING, a
-format string and an arbitary number of formatting arguments. This
-slot defaults to a function which writes to the file determined by
+the message, which will be one of :ERROR, :INFO, or :WARNING, a format
+string and an arbitary number of formatting arguments. This slot
+defaults to a function which writes to the file determined by
*MESSAGE-LOG-PATHNAME* \(unless that value is NIL).
If the value of this slot is NIL, message logging is turned off for
Modified: trunk/thirdparty/hunchentoot/doc/index.xml
===================================================================
--- trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 19:58:57 UTC (rev 4280)
+++ trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 20:28:27 UTC (rev 4281)
@@ -384,6 +384,11 @@
</clix:description>
</clix:function>
+ <clix:special-variable name='*acceptor*'>
+ <clix:description>The current ACCEPTOR object in the context of a request.
+ </clix:description>
+ </clix:special-variable>
+
<clix:readers generic='true'>
<clix:listed-reader name='acceptor-address'>
<clix:lambda-list>acceptor
@@ -450,10 +455,10 @@
</clix:returns>
</clix:listed-accessor>
- <clix:listed-accessor name='acceptor-handler-selector'>
+ <clix:listed-accessor name='acceptor-request-dispatcher'>
<clix:lambda-list>acceptor
</clix:lambda-list>
- <clix:returns>handler-selector
+ <clix:returns>request-dispatcher
</clix:returns>
</clix:listed-accessor>
@@ -600,11 +605,10 @@
<clix:ref>ACCEPTOR</clix:ref> object and a LispWorks socket handle or a usocket socket
stream object in <clix:arg>socket</clix:arg>. It reads the request headers, sets up the
<a href="#requests">request</a> and <a href="#replies">reply</a>
-objects, and hands over to (the unexported
-function) <code>PROCESS-REQUEST</code> which selects and calls a
-handler for the request and sends its reply to the client. This is
-done in a loop until the stream has to be closed or until a connection
-timeout occurs.
+objects, and hands over to <clix:ref>PROCESS-REQUEST</clix:ref> which
+selects and calls a handler for the request and sends its reply to the
+client. This is done in a loop until the stream has to be closed or
+until a connection timeout occurs.
<p>
It is probably not a good idea to re-implement this method until you
@@ -1102,6 +1106,90 @@
</clix:subchapter>
<clix:subchapter name="logging" title="Logging">
+
+By default, Hunchentoot logs accesses and errors to two separate files
+in the file system, but <em>only</em> if the special variables
+<clix:ref>*MESSAGE-LOG-PATHNAME*</clix:ref> and <clix:ref>*ACCESS-LOG-PATHNAME*</clix:ref> are set accordingly.
+Access logging is done in a format similar to what
+the Apache web server can write so that logfile analysis using
+standard tools is possible. Errors during request processing are
+logged to a separate file.
+<p>
+The standard logging mechanism is deliberately simple and slow. The
+log files are opened for each log entry and closed again after
+writing, and access to them is protected by a global lock. If you
+want more sophisticated logging, use
+the <a href="#acceptor-access-logger"><code>:access-logger</code></a>
+and <a href="#acceptor-message-logger"><code>:message-logger</code></a>
+initargs of the acceptor class to establish your own logging
+functions. See the docstrings of the corresponding slots for more
+information.
+</p>
+<p>
+Errors happening within a <a href="#handlers">handler</a> which are
+not caught by the handler itself are handled by Hunchentoot by logging
+them to the log file.
+</p>
+
+ <clix:function name='log-message'>
+ <clix:lambda-list>log-level format-string
+ <clix:lkw>rest
+ </clix:lkw> format-arguments
+ </clix:lambda-list>
+ <clix:returns>result
+ </clix:returns>
+ <clix:description>Convenience function which calls the message
+logger of the current acceptor (if there is one) with the same
+arguments it accepts. Returns <code>NIL</code> if there is no message
+logger or whatever the message logger returns.
+<p>
+This is the function which Hunchentoot itself uses to log errors it
+catches during request processing.
+</p>
+ </clix:description>
+ </clix:function>
+
+ <clix:special-variable name='*message-log-pathname*'>
+ <clix:description>
+A designator for the pathname of the message log file used by the
+<a href="#logging">default message logger</a>. The initial value is <code>NIL</code> which
+means that <em>nothing</em> will be logged!
+ </clix:description>
+ </clix:special-variable>
+
+ <clix:special-variable name='*access-log-pathname*'>
+ <clix:description>
+A designator for the pathname of the access log file used by the
+<a href="#logging">default access logger</a>. The initial value is <code>NIL</code> which
+means that <em>nothing</em> will be logged!
+ </clix:description>
+ </clix:special-variable>
+
+ <clix:special-variable name='*log-lisp-errors-p*'>
+ <clix:description>Whether Lisp errors in request handlers should be logged.
+ </clix:description>
+ </clix:special-variable>
+
+ <clix:special-variable name='*log-lisp-warnings-p*'>
+ <clix:description>Whether Lisp warnings in request handlers should be logged.
+ </clix:description>
+ </clix:special-variable>
+
+ <clix:special-variable name='*lisp-errors-log-level*'>
+ <clix:description>Log level for Lisp errors. Should be one
+of <code>:ERROR</code> (the default), <code>:WARNING</code>,
+or <code>:INFO</code>.
+ </clix:description>
+ </clix:special-variable>
+
+ <clix:special-variable name='*lisp-warnings-log-level*'>
+ <clix:description>Log level for Lisp warnings.
+Should be one of <code>:ERROR</code>, <code>:WARNING</code>
+(the default), or <code>:INFO</code>.
+ </clix:description>
+ </clix:special-variable>
+
+
</clix:subchapter>
<clix:subchapter name="conditions" title="Conditions">
@@ -1113,16 +1201,7 @@
</clix:chapter>
<clix:chapter name='dict' title='The HUNCHENTOOT dictionary'>
- <clix:special-variable name='*acceptor*'>
- <clix:description>The current ACCEPTOR object.
- </clix:description>
- </clix:special-variable>
- <clix:special-variable name='*access-log-pathname*'>
- <clix:description>A designator for the pathname of the access log file used by the
-LOG-ACCESS-TO-FILE function. The initial value is NIL which means
-that nothingq will be logged.
- </clix:description>
- </clix:special-variable>
+
<clix:special-variable name='*approved-return-codes*'>
<clix:description>A list of return codes the server should not treat as an error -
see *HANDLE-HTTP-ERRORS-P*.
@@ -1202,28 +1281,7 @@
<clix:description>The external format used to compute the REQUEST object.
</clix:description>
</clix:special-variable>
- <clix:special-variable name='*lisp-errors-log-level*'>
- <clix:description>Log level for Lisp errors.
- </clix:description>
- </clix:special-variable>
- <clix:special-variable name='*lisp-warnings-log-level*'>
- <clix:description>Log level for Lisp warnings.
- </clix:description>
- </clix:special-variable>
- <clix:special-variable name='*log-lisp-errors-p*'>
- <clix:description>Whether Lisp errors should be logged.
- </clix:description>
- </clix:special-variable>
- <clix:special-variable name='*log-lisp-warnings-p*'>
- <clix:description>Whether Lisp warnings should be logged.
- </clix:description>
- </clix:special-variable>
- <clix:special-variable name='*message-log-pathname*'>
- <clix:description>A designator for the pathname of the message log file used by the
-LOG-MESSAGE-TO-FILE function. The initial value is NIL which means
-that nothing will be logged.
- </clix:description>
- </clix:special-variable>
+
<clix:special-variable name='*methods-for-post-parameters*'>
<clix:description>A list of the request method types (as keywords) for which
Hunchentoot will try to compute POST-PARAMETERS.
@@ -2018,17 +2076,6 @@
</clix:condition>
- <clix:function name='log-message'>
- <clix:lambda-list>log-level format-string
- <clix:lkw>rest
- </clix:lkw> format-arguments
- </clix:lambda-list>
- <clix:returns>result
- </clix:returns>
- <clix:description>Convenience function which calls the message logger of the current
-acceptor (if there is one) with the same arguments it accepts.
- </clix:description>
- </clix:function>
<clix:function name='mime-type'>
<clix:lambda-list>pathspec
</clix:lambda-list>
@@ -2548,6 +2595,33 @@
name doesn't exist, it is created.
</clix:description>
</clix:function>
+
+
+ <clix:function generic='true' name='process-request'>
+ <clix:lambda-list>request
+ </clix:lambda-list>
+ <clix:returns>nil
+ </clix:returns>
+ <clix:description>
+This function is called by <clix:ref>PROCESS-CONNECTION</clix:ref>
+after the incoming headers have been read. It selects and calls a
+handler and sends the output of this handler to the client. It also
+sets up simple error handling for the request handler. Note
+that <clix:ref>PROCESS-CONNECTION</clix:ref> is called once per
+connection and loops in case of a persistent connection
+while <clix:ref>PROCESS-REQUEST</clix:ref> is called anew for each
+request.
+<p>
+Like <clix:ref>PROCESS-CONNECTION</clix:ref>, this might be a good
+place to introduce around methods which bind special variables or do
+other interesting things.
+</p>
+<p>
+The return value of this function is ignored.
+</p>
+ </clix:description>
+ </clix:function>
+
</clix:chapter>
<clix:chapter name="testing" title="Testing">
Modified: trunk/thirdparty/hunchentoot/log.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/log.lisp 2009-02-18 19:58:57 UTC (rev 4280)
+++ trunk/thirdparty/hunchentoot/log.lisp 2009-02-18 20:28:27 UTC (rev 4281)
@@ -30,6 +30,11 @@
(in-package :hunchentoot)
(defmacro with-log-file ((stream-var pathname lock) &body body)
+ "Helper macro which executes BODY only if PATHNAME \(which is
+evaluated) is not NIL. In this case, the file designated by PATHNAME
+is opened for writing \(appending) and created if it doesn't exist.
+STREAM-VAR is then bound to a flexi stream which can be used to write
+characters to the file in UTF-8 format."
(with-unique-names (binary-stream)
(with-rebinding (pathname)
`(when ,pathname
@@ -77,7 +82,13 @@
(defun log-message (log-level format-string &rest format-arguments)
"Convenience function which calls the message logger of the current
-acceptor \(if there is one) with the same arguments it accepts."
+acceptor \(if there is one) with the same arguments it accepts.
+
+Returns NIL if there is no message logger or whatever the message
+logger returns.
+
+This is the function which Hunchentoot itself uses to log errors it
+catches during request processing."
(when-let (message-logger (acceptor-message-logger *acceptor*))
(apply message-logger log-level format-string format-arguments)))
Modified: trunk/thirdparty/hunchentoot/request.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/request.lisp 2009-02-18 19:58:57 UTC (rev 4280)
+++ trunk/thirdparty/hunchentoot/request.lisp 2009-02-18 20:28:27 UTC (rev 4281)
@@ -99,10 +99,10 @@
(:documentation "This function is called by PROCESS-CONNECTION after
the incoming headers have been read. It selects and calls a handler
and sends the output of this handler to the client using START-OUTPUT.
-It also sets up simple error handling for the actual request handler.
-Note that PROCESS-CONNECTION is called once per connection and loops
-in case of a persistent connection while PROCESS-REQUEST is called
-anew for each request.
+It also sets up simple error handling for the request handler. Note
+that PROCESS-CONNECTION is called once per connection and loops in
+case of a persistent connection while PROCESS-REQUEST is called anew
+for each request.
Like PROCESS-CONNECTION, this might be a good place to introduce
around methods which bind special variables or do other interesting
Modified: trunk/thirdparty/hunchentoot/specials.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/specials.lisp 2009-02-18 19:58:57 UTC (rev 4280)
+++ trunk/thirdparty/hunchentoot/specials.lisp 2009-02-18 20:28:27 UTC (rev 4281)
@@ -194,19 +194,21 @@
purposes.")
(defvar *show-lisp-errors-p* nil
- "Whether Lisp errors should be shown in HTML output.")
+ "Whether Lisp errors in request handlers should be shown in HTML output.")
(defvar *log-lisp-errors-p* t
- "Whether Lisp errors should be logged.")
+ "Whether Lisp errors in request handlers should be logged.")
(defvar *log-lisp-warnings-p* t
- "Whether Lisp warnings should be logged.")
+ "Whether Lisp warnings in request handlers should be logged.")
(defvar *lisp-errors-log-level* :error
- "Log level for Lisp errors.")
+ "Log level for Lisp errors. Should be one of :ERROR \(the default),
+:WARNING, or :INFO.")
(defvar *lisp-warnings-log-level* :warning
- "Log level for Lisp warnings.")
+ "Log level for Lisp warnings. Should be one of :ERROR, :WARNING
+\(the default), or :INFO.")
(defvar *message-log-pathname* nil
"A designator for the pathname of the message log file used by the
@@ -216,7 +218,7 @@
(defvar *access-log-pathname* nil
"A designator for the pathname of the access log file used by the
LOG-ACCESS-TO-FILE function. The initial value is NIL which means
-that nothingq will be logged.")
+that nothing will be logged.")
(defvar *message-log-lock* (make-lock "global-message-log-lock")
"A global lock to prevent concurrent access to the log file
Revision: 4280
Author: edi
URL: http://bknr.net/trac/changeset/4280
Checkpoint
U trunk/thirdparty/hunchentoot/acceptor.lisp
U trunk/thirdparty/hunchentoot/doc/index.xml
U trunk/thirdparty/hunchentoot/request.lisp
U trunk/thirdparty/hunchentoot/session.lisp
Modified: trunk/thirdparty/hunchentoot/acceptor.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 14:57:40 UTC (rev 4279)
+++ trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 19:58:57 UTC (rev 4280)
@@ -63,12 +63,13 @@
symbol naming) a class which inherits from REPLY. The default is the
symbol REPLY.")
(request-dispatcher :initarg :request-dispatcher
- :accessor acceptor-request-dispatcher
- :documentation "A designator for the request
+ :accessor acceptor-request-dispatcher
+ :documentation "A designator for the request
dispatcher function used by this acceptor. A function which accepts a
REQUEST object and calls a request handler of its choice \(and returns
its return value). The default is the unexported symbol
-LIST-REQUEST-DISPATCHER which works through the list *DISPATCH-TABLE*.")
+LIST-REQUEST-DISPATCHER which works through the list
+*DISPATCH-TABLE*.")
(taskmaster :initarg :taskmaster
:reader acceptor-taskmaster
:documentation "The taskmaster \(i.e. an instance of a
@@ -220,8 +221,14 @@
stream object in SOCKET. It reads the request headers, sets up the
request and reply objects, and hands over to PROCESS-REQUEST. This is
done in a loop until the stream has to be closed or until a connection
-timeout occurs."))
+timeout occurs.
+It is probably not a good idea to re-implement this method until you
+really, really know what you're doing, but you can for example write
+an around method specialized for your subclass of ACCEPTOR which binds
+or rebinds special variables which can then be accessed by your
+handlers."))
+
(defgeneric acceptor-ssl-p (acceptor)
(:documentation "Returns a true value if ACCEPTOR uses SSL
connections. The default is to unconditionally return NIL and
@@ -402,9 +409,9 @@
nil)
(defun list-request-dispatcher (request)
- "The default handler selector which selects a request handler based
-on a list of individual request dispatchers all of which can either
-return a handler or neglect by returning NIL."
+ "The default request dispatcher which selects a request handler
+based on a list of individual request dispatchers all of which can
+either return a handler or neglect by returning NIL."
(loop for dispatcher in *dispatch-table*
for action = (funcall dispatcher request)
when action return (funcall action)
Modified: trunk/thirdparty/hunchentoot/doc/index.xml
===================================================================
--- trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 14:57:40 UTC (rev 4279)
+++ trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 19:58:57 UTC (rev 4280)
@@ -605,6 +605,15 @@
handler for the request and sends its reply to the client. This is
done in a loop until the stream has to be closed or until a connection
timeout occurs.
+
+<p>
+It is probably not a good idea to re-implement this method until you
+really, really know what you're doing, but you can for example write
+an around method specialized for your subclass
+of <clix:ref>ACCEPTOR</clix:ref> which binds or rebinds special
+variables which can then be accessed by
+your <a href="#handlers">handlers</a>.
+</p>
</clix:description>
</clix:function>
Modified: trunk/thirdparty/hunchentoot/request.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/request.lisp 2009-02-18 14:57:40 UTC (rev 4279)
+++ trunk/thirdparty/hunchentoot/request.lisp 2009-02-18 19:58:57 UTC (rev 4280)
@@ -96,11 +96,18 @@
the REQUEST-CLASS slot of the ACCEPTOR class."))
(defgeneric process-request (request)
- (:documentation "This function is called by PROCESS-CONNECTION after the incoming
-headers have been read. It selects and calls a handler and sends the
-output of this handler to the client using START-OUTPUT. It also sets
-up simple error handling for the actual request handler.
+ (:documentation "This function is called by PROCESS-CONNECTION after
+the incoming headers have been read. It selects and calls a handler
+and sends the output of this handler to the client using START-OUTPUT.
+It also sets up simple error handling for the actual request handler.
+Note that PROCESS-CONNECTION is called once per connection and loops
+in case of a persistent connection while PROCESS-REQUEST is called
+anew for each request.
+Like PROCESS-CONNECTION, this might be a good place to introduce
+around methods which bind special variables or do other interesting
+things.
+
The return value of this function is ignored."))
(defun convert-hack (string external-format)
@@ -204,9 +211,9 @@
(setf (return-code*) +http-bad-request+)))))
(defmethod process-request (request)
-
- "Standard implementation for processing a request."
-
+ "Standard implementation for processing a request. You should not
+change or replace this functionality unless you know what you're
+doing."
(let (*tmp-files* *headers-sent*)
(unwind-protect
(let* ((*request* request))
Modified: trunk/thirdparty/hunchentoot/session.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/session.lisp 2009-02-18 14:57:40 UTC (rev 4279)
+++ trunk/thirdparty/hunchentoot/session.lisp 2009-02-18 19:58:57 UTC (rev 4280)
@@ -127,11 +127,11 @@
(defun encode-session-string (id user-agent remote-addr start)
"Creates a uniquely encoded session string based on the values ID,
USER-AGENT, REMOTE-ADDR, and START"
- ;; *SESSION-SECRET* is used twice due to known theoretical
- ;; vulnerabilities of MD5 encoding
(unless (boundp '*session-secret*)
(hunchentoot-warn "Session secret is unbound. Using Lisp's RANDOM function to initialize it.")
(reset-session-secret))
+ ;; *SESSION-SECRET* is used twice due to known theoretical
+ ;; vulnerabilities of MD5 encoding
(md5-hex (concatenate 'string
*session-secret*
(md5-hex (format nil "~A~A~@[~A~]~@[~A~]~A"
Revision: 4279
Author: hans
URL: http://bknr.net/trac/changeset/4279
Warn about unbound *session-secret* when sessions are first used, not upon
startup.
Rename handler-selector to request-dispatcher.
Make PROCESS-REQUEST a generic function and export it so that applications
can bind special variables early in the request processing chain.
U trunk/thirdparty/hunchentoot/acceptor.lisp
U trunk/thirdparty/hunchentoot/packages.lisp
U trunk/thirdparty/hunchentoot/request.lisp
U trunk/thirdparty/hunchentoot/session.lisp
Modified: trunk/thirdparty/hunchentoot/acceptor.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 00:32:16 UTC (rev 4278)
+++ trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 14:57:40 UTC (rev 4279)
@@ -62,13 +62,13 @@
objects is created when a request is served in and should be \(a
symbol naming) a class which inherits from REPLY. The default is the
symbol REPLY.")
- (handler-selector :initarg :handler-selector
- :accessor acceptor-handler-selector
- :documentation "A designator for the handler
-selector function used by this acceptor. A function which accepts a
+ (request-dispatcher :initarg :request-dispatcher
+ :accessor acceptor-request-dispatcher
+ :documentation "A designator for the request
+dispatcher function used by this acceptor. A function which accepts a
REQUEST object and calls a request handler of its choice \(and returns
its return value). The default is the unexported symbol
-LIST-HANDLER-SELECTOR which works through the list *DISPATCH-TABLE*.")
+LIST-REQUEST-DISPATCHER which works through the list *DISPATCH-TABLE*.")
(taskmaster :initarg :taskmaster
:reader acceptor-taskmaster
:documentation "The taskmaster \(i.e. an instance of a
@@ -151,7 +151,7 @@
:name (gensym)
:request-class 'request
:reply-class 'reply
- :handler-selector 'list-handler-selector
+ :request-dispatcher 'list-request-dispatcher
:taskmaster (make-instance (cond (*supports-threads-p* 'one-thread-per-connection-taskmaster)
(t 'single-threaded-taskmaster)))
:output-chunking-p t
@@ -230,11 +230,6 @@
;; general implementation
-(defmethod start :before ((acceptor acceptor))
- (unless (boundp '*session-secret*)
- (hunchentoot-warn "Session secret is unbound. Using Lisp's RANDOM function to initialize it.")
- (reset-session-secret)))
-
(defmethod start ((acceptor acceptor))
(start-listening acceptor)
(let ((taskmaster (acceptor-taskmaster acceptor)))
@@ -286,7 +281,6 @@
(defmethod process-connection ((*acceptor* acceptor) (socket t))
(let ((*hunchentoot-stream*
(initialize-connection-stream *acceptor* (make-socket-stream socket *acceptor*))))
- (print *hunchentoot-stream*)
(unwind-protect
;; process requests until either the acceptor is shut down,
;; *CLOSE-HUNCHENTOOT-STREAM* has been set to T by the
@@ -337,53 +331,6 @@
(ignore-errors
(force-output *hunchentoot-stream*)
(close *hunchentoot-stream* :abort t))))))
-
-(defun process-request (request)
- "This function is called by PROCESS-CONNECTION after the incoming
-headers have been read. It selects and calls a handler and sends the
-output of this handler to the client using START-OUTPUT. It also sets
-up simple error handling for the actual request handler.
-
-The return value of this function is ignored."
- (let (*tmp-files* *headers-sent*)
- (unwind-protect
- (let* ((*request* request))
- (multiple-value-bind (body error)
- (catch 'handler-done
- (handler-bind ((error
- (lambda (cond)
- (when *log-lisp-errors-p*
- (log-message *lisp-errors-log-level* "~A" cond))
- ;; if the headers were already sent
- ;; the error happens within the body
- ;; and we have to close the stream
- (when *headers-sent*
- (setq *close-hunchentoot-stream* t))
- (throw 'handler-done
- (values nil cond))))
- (warning
- (lambda (cond)
- (when *log-lisp-warnings-p*
- (log-message *lisp-warnings-log-level* "~A" cond)))))
- ;; skip dispatch if bad request
- (when (eql (return-code *reply*) +http-ok+)
- ;; now do the work
- (funcall (acceptor-handler-selector *acceptor*) *request*))))
- (when error
- (setf (return-code *reply*)
- +http-internal-server-error+))
- (start-output :content (cond ((and error *show-lisp-errors-p*)
- (format nil "<pre>~A</pre>"
- (escape-for-html (format nil "~A" error))))
- (error
- "An error has occured.")
- (t body)))))
- (dolist (path *tmp-files*)
- (when (and (pathnamep path) (probe-file path))
- ;; the handler may have chosen to (re)move the uploaded
- ;; file, so ignore errors that happen during deletion
- (ignore-errors
- (delete-file path)))))))
(defmethod acceptor-ssl-p ((acceptor t))
;; the default is to always answer "no"
@@ -454,7 +401,7 @@
(mp:process-unstop (acceptor-process acceptor))
nil)
-(defun list-handler-selector (request)
+(defun list-request-dispatcher (request)
"The default handler selector which selects a request handler based
on a list of individual request dispatchers all of which can either
return a handler or neglect by returning NIL."
Modified: trunk/thirdparty/hunchentoot/packages.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/packages.lisp 2009-02-18 00:32:16 UTC (rev 4278)
+++ trunk/thirdparty/hunchentoot/packages.lisp 2009-02-18 14:57:40 UTC (rev 4279)
@@ -121,7 +121,7 @@
"ACCEPTOR-ACCESS-LOGGER"
"ACCEPTOR-ADDRESS"
"ACCEPT-CONNECTIONS"
- "ACCEPTOR-HANDLER-SELECTOR"
+ "ACCEPTOR-REQUEST-DISPATCHER"
"ACCEPTOR-INPUT-CHUNKING-P"
"ACCEPTOR-MESSAGE-LOGGER"
"ACCEPTOR-NAME"
@@ -194,6 +194,7 @@
"POST-PARAMETERS"
"POST-PARAMETERS*"
"PROCESS-CONNECTION"
+ "PROCESS-REQUEST"
"QUERY-STRING"
"QUERY-STRING*"
"RAW-POST-DATA"
Modified: trunk/thirdparty/hunchentoot/request.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/request.lisp 2009-02-18 00:32:16 UTC (rev 4278)
+++ trunk/thirdparty/hunchentoot/request.lisp 2009-02-18 14:57:40 UTC (rev 4279)
@@ -95,6 +95,14 @@
can subclass REQUEST in order to implement your own behaviour. See
the REQUEST-CLASS slot of the ACCEPTOR class."))
+(defgeneric process-request (request)
+ (:documentation "This function is called by PROCESS-CONNECTION after the incoming
+headers have been read. It selects and calls a handler and sends the
+output of this handler to the client using START-OUTPUT. It also sets
+up simple error handling for the actual request handler.
+
+The return value of this function is ignored."))
+
(defun convert-hack (string external-format)
"The rfc2388 package is buggy in that it operates on a character
stream and thus only accepts encodings which are 8 bit transparent.
@@ -195,6 +203,50 @@
;; we assume it's not our fault...
(setf (return-code*) +http-bad-request+)))))
+(defmethod process-request (request)
+
+ "Standard implementation for processing a request."
+
+ (let (*tmp-files* *headers-sent*)
+ (unwind-protect
+ (let* ((*request* request))
+ (multiple-value-bind (body error)
+ (catch 'handler-done
+ (handler-bind ((error
+ (lambda (cond)
+ (when *log-lisp-errors-p*
+ (log-message *lisp-errors-log-level* "~A" cond))
+ ;; if the headers were already sent
+ ;; the error happens within the body
+ ;; and we have to close the stream
+ (when *headers-sent*
+ (setq *close-hunchentoot-stream* t))
+ (throw 'handler-done
+ (values nil cond))))
+ (warning
+ (lambda (cond)
+ (when *log-lisp-warnings-p*
+ (log-message *lisp-warnings-log-level* "~A" cond)))))
+ ;; skip dispatch if bad request
+ (when (eql (return-code *reply*) +http-ok+)
+ ;; now do the work
+ (funcall (acceptor-request-dispatcher *acceptor*) *request*))))
+ (when error
+ (setf (return-code *reply*)
+ +http-internal-server-error+))
+ (start-output :content (cond ((and error *show-lisp-errors-p*)
+ (format nil "<pre>~A</pre>"
+ (escape-for-html (format nil "~A" error))))
+ (error
+ "An error has occured.")
+ (t body)))))
+ (dolist (path *tmp-files*)
+ (when (and (pathnamep path) (probe-file path))
+ ;; the handler may have chosen to (re)move the uploaded
+ ;; file, so ignore errors that happen during deletion
+ (ignore-errors
+ (delete-file path)))))))
+
(defun parse-multipart-form-data (request external-format)
"Parse the REQUEST body as multipart/form-data, assuming that its
content type has already been verified. Returns the form data as
Modified: trunk/thirdparty/hunchentoot/session.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/session.lisp 2009-02-18 00:32:16 UTC (rev 4278)
+++ trunk/thirdparty/hunchentoot/session.lisp 2009-02-18 14:57:40 UTC (rev 4279)
@@ -129,6 +129,9 @@
USER-AGENT, REMOTE-ADDR, and START"
;; *SESSION-SECRET* is used twice due to known theoretical
;; vulnerabilities of MD5 encoding
+ (unless (boundp '*session-secret*)
+ (hunchentoot-warn "Session secret is unbound. Using Lisp's RANDOM function to initialize it.")
+ (reset-session-secret))
(md5-hex (concatenate 'string
*session-secret*
(md5-hex (format nil "~A~A~@[~A~]~@[~A~]~A"
Revision: 4278
Author: edi
URL: http://bknr.net/trac/changeset/4278
Enough for today
U trunk/thirdparty/hunchentoot/acceptor.lisp
U trunk/thirdparty/hunchentoot/doc/index.xml
U trunk/thirdparty/hunchentoot/specials.lisp
Modified: trunk/thirdparty/hunchentoot/acceptor.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 00:24:33 UTC (rev 4277)
+++ trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 00:32:16 UTC (rev 4278)
@@ -99,15 +99,13 @@
:documentation "The read timeout of the acceptor,
specified in \(fractional) seconds. The precise semantics of this
parameter is determined by the underlying Lisp's implementation of
-socket timeouts. NIL \(which is the default that you might want to
-change for production environments) means no timeout.")
+socket timeouts. NIL means no timeout.")
(write-timeout :initarg :write-timeout
:reader acceptor-write-timeout
:documentation "The write timeout of the acceptor,
specified in \(fractional) seconds. The precise semantics of this
parameter is determined by the underlying Lisp's implementation of
-socket timeouts. NIL \(which is the default that you might want to
-change for production environments) means no timeout.")
+socket timeouts. NIL means no timeout.")
#+:lispworks
(process :accessor acceptor-process
:documentation "The Lisp process which accepts incoming
@@ -159,8 +157,8 @@
:output-chunking-p t
:input-chunking-p t
:persistent-connections-p t
- :read-timeout nil
- :write-timeout nil
+ :read-timeout *default-connection-timeout*
+ :write-timeout *default-connection-timeout*
:access-logger 'log-access-to-file
:message-logger 'log-message-to-file)
(:documentation "To create a Hunchentoot webserver, you make an
Modified: trunk/thirdparty/hunchentoot/doc/index.xml
===================================================================
--- trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 00:24:33 UTC (rev 4277)
+++ trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 00:32:16 UTC (rev 4278)
@@ -1077,9 +1077,30 @@
</clix:description>
</clix:readers>
+
</clix:subchapter>
+ <clix:subchapter name="replies" title="Reply objects">
+ </clix:subchapter>
+ <clix:subchapter name="sessions" title="Sessions">
+ </clix:subchapter>
+
+ <clix:subchapter name="handlers" title="Handlers">
+ </clix:subchapter>
+
+ <clix:subchapter name="handler-selection" title="Handler selection">
+ </clix:subchapter>
+
+ <clix:subchapter name="logging" title="Logging">
+ </clix:subchapter>
+
+ <clix:subchapter name="conditions" title="Conditions">
+ </clix:subchapter>
+
+ <clix:subchapter name="misc" title="Miscellaneous">
+ </clix:subchapter>
+
</clix:chapter>
<clix:chapter name='dict' title='The HUNCHENTOOT dictionary'>
@@ -1986,18 +2007,8 @@
<clix:description>Superclass for all errors related to Hunchentoot.
</clix:description>
</clix:condition>
- <clix:function name='hunchentoot-error'>
- <clix:lambda-list>format-control
- <clix:lkw>rest
- </clix:lkw> format-arguments
- </clix:lambda-list>
- <clix:returns>result
- </clix:returns>
- <clix:description>Signals an error of type HUNCHENTOOT-SIMPLE-ERROR with the provided
-format control and arguments.
- </clix:description>
- </clix:function>
+
<clix:function name='log-message'>
<clix:lambda-list>log-level format-string
<clix:lkw>rest
@@ -2475,15 +2486,6 @@
<clix:description>Whether the current connection to the client is secure.
</clix:description>
</clix:function>
- <clix:function generic='true' name='start'>
- <clix:lambda-list>acceptor
- </clix:lambda-list>
- <clix:returns>result
- </clix:returns>
- <clix:description>Starts the ACCEPTOR so that it begins accepting
-connections. Returns the acceptor.
- </clix:description>
- </clix:function>
<clix:function name='start-session'>
<clix:lambda-list>
Modified: trunk/thirdparty/hunchentoot/specials.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/specials.lisp 2009-02-18 00:24:33 UTC (rev 4277)
+++ trunk/thirdparty/hunchentoot/specials.lisp 2009-02-18 00:32:16 UTC (rev 4278)
@@ -308,8 +308,8 @@
"Length of buffers used for internal purposes.")
(defvar *default-connection-timeout* 20
- "The default connection timeout used when a Hunchentoot server is
-reading from and writing to a socket stream.")
+ "The default connection timeout used when an acceptor is reading
+from and writing to a socket stream.")
(defvar-unbound *local-host*
"Bound to a string denoting the address at which the current
Revision: 4277
Author: edi
URL: http://bknr.net/trac/changeset/4277
And more.
U trunk/thirdparty/hunchentoot/doc/index.xml
Modified: trunk/thirdparty/hunchentoot/doc/index.xml
===================================================================
--- trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 00:21:31 UTC (rev 4276)
+++ trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-18 00:24:33 UTC (rev 4277)
@@ -2634,7 +2634,7 @@
using
the <a href="http://common-lisp.net/project/usocket/">usocket</a>
and <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux
- Threads</a> for non-LispWorks Lisps, thereby removing most of
+ Threads</a> libraries for non-LispWorks Lisps, thereby removing most of
the platform dependent code. Threading behaviour was made
controllable through the introduction of
taskmasters. <a href="http://www.cliki.net/mod_lisp">mod_lisp</a>
@@ -2661,10 +2661,7 @@
<clix:chapter name="ack" title="Acknowledgements">
Thanks to Jeff Caldwell - TBNL would not have been released
- without his efforts. Thanks
- to <a href="http://www.fractalconcept.com/">Marc Battyani</a> for
- mod_lisp and to <a href="http://www.swiss.ai.mit.edu/~cph/">Chris
- Hanson</a> for mod_lisp2. Thanks
+ without his efforts. Thanks
to <a href="http://www.cliki.net/Stefan%20Scholl">Stefan
Scholl</a> and Travis Cross for various additions and fixes to
TBNL, to <a href="http://www.foldr.org/~michaelw/">Michael
@@ -2692,7 +2689,10 @@
Hunchentoot to other Lisps than LispWorks, I stole code from
ACL-COMPAT, <a href="http://www.cliki.net/kmrcl">KMRCL</a>,
and <a href="http://www.cliki.net/trivial-sockets">trivial-sockets</a> for
- implementation-dependent stuff like sockets and MP.
+ implementation-dependent stuff like sockets and MP. (This has been replaced by
+ <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux
+ Threads</a>
+ and <a href="http://common-lisp.net/project/usocket/">usocket</a>.)
</p>
<p>
Parts of this documentation were prepared
Revision: 4276
Author: edi
URL: http://bknr.net/trac/changeset/4276
More changes
U trunk/thirdparty/hunchentoot/acceptor.lisp
U trunk/thirdparty/hunchentoot/packages.lisp
U trunk/thirdparty/hunchentoot/request.lisp
U trunk/thirdparty/hunchentoot/taskmaster.lisp
Modified: trunk/thirdparty/hunchentoot/acceptor.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 00:21:06 UTC (rev 4275)
+++ trunk/thirdparty/hunchentoot/acceptor.lisp 2009-02-18 00:21:31 UTC (rev 4276)
@@ -197,7 +197,9 @@
(defgeneric accept-connections (acceptor)
(:documentation "In a loop, accepts a connection and hands it over
to the acceptor's taskmaster for processing using
-HANDLE-INCOMING-CONNECTION."))
+HANDLE-INCOMING-CONNECTION. On LispWorks, this function returns
+immediately, on other Lisps it retusn only once the acceptor has been
+stopped."))
(defgeneric initialize-connection-stream (acceptor stream)
(:documentation "Can be used to modify the stream which is used to
@@ -246,7 +248,8 @@
(setf (acceptor-shutdown-p acceptor) t)
(shutdown (acceptor-taskmaster acceptor))
#-:lispworks
- (usocket:socket-close (acceptor-listen-socket acceptor)))
+ (usocket:socket-close (acceptor-listen-socket acceptor))
+ acceptor)
(defmethod initialize-connection-stream ((acceptor acceptor) stream)
(declare (ignore acceptor))
@@ -397,7 +400,8 @@
usocket:*wildcard-host*)
(acceptor-port acceptor)
:reuseaddress t
- :element-type '(unsigned-byte 8))))
+ :element-type '(unsigned-byte 8)))
+ (values))
#-:lispworks
(defmethod accept-connections ((acceptor acceptor))
@@ -444,11 +448,13 @@
(when startup-condition
(error startup-condition))
(mp:process-stop listener-process)
- (setf (acceptor-process acceptor) listener-process)))
+ (setf (acceptor-process acceptor) listener-process)
+ (values)))
#+:lispworks
(defmethod accept-connections ((acceptor acceptor))
- (mp:process-unstop (acceptor-process acceptor)))
+ (mp:process-unstop (acceptor-process acceptor))
+ nil)
(defun list-handler-selector (request)
"The default handler selector which selects a request handler based
Modified: trunk/thirdparty/hunchentoot/packages.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/packages.lisp 2009-02-18 00:21:06 UTC (rev 4275)
+++ trunk/thirdparty/hunchentoot/packages.lisp 2009-02-18 00:21:31 UTC (rev 4276)
@@ -120,6 +120,7 @@
"ACCEPTOR"
"ACCEPTOR-ACCESS-LOGGER"
"ACCEPTOR-ADDRESS"
+ "ACCEPT-CONNECTIONS"
"ACCEPTOR-HANDLER-SELECTOR"
"ACCEPTOR-INPUT-CHUNKING-P"
"ACCEPTOR-MESSAGE-LOGGER"
@@ -193,6 +194,7 @@
"POST-PARAMETERS"
"POST-PARAMETERS*"
"PROCESS-CONNECTION"
+ "QUERY-STRING"
"QUERY-STRING*"
"RAW-POST-DATA"
"REAL-REMOTE-ADDR"
Modified: trunk/thirdparty/hunchentoot/request.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/request.lisp 2009-02-18 00:21:06 UTC (rev 4275)
+++ trunk/thirdparty/hunchentoot/request.lisp 2009-02-18 00:21:31 UTC (rev 4276)
@@ -89,7 +89,7 @@
POST request, populated only if not a multipart/form-data request."))
(:documentation "Objects of this class hold all the information
about an incoming request. They are created automatically by
-Hunchentoot and can be accessed by the corresponding handler.
+acceptors and can be accessed by the corresponding handler.
You should not mess with the slots of these objects directly, but you
can subclass REQUEST in order to implement your own behaviour. See
Modified: trunk/thirdparty/hunchentoot/taskmaster.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/taskmaster.lisp 2009-02-18 00:21:06 UTC (rev 4275)
+++ trunk/thirdparty/hunchentoot/taskmaster.lisp 2009-02-18 00:21:31 UTC (rev 4276)
@@ -34,7 +34,9 @@
:documentation "A backpointer to the acceptor instance
this taskmaster works for."))
(:documentation "An instance of this class is responsible for
-distributing the work of handling requests when its acceptor "))
+distributing the work of handling requests for its acceptor. This is
+an \"abstract\" class in the sense that usually only instances of
+subclasses of TASKMASTER will be used."))
(defgeneric execute-acceptor (taskmaster)
(:documentation "This is a callback called by the acceptor once it
@@ -44,24 +46,19 @@
taskmaster instance the method might be called from a new thread."))
(defgeneric handle-incoming-connection (taskmaster socket)
- (:documentation
- "This function is called by Hunchentoot to start processing of
-requests on a new incoming connection. SOCKET is the usocket instance
-that represents the new connection \(or a socket handle on LispWorks).
-The taskmaster starts processing requests on the incoming
-connection by calling the START-REQUEST-PROCESSING function of the
-acceptor instance, taken from the ACCEPTOR slot in the taskmaster
-instance. The SOCKET argument is passed to START-REQUEST-PROCESSING
-as argument.
+ (:documentation "This function is called by the acceptor to start
+processing of requests on a new incoming connection. SOCKET is the
+usocket instance that represents the new connection \(or a socket
+handle on LispWorks). The taskmaster starts processing requests on
+the incoming connection by calling the PROCESS-CONNECTION method of
+the acceptor instance. The SOCKET argument is passed to
+PROCESS-CONNECTION as an argument."))
-In a multi-threaded environment, the taskmaster runs this function
-in a separate thread. In a single-threaded environment, this function
-is called directly."))
-
(defgeneric shutdown (taskmaster)
(:documentation "Shuts down the taskmaster, i.e. frees all resources
that were set up by it. For example, a multi-threaded taskmaster
-might terminate all threads that are currently associated with it."))
+might terminate all threads that are currently associated with it.
+This function is called by the acceptor's STOP method."))
(defclass single-threaded-taskmaster (taskmaster)
()
@@ -88,12 +85,16 @@
connections and hands them off to new processes for request
handling."))
(:documentation "A taskmaster that starts one thread for listening
-to incoming requests and one thread for each incoming connection."))
+to incoming requests and one thread for each incoming connection.
+This is the default taskmaster implementation for multi-threaded Lisp
+implementations."))
+
;; usocket implementation
#-:lispworks
-(defmethod shutdown ((taskmaster taskmaster)))
+(defmethod shutdown ((taskmaster taskmaster))
+ taskmaster)
#-:lispworks
(defmethod shutdown ((taskmaster one-thread-per-connection-taskmaster))
@@ -101,7 +102,8 @@
(loop
(unless (bt:thread-alive-p (acceptor-process taskmaster))
(return))
- (sleep 1)))
+ (sleep 1))
+ taskmaster)
#-:lispworks
(defmethod execute-acceptor ((taskmaster one-thread-per-connection-taskmaster))
@@ -136,7 +138,8 @@
(when-let (process (acceptor-process (taskmaster-acceptor taskmaster)))
;; kill the main acceptor process, see LW documentation for
;; COMM:START-UP-SERVER
- (mp:process-kill process)))
+ (mp:process-kill process))
+ taskmaster)
#+:lispworks
(defmethod execute-acceptor ((taskmaster one-thread-per-connection-taskmaster))
Revision: 4275
Author: edi
URL: http://bknr.net/trac/changeset/4275
Checkpoint
U trunk/thirdparty/hunchentoot/doc/index.xml
Change set too large, please see URL above
Revision: 4274
Author: edi
URL: http://bknr.net/trac/changeset/4274
Checkpoint
U trunk/thirdparty/hunchentoot/doc/index.xml
Modified: trunk/thirdparty/hunchentoot/doc/index.xml
===================================================================
--- trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-17 22:59:48 UTC (rev 4273)
+++ trunk/thirdparty/hunchentoot/doc/index.xml 2009-02-17 23:31:17 UTC (rev 4274)
@@ -377,7 +377,7 @@
<clix:function generic='true' name='stop'>
<clix:lambda-list>acceptor
</clix:lambda-list>
- <clix:returns>result
+ <clix:returns>acceptor
</clix:returns>
<clix:description>Stops <clix:arg>acceptor</clix:arg> so that it
no longer accepts requests.
@@ -667,7 +667,114 @@
</clix:subchapter>
+ <clix:subchapter name="taskmasters" title="Taskmasters">
+As a "normal" Hunchentoot user, you can completely ignore taskmasters
+and skip this section. But if you're still reading, here are the
+dirty details: Each <a href="#acceptors">acceptor</a> has a taskmaster
+associated with it at creation time. It is the taskmaster's job to
+distribute the work of accepting and handling incoming connections.
+The acceptor calls the taskmaster if appropriate and the taskmaster
+calls back into the acceptor. This is done using the generic
+functions described in this and
+the <a href="#acceptor-behaviour">previous</a> section. Hunchentoot
+comes with two standard taskmaster implementations - one (which is the
+default used on multi-threaded Lisps) which starts a new thread for
+each incoming connection and one which handles all requests
+sequentially. It should for example be relatively straightforward to
+create a taskmaster which allocates threads from a fixed pool instead
+of creating a new one for each connection.
+<p>
+If you want to implement your own taskmasters, you should subclass
+<clix:ref>TASKMASTER</clix:ref> and specialize the generic functions in this section.
+</p>
+
+ <clix:class name='taskmaster'>
+ <clix:description>An instance of this class is responsible for
+distributing the work of handling requests for its acceptor.
+This is
+an "abstract" class in the sense that usually only instances of
+subclasses of <clix:ref>TASKMASTER</clix:ref> will be used.
+ </clix:description>
+ </clix:class>
+
+ <clix:class name='one-thread-per-connection-taskmaster'>
+ <clix:description>A taskmaster that starts one thread for listening
+to incoming requests and one thread for each incoming connection.
+<p>
+This is the default taskmaster implementation for multi-threaded Lisp
+implementations.
+</p>
+ </clix:description>
+ </clix:class>
+
+ <clix:class name='single-threaded-taskmaster'>
+ <clix:description>A taskmaster that runs synchronously in the
+thread where the <clix:ref>START</clix:ref> function was invoked (or
+in the case of LispWorks in the thread started
+by <a href="http://www.lispworks.com/documentation/lw51/LWRM/html/lwref-61.htm#marker-9…"><code>COMM:START-UP-SERVER</code></a>).
+This is the simplest possible taskmaster implementation in that its
+methods do nothing but calling their acceptor "sister"
+methods - <clix:ref>EXECUTE-ACCEPTOR</clix:ref> calls <clix:ref>ACCEPT-CONNECTIONS</clix:ref>,
+<clix:ref>HANDLE-INCOMING-CONNECTION</clix:ref> calls <clix:ref>PROCESS-CONNECTION</clix:ref>.
+ </clix:description>
+ </clix:class>
+
+ <clix:function generic='true' name='execute-acceptor'>
+ <clix:lambda-list>taskmaster
+ </clix:lambda-list>
+ <clix:returns>result
+ </clix:returns>
+ <clix:description>This is a callback called by the acceptor once it
+has performed all initial processing to start listening for incoming
+connections (see <clix:ref>START-LISTENING</clix:ref>). It usually calls the
+<clix:ref>ACCEPT-CONNECTIONS</clix:ref> method of the acceptor, but depending on the
+taskmaster instance the method might be called from a new thread.
+ </clix:description>
+ </clix:function>
+
+ <clix:function generic='true' name='handle-incoming-connection'>
+ <clix:lambda-list>taskmaster socket
+ </clix:lambda-list>
+ <clix:returns>result
+ </clix:returns>
+ <clix:description>This function is called by the acceptor to start
+processing of requests on a new incoming connection. <clix:arg>socket</clix:arg> is the
+usocket instance that represents the new connection (or a socket
+handle on LispWorks). The taskmaster starts processing requests on
+the incoming connection by calling the <clix:ref>PROCESS-CONNECTION</clix:ref>
+method of the acceptor instance. The <clix:arg>socket</clix:arg> argument is passed to
+<clix:ref>PROCESS-CONNECTION</clix:ref> as an argument.
+ </clix:description>
+ </clix:function>
+
+ <clix:function generic='true' name='shutdown'>
+ <clix:lambda-list>taskmaster
+ </clix:lambda-list>
+ <clix:returns>taskmaster
+ </clix:returns>
+ <clix:description>Shuts down the taskmaster, i.e. frees all resources
+that were set up by it. For example, a multi-threaded taskmaster
+might terminate all threads that are currently associated with it.
+This function is called by the acceptor's <clix:ref>STOP</clix:ref> method.
+ </clix:description>
+ </clix:function>
+
+ <clix:accessor generic='true' name='taskmaster-acceptor'>
+ <clix:lambda-list>taskmaster
+ </clix:lambda-list>
+ <clix:returns>acceptor
+ </clix:returns>
+ <clix:description>
+This is an accessor for the slot of a <clix:ref>TASKMASTER</clix:ref>
+object that links back to the <a href="#acceptors">acceptor</a> it is
+associated with.
+ </clix:description>
+ </clix:accessor>
+
+ </clix:subchapter>
+
+
</clix:chapter>
<clix:chapter name='dict' title='The HUNCHENTOOT dictionary'>
@@ -1516,18 +1623,7 @@
<clix:description>Escapes the characters #\<, #\>, #\', #\", and #\& for HTML output.
</clix:description>
</clix:function>
- <clix:function generic='true' name='execute-acceptor'>
- <clix:lambda-list>taskmaster
- </clix:lambda-list>
- <clix:returns>result
- </clix:returns>
- <clix:description>This is a callback called by the acceptor once it
-has performed all initial processing to start listening for incoming
-connections (see START-LISTENING). It usually calls the
-ACCEPT-CONNECTIONS method of the acceptor, but depending on the
-taskmaster instance the method might be called from a new thread.
- </clix:description>
- </clix:function>
+
<clix:function name='get-parameter'>
<clix:lambda-list>name
<clix:lkw>optional
@@ -1578,25 +1674,7 @@
TIME.
</clix:description>
</clix:function>
- <clix:function generic='true' name='handle-incoming-connection'>
- <clix:lambda-list>taskmaster socket
- </clix:lambda-list>
- <clix:returns>result
- </clix:returns>
- <clix:description>This function is called by Hunchentoot to start processing of
-requests on a new incoming connection. SOCKET is the usocket instance
-that represents the new connection (or a socket handle on LispWorks).
-The taskmaster starts processing requests on the incoming
-connection by calling the START-REQUEST-PROCESSING function of the
-acceptor instance, taken from the ACCEPTOR slot in the taskmaster
-instance. The SOCKET argument is passed to START-REQUEST-PROCESSING
-as argument.
-In a multi-threaded environment, the taskmaster runs this function
-in a separate thread. In a single-threaded environment, this function
-is called directly.
- </clix:description>
- </clix:function>
<clix:function name='handle-static-file'>
<clix:lambda-list>path
<clix:lkw>optional
@@ -1782,11 +1860,7 @@
<clix:description>Adds appropriate headers to completely prevent caching on most browsers.
</clix:description>
</clix:function>
- <clix:class name='one-thread-per-connection-taskmaster'>
- <clix:description>A taskmaster that starts one thread for listening
-to incoming requests and one thread for each incoming connection.
- </clix:description>
- </clix:class>
+
<clix:function name='parameter'>
<clix:lambda-list>name
<clix:lkw>optional
@@ -2449,26 +2523,6 @@
(case-sensitive) already exists, it is replaced.
</clix:description>
</clix:function>
- <clix:function generic='true' name='shutdown'>
- <clix:lambda-list>taskmaster
- </clix:lambda-list>
- <clix:returns>result
- </clix:returns>
- <clix:description>Shuts down the taskmaster, i.e. frees all resources
-that were set up by it. For example, a multi-threaded taskmaster
-might terminate all threads that are currently associated with it.
- </clix:description>
- </clix:function>
- <clix:class name='single-threaded-taskmaster'>
- <clix:description>A taskmaster that runs synchronously in the thread
-where the START function was invoked (or in the case of LispWorks in
-the thread started by COMM:START-UP-SERVER). This is the simplest
-possible taskmaster implementation in that its methods do nothing but
-calling their acceptor "sister" methods - EXECUTE-ACCEPTOR calls
-ACCEPT-CONNECTIONS, HANDLE-INCOMING-CONNECTION calls
-PROCESS-CONNECTION.
- </clix:description>
- </clix:class>
<clix:function name='ssl-p'>
<clix:lambda-list>
@@ -2501,27 +2555,6 @@
</clix:description>
</clix:function>
- <clix:class name='taskmaster'>
- <clix:description>An instance of this class is responsible for
-distributing the work of handling requests when its acceptor
- </clix:description>
- </clix:class>
- <clix:accessor generic='true' name='taskmaster-acceptor'>
- <clix:lambda-list>taskmaster
- </clix:lambda-list>
- <clix:returns>result
- </clix:returns>
- <clix:description>
- </clix:description>
- </clix:accessor>
- <clix:accessor specialized='(TASKMASTER)' name='taskmaster-acceptor'>
- <clix:lambda-list>(taskmaster taskmaster)
- </clix:lambda-list>
- <clix:returns>result
- </clix:returns>
- <clix:description>
- </clix:description>
- </clix:accessor>
<clix:function name='url-decode'>
<clix:lambda-list>string
<clix:lkw>optional