Abstract
Hunchentoot is a web server written in Common Lisp and at the same time a toolkit for building dynamic websites. As a stand-alone web server, Hunchentoot is capable of HTTP/1.1 chunking (both directions), persistent connections (keep-alive), and SSL, but it can also sit behind the popular Apache using Marc Battyani's mod_lisp.Hunchentoot provides facilities like automatic session handling (with and without cookies), logging (to Apache's log files or to a file in the file system), customizable error handling, and easy access to GET and POST parameters sent by the client. It does not include functionality to programmatically generate HTML output. For this task you can use any library you like, e.g. (shameless self-plug) CL-WHO or HTML-TEMPLATE.
Hunchentoot talks with its front-end or with the client over TCP/IP sockets and uses multiprocessing to handle several requests at the same time. Therefore, it cannot be implemented completely in portable Common Lisp. It currently works with LispWorks (which is the main development and testing platform), CMUCL (with MP support), SBCL (with Unicode and thread support), OpenMCL, and Allegro Common Lisp. Porting to other CL implementations shouldn't be too hard, see the files
port-xxx.lispandunix-xxx.lispwhich comprise all the implementation-specific code.Hunchentoot comes with a BSD-style license so you can basically do with it whatever you want.
Hunchentoot is used by Jalat, Heike Stephan, Makewavs, and ERGO.
Download shortcut: http://weitz.de/files/hunchentoot.tar.gz.
The preferred method to compile and load Hunchentoot is via ASDF.
Hunchentoot together with this documentation can be downloaded from http://weitz.de/files/hunchentoot.tar.gz. The current version is 0.6.1. There's also a port for Gentoo Linux thanks to Matthew Kennedy
ProxyPass /lisp http://127.0.0.1:3000/lisp ProxyPassReverse /lisp http://127.0.0.1:3000/lispThis will tunnel all requests where the URI path begins with "/lisp" to a (Hunchentoot) server listening on port 3000 on the same machine.
Of course, there are several other (more lightweight) web proxies that you could use instead of Apache.
For this setup you need two things:
apxs -c -i -a mod_lisp.cas root (and afterwards restart Apache).
The newest version of mod_lisp is available from http://www.fractalconcept.com:8000/public/open-source/mod_lisp/. For Apache 1.3.x you must use mod_lisp.c, for Apache 2.x you must use mod_lisp2.c, which is a reimplementation of Marc's mod_lisp by Chris Hanson.
You can get pre-compiled modules for the Win32 version of Apache 2 (but probably not the latest version) from http://www.fractalconcept.com:8000/public/open-source/mod_lisp/windows/. Put the file into Apache's modules folder and add the line
LoadModule lisp_module modules/mod_lisp2.soto your
httpd.conf file.
httpd.conf) add these lines
LispServer 127.0.0.1 3000 "foo" <Location /hunchentoot> SetHandler lisp-handler </Location>and afterwards restart Apache. This informs mod_lisp that there's a Lisp listening on port 3000 and named "foo" - you can of course use any other name or port or even put Hunchentoot on another physical machine. (In the latter case you'll have to replace
127.0.0.1 with the FQDN or IP address of
this machine.)
The Location/SetHandler part means that every URL which
starts with /hunchentoot will be handled by mod_lisp (and thus
Hunchentoot) on this server. (Again, you can of course use other locations. See the
Apache documentation for things like virtual hosts or
directives like LocationMatch.)
To interface a Hunchentoot server with mod_lisp, you must start it
with the :MOD-LISP-P keyword parameter
of START-SERVER set to a true
value.
To run the example, enter the following code into your listener:
(asdf:oos 'asdf:load-op :hunchentoot-test) (hunchentoot:start-server :port 4242)You should now be able to point your browser at
http://localhost:4242/hunchentoot/test and see
something.
A tutorial for (an older version of) Hunchentoot can be found at http://www.jalat.com/blogs/lisp?id=3 thanks to Asbjørn Bjørnstad. And there's a TBNL tutorial from Frank Buss. Hunchentoot is not TBNL, but the two are similar enough to make the tutorial worthwhile.
For Win32, Bill Clementson
explains how to set up Hunchentoot's predecessor TBNL with
Apache/mod_lisp in his blog entry
at http://bc.tech.coop/blog/041105.html. See also http://bc.tech.coop/blog/061013.html.
Starts a Hunchentoot server instance and returns it.portist the port the server will be listening on - the default is 80 (or 443 if SSL information is provided). Ifaddressis a string denoting an IP address, then the server only receives connections for that address. This must be one of the addresses associated with the machine and allowed values are host names such as"www.zappa.com"and address strings such as"72.3.247.29". IfaddressisNIL, then the server will receive connections to all IP addresses on the machine. This is the default.If
mod-lisp-pis true (the default isNIL), the server will act as a back-end for mod_lisp, otherwise it will be a stand-alone web server. Ifuse-apache-log-pis true (which is the default), log messages will be written to the Apache log file - this parameter has no effect ifmod-lisp-pis NIL.If
input-chunking-pis true (which is the default), the server will accept request bodies without aContent-Lengthheader if the client uses chunked transfer encoding. If you want to use this feature behind mod_lisp, you should make sure that your combination of Apache and mod_lisp can cope with that.
read-timeoutis the read timeout (in seconds) for the socket stream used by the server - the default value is*DEFAULT-READ-TIMEOUT*. This parameter is ignored on OpenMCL and AllegroCL.write-timeoutis the write timeout (in seconds) for the socket stream used by the server - the default value is*DEFAULT-WRITE-TIMEOUT*. This parameter is ignored on all implementations except for LispWorks 5.0 or higher. You can useNILin both cases to denote that you don't want a timeout. Ifmod-lisp-pis true, the timeouts are always set toNIL.On Unix you can use
setuidandsetgidto change the UID and GID of the process directly after the server has been started. (You might want to do this if you're using a privileged port like 80.)setuidandsetgidcan be integers (the actual IDs) or strings (for the user and group name respectively).If you want your server to use SSL, you must provide the pathname designator(s)
ssl-certificate-filefor the certificate file and optionallyssl-privatekey-filefor the private key file, both files must be in PEM format. If you only provide the value forssl-certificate-fileit is assumed that both the certificate and the private key are in one file. If your private key needs a password you can provide it through thessl-privatekey-passwordkeyword argument. If you don't use LispWorks, the private key must not be associated with a password, and the certificate and the private key must be in separate files.
[Function]
stop-server server => |
Stops a server started withSTART-SERVER.servermust be an object as returned bySTART-SERVER.
[Special variable]
*server*
During the execution of dispatch functions and handlers this variable is bound to the server object (as returned bySTART-SERVER) which processes the request.
[Readers]
server-local-port server => port
server-address server => address
These methods can be used to query a Hunchentoot server object. The values correspond to theportandaddressparameters ofSTART-SERVER.
[Special variable]
*default-read-timeout*
The default value for theread-timeoutkeyword argument toSTART-SERVER. The initial value is 20 (seconds).
[Special variable]
*default-write-timeout*
The default value for thewrite-timeoutkeyword argument toSTART-SERVER. The initial value is 20 (seconds).
[Special variable]
*cleanup-interval*
Should beNILor a positive integer. The system calls*CLEANUP-FUNCTION*whenever*CLEANUP-INTERVAL*new worker threads have been created unless the value isNIL. The initial value is 100.
[Special variable]
*cleanup-function*
The function (with no arguments) which is called if*CLEANUP-INTERVAL*is notNIL. The initial value is a function which calls(HCL:on LispWorks and does nothing on other Lisps.MARK-AND-SWEEP2)On LispWorks this is necessary because each worker (which is created to handle an incoming http request and which dies afterwards unless the connection is persistent) is a Lisp process and LispWorks creates processes in generation 2.
Note that you can also set this value to
NILand tune LispWork's GC yourself, using for exampleCOLLECT-GENERATION-2.
test/test.lisp for examples.)
[Special variable]
*dispatch-table*
This is a list of function designators for dispatch functions each of which should be a function of one argument which accepts aREQUESTobject and, depending on this object, should either return a handler to handle the request orNILwhich means that the next dispatcher will be queried. A handler is a designator for a function with no arguments which usually returns a string or an array of octets to be sent to the client as the body of the http reply. (Note that if you use symbols as function designators, you can redefine your handler functions without the need to change the dispatch functions.) See the section about replies for more about what handlers can do.The dispatchers in
*DISPATCH-TABLE*are tried in turn until one of them returns a handler. If this doesn't happen, Hunchentoot will return a 404 status code (Not Found) to the client.The default value of
*DISPATCH-TABLE*is a list which just contains the symbolDEFAULT-DISPATCHER. See also*META-DISPATCHER*.
[Function]
default-dispatcher request => handler
This is a function which will always unconditionally return the value of*DEFAULT-HANDLER*. It is intended to be the last element of*DISPATCH-TABLE*
[Special variable]
*default-handler*
This variable holds the handler which is always returned byDEFAULT-DISPATCHER. The default value is a function which unconditonally shows a short Hunchentoot info page.
[Special variable]
*meta-dispatcher*
The value of this variable should be a function of one argument. It is called with the current Hunchentoot server instance and must return a dispatch table suitable for Hunchentoot. The initial value is a function which always unconditionally returns*DISPATCH-TABLE*.This can obviously be used to assign different dispatch tables to different servers (and is useless if you only have one server).
[Function]
create-prefix-dispatcher prefix handler => dispatch-fn
A convenience function which will return a dispatcher that returnshandlerwhenever the path part of the request URI starts with the stringprefix.
[Function]
create-regex-dispatcher regex handler => dispatch-fn
A convenience function which will return a dispatcher that returnshandlerwhenever the path part of the request URI matches the CL-PPCRE regular expressionregex(which can be a string, an s-expression, or a scanner).
[Function]
handle-static-file path &optional content-type => nil
Sends the file denote by the pathname designatorpathwith content typecontent-typeto the client. Sets the necessary handlers. In particular the function employsHANDLE-IF-MODIFIED-SINCE.If
content-typeisNILthe function tries to determine the correct content type from the file's suffix or falls back to"application/octet-stream"as a last resort.Note that this function calls
SEND-HEADERSinternally, so after you've called it, the headers are sent and the return value of your handler is ignored.
[Function]
create-static-file-dispatcher-and-handler uri path &optional content-type => dispatch-fn
A convenience function which will return a dispatcher that dispatches to a handler which emits the file denoted by the pathname designatorpathwith content typecontent-typeif theSCRIPT-NAMEof the request matches the stringuri. UsesHANDLE-STATIC-FILEinternally.If
content-typeisNILthe function tries to determine the correct content type from the file's suffix or falls back to"application/octet-stream"as a last resort.*DEFAULT-CONTENT-TYPE*.
[Function]
create-folder-dispatcher-and-handler uri-prefix base-path &optional content-type => dispatch-fn
Creates and returns a dispatch function which will dispatch to a handler function which emits the file relative tobase-paththat is denoted by the URI of the request relative touri-prefix.uri-prefixmust be a string ending with a slash,base-pathmust be a pathname designator for an existing directory. UsesHANDLE-STATIC-FILEinternally.If
content-typeis notNIL, it will be used as a the content type for all files in the folder. Otherwise (which is the default) the content type of each file will be determined as usual.
[Generic function]
dispatch-request dispatch-table => result
This is a generic function so users can customize its behaviour. Look at the source code for details.
[Macro]
define-easy-handler description lambda-list [[declaration* | documentation]] form*
Defines a handler as if byDEFUNand optionally registers it with a URI so that it will be found byDISPATCH-EASY-HANDLERS.
descriptionis either a symbolnameor a list matching the destructuring lambda list(name &key uri default-parameter-type default-request-type).lambda-listis a list the elements of which are either a symbolvaror a list matching the destructuring lambda list(var &key real-name parameter-type init-form request-type).The resulting handler will be a Lisp function with the namenameand keyword parameters named by thevarsymbols. Eachvarwill be bound to the value of the GET or POST parameter calledreal-name(a string) before the body of the function is executed. Ifreal-nameis not provided, it will be computed by downcasing the symbol name ofname.If
uri(which is evaluated) is provided, then it must be a string or a function designator for a unary function. In this case, the handler will be returned byDISPATCH-EASY-HANDLERS, ifuriis a string and the script name of the current request isuri, or ifuridesignates a function and applying this function to the currentREQUESTobject returns a true value.Whether the GET or POST parameter (or both) will be taken into consideration, depends on
request-typewhich can be:GET,:POST,:BOTH, orNIL. In the last case, the value ofdefault-request-type(the default of which is:BOTH) will be used.The value of
varwill usually be a string (unless it resulted from a file upload in which case it won't be converted at all), but ifparameter-type(which is evaluated) is provided, the string will be converted to another Lisp type by the following rules:If the corresponding GET or POST parameter wasn't provided by the client,
var's value will beNIL. Ifparameter-typeis'STRING,var's value remains as is. Ifparameter-typeis'INTEGERand the parameter string consists solely of decimal digits,var's value will be the corresponding integer, otherwiseNIL. Ifparameter-typeis'KEYWORD,var's value will be the keyword obtained by interning the upcased parameter string into the keyword package. Ifparameter-typeis'CHARACTERand the parameter string is of length one,var's value will be the single character of this string, otherwiseNIL. Ifparameter-typeis'BOOLEAN,var's value will always beT(unless it isNILby the first rule above, of course). Ifparameter-typeis any other atom, it is supposed to be a function designator for a unary function which will be called to convert the string to something else.Those were the rules for simple parameter types, but
parameter-typecan also be a list starting with one of the symbolsLIST,ARRAY, orHASH-TABLE. The second value of the list must always be a simple parameter type as in the last paragraph - we'll call it the inner type below.In the case of
'LIST, all GET/POST parameters calledreal-namewill be collected, converted to the inner type as by the rules above, and assembled into a list which will be the value ofvar.In the case of
'ARRAY, all GET/POST parameters which have a name like the result of(format nil "~A[~A]" real-name n)wherenis a non-negative integer, will be assembled into an array where thenth element will be set accordingly, after conversion to the inner type. The array, which will become the value ofvar, will be big enough to hold all matching parameters, but not bigger. Array elements not set as described above will beNIL. Note thatVARwill always be bound to an array, which may be empty, so it will never beNIL, even if no appropriate GET/POST parameters are found.The full form of a
'HASH-TABLEparameter type is(hash-table inner-type key-type test-function),butkey-typeandtest-functioncan be left out in which case they default to'STRINGand'EQUAL, respectively. For this parameter type, all GET/POST parameters which have a name like the result of(format nil "~A{~A}" real-name key)(wherekeyis a string that doesn't contain curly brackets) will become the values (after conversion toinner-type) of a hash table with test functiontest-functionwherekey(after conversion tokey-type) will be the corresponding key. Note thatvarwill always be bound to a hash table, which may be empty, so it will never beNIL, even if no appropriate GET/POST parameters are found.To make matters even more complicated, the three compound parameter types also have an abbreviated form - just one of the symbols
LIST,ARRAY, orHASH-TABLE. In this case, the inner type will default to'STRING.If
parameter-typeis not provided orNIL,default-parameter-type(the default of which is'STRING) will be used instead.If the result of the computations above would be that
varwould be bound toNIL, theninit-form(if provided) will be evaluated instead, andvarwill be bound to the result of this evaluation.Handlers built with this macro are constructed in such a way that the resulting Lisp function is useful even outside of Hunchentoot. Specifically, all the parameter computations above will only happen if
*REQUEST*is bound, i.e. if we're within a Hunchentoot request. Otherwise,varwill always be bound to the result of evaluatinginit-formunless a corresponding keyword argument is provided.The example code that comes with Hunchentoot contains an example which demonstrates some of the features of
DEFINE-EASY-HANDLER.
[Function]
dispatch-easy-handlers request => handler
This is a dispatcher which returns the appropriate handler defined withDEFINE-EASY-HANDLER, if there is one. The newest handlers are checked first.DEFINE-EASY-HANDLERmakes sure that there's always only one handler per name and one per URI. URIs are compared byEQUAL, so anonymous functions won't be recognized as being identical.
REQUEST object
which is available to the handler via the
special variable *REQUEST*. This object holds
all the information available about the request and can be queried
with the functions described in this chapter. Note that the internal
structure of REQUEST objects should be considered opaque and may change
in future releases of Hunchentoot.
In all of the functions below, the default value
for request (which is either an optional or a
keyword argument) is the value of *REQUEST*,
i.e. handlers will usually not need to provide this argument when
calling the function.
(Some of the function names in this section might seem a bit strange. This is because they were initially chosen to be similar to environment variables in CGI scripts.)
[Special variable]
*request*
Holds the currentREQUESTobject.
[Function]
host &optional request => string
Returns the value of the incomingHosthttp header. (This corresponds to the environment variableHTTP_HOSTin CGI scripts.)
[Function]
request-method &optional request => keyword
Returns the request method as a keyword, i.e. something like:POST. (This corresponds to the environment variableREQUEST_METHODin CGI scripts.)
[Function]
request-uri &optional request => string
Returns the URI forrequest. Note that this not the full URI but only the part behind the scheme and authority components, so that if the user has typedhttp://user:password@www.domain.com/xxx/frob.html?foo=barinto his browser, this function will return"/xxx/frob.html?foo=bar". (This corresponds to the environment variableREQUEST_URIin CGI scripts.)
[Function]
script-name &optional request => string
Returns the file name (or path) component of the URI forrequest, i.e. the part of the string returned byREQUEST-URIin front of the first question mark (if any). (This corresponds to the environment variableSCRIPT_NAMEin CGI scripts.)
[Function]
query-string &optional request => string
Returns the query component of the URI forrequest, i.e. the part of the string returned byREQUEST-URIbehind the first question mark (if any). (This corresponds to the environment variableQUERY_STRINGin CGI scripts.) See alsoGET-PARAMETERandGET-PARAMETERS.
[Function]
get-parameter name &optional request => string
Returns the value of the GET parameter (as provided via the request URI) named by the stringnameas a string (orNILif there ain't no GET parameter with this name). Note that only the first value will be returned if the client provided more than one GET parameter with the namename. See alsoGET-PARAMETERS.
[Function]
get-parameters &optional request => alist
Returns an alist of all GET parameters (as provided via the request URI). The car of each element of this list is the parameter's name while the cdr is its value (as a string). The elements of this list are in the same order as they were within the request URI. See alsoGET-PARAMETER.
[Function]
post-parameter name &optional request => string
Returns the value of the POST parameter (as provided in the request's body) named by the stringname. Note that only the first value will be returned if the client provided more than one POST parameter with the namename. This value will usually be a string (orNILif there ain't no POST parameter with this name). If, however, the browser sent a file through a multipart/form-data form, the value of this function is a three-element list(path file-name content-type)wherepathis a pathname denoting the place were the uploaded file was stored,file-name(a string) is the file name sent by the browser, andcontent-type(also a string) is the content type sent by the browser. The file denoted bypathwill be deleted after the request has been handled - you have to move or copy it somewhere else if you want to keep it. See alsoPOST-PARAMETERSand*TMP-DIRECTORY*.
[Function]
post-parameters &optional request => alist
Returns an alist of all POST parameters (as provided via the request's body). The car of each element of this list is the parameter's name while the cdr is its value. The elements of this list are in the same order as they were within the request's body. See alsoPOST-PARAMETER.
[Special variable]
*file-upload-hook*
If this is notNIL, it should be a designator for a unary function which will be called with a pathname for each file which is uploaded to Hunchentoot. The pathname denotes the temporary file to which the uploaded file is written. The hook is called directly before the file is created. At this point,*REQUEST*is already bound to the currentREQUESTobject, but obviously you can't access the post parameters yet.
[Function]
raw-post-data &key request external-format force-text force-binary want-binary => raw-body-or-stream
Returns the content sent by the client in the request body if there was any (unless the content type wasmultipart/form-datain which caseNILis returned). By default, the result is a string if the type of theContent-Typemedia type is"text", and a vector of octets otherwise. In the case of a string, the external format to be used to decode the content will be determined from thecharsetparameter sent by the client (or otherwise*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*will be used).You can also provide an external format explicitly (through
external-format) in which case the result will unconditionally be a string. Likewise, you can provide a true value forforce-textwhich will force Hunchentoot to act as if the type of the media type had been"text"(withexternal-formattaking precedence if provided). Or you can provide a true value forforce-binarywhich means that you want a vector of octets at any rate. (If bothforce-textandforce-binaryare true, an error will be signaled.)If, however, you provide a true value for
want-stream, the other parameters are ignored and you'll get the content (flexi) stream to read from it yourself. It is then your responsibility to read the correct amount of data, because otherwise you won't be able to return a response to the client. If the content type of the request wasmultipart/form-dataorapplication/x-www-form-urlencoded, the content has been read by Hunchentoot already and you can't read from the stream anymore.You can call
RAW-POST-DATAmore than once per request, but you can't mix calls which have different values forwant-stream.Note that this function is slightly misnamed because a client can send content even if the request method is not POST.
[Function]
parameter name &optional request => string
Returns the value of the GET or POST parameter named by the stringnameas a string (orNILif there ain't no parameter with this name). If both a GET and a POST parameter with the namenameexist, the GET parameter will be returned. See alsoGET-PARAMETERandPOST-PARAMETER.
[Function]
header-in name &optional request => string
Returns the incoming header named by the keywordnameas a string (orNILif there ain't no header with this name). Note that this queries the headers sent to Hunchentoot by the client or by mod_lisp. In the latter case this may not only include the incoming http headers but also some headers sent by mod_lisp.For backwards compatibility,
namecan also be a string which is matched case-insensitively. See alsoHEADERS-IN.
[Function]
headers-in &optional request => alist
Returns an alist of all incoming headers. The car of each element of this list is the headers's name (a Lisp keyword) while the cdr is its value (as a string). There's no guarantee about the order of this list. See alsoHEADER-INand the remark about incoming headers there.
[Function]
authorization &optional request => user, password
Returns as two values the user and password (if any) from the incomingAuthorizationhttp header. ReturnsNILif there is no such header.
[Function]
remote-addr &optional request => string
Returns the IP address (as a string) of the client which sent the request. (This corresponds to the environment variableREMOTE_ADDRin CGI scripts.) See alsoREAL-REMOTE-ADDR.
[Function]
remote-port &optional request => number
Returns the IP port (as a number) of the client which sent the request.
[Function]
real-remote-addr &optional request => string{, list}
Returns the value of the incomingX-Forwarded-Forhttp header as the second value in the form of a list of IP addresses and the first element of this list as the first value if this header exists. Otherwise returns the value ofREMOTE-ADDRas the only value.
[Function]
server-addr &optional request => string
Returns the IP address (as a string) where the request came in. (This corresponds to the environment variableSERVER_ADDRin CGI scripts.)
[Function]
server-port &optional request => number
Returns the IP port (as a number) where the request came in.
[Function]
server-protocol &optional request => keyword
Returns the version of the http protocol which is used by the client as a Lisp keyword - this is usually either:HTTP/1.0or:HTTP/1.1. (This corresponds to the environment variableSERVER_PROTOCOLin CGI scripts.)
[Function]
mod-lisp-id &optional request => string
Returns the 'Server ID' sent by mod_lisp. This corresponds to the third parameter in the "LispServer" directive in Apache's configuration file and can be interesting if you deploy several different Apaches or Hunchentoot instances at once. ReturnsNILin stand-alone servers.
[Function]
ssl-session-id &optional request => string
Returns Apache's SSL session ID if it exists. Note that SSL sessions aren't related to Hunchentoot sessions. (This corresponds to the environment variableSSL_SESSION_IDin CGI scripts.) ReturnsNILin stand-alone servers.
[Function]
user-agent &optional request => string
Returns the value of the incomingUser-Agenthttp header. (This corresponds to the environment variableHTTP_USER_AGENTin CGI scripts.)
[Function]
referer &optional request => string
Returns the value of the incomingReferer(sic!) http header. (This corresponds to the environment variableHTTP_REFERERin CGI scripts.)
[Function]
cookie-in name &optional request => string
Returns the value of the incoming cookie named by the stringname(orNILif there ain't no cookie with this name). See alsoCOOKIES-IN.
[Function]
cookies-in &optional request => alist
Returns an alist of all incoming cookies. The car of each element of this list is the cookie's name while the cdr is the cookie's value. See alsoCOOKIE-IN.
This accessor can be used to associate arbitrary data with the the symbolsymbolin theREQUESTobjectrequest.present-pis true if such data was found, otherwiseNIL.
[Function]
delete-aux-request-value symbol &optional request => |
Completely removes any data associated with the symbolsymbolfrom theREQUESTobjectrequest. Note that this is different from usingAUX-REQUEST-VALUEto set the data toNIL.
[Function]
recompute-request-parameters &key request external-format => |
Recomputes the GET and POST parameters for theREQUESTobjectrequest. This only makes sense if you've changed the external format and with POST parameters it will only work if the request body was sent with theapplication/x-www-form-urlencodedcontent type.The default value for
external-formatis*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*. Seetest/test.lispfor an example.
REPLY object which will be described in this section.
REPLY object which is accessible
to the handler via the
special variable *REPLY*. This object holds
all the information available about the reply and can be accessed
with the functions described in this chapter. Note that the internal
structure of REPLY objects should be considered opaque and may change
in future releases of Hunchentoot.
In all of the functions below, the default value
for the optional argument reply is the value of *REPLY*,
i.e. handlers will usually not need to provide this argument when
calling the function.
While Hunchentoot's preferred way of sending data to the client is the
one described above (i.e. the handler returns the whole payload as a
string or an array of octets) you can, if you really need to (for
example for large content bodies), get a stream you can write to
directly. This is achieved by first setting
up *REPLY* and then
calling SEND-HEADERS. Note
that in this case the usual error handling is
disabled. See the file test/test.lisp for an example.
[Special variable]
*reply*
Holds the currentREPLYobject.
[Accessor]
header-out name &optional reply => string
(setf (header-out name &optional reply) new-value)
HEADER-OUTreturns the outgoing http header named by the keywordnameif there is one, otherwiseNIL.SETFofHEADER-OUTchanges the current value of the header namedname. If no header namednameexists it is created. For backwards compatibility,namecan also be a string in which case the association between a header and its name is case-insensitive.Note that the headers
Set-Cookie,Content-Length, andContent-Typecannot be queried byHEADER-OUTand must not be set bySETFofHEADER-OUT.See also
HEADERS-OUT,CONTENT-TYPE,CONTENT-LENGTH,COOKIES-OUT, andCOOKIE-OUT.
[Function]
headers-out &optional request => alist
Returns an alist of all outgoing http parameters (except forSet-Cookie,Content-Length, andContent-Type). The car of each element of this list is the headers's name while the cdr is its value. This alist should not be manipulated directly, useSETFofHEADER-OUTinstead.
[Function]
cookie-out name &optional reply => cookie
Returns the outgoing cookie named by the stringname(orNILif there ain't no cookie with this name). See alsoCOOKIES-OUTand the section about cookies.
[Function]
cookies-out &optional reply => alist
Returns an alist of all outgoing cookies. The car of each element of this list is the cookie's name while the cdr is the cookie itself. See alsoCOOKIE-OUTand the section about cookies.
[Accessor]
return-code &optional reply => number
(setf (return-code &optional reply) new-value)
RETURN-CODEreturns the http return code of the reply,SETFofRETURN-CODEchanges it. The return code of eachREPLYobject is initially set to+HTTP-OK+.
[Accessor]
content-type &optional reply => string
(setf (content-type &optional reply) new-value)
CONTENT-TYPEreturns the outgoingContent-Typehttp header.SETFofCONTENT-TYPEchanges the current value of this header. The content type of eachREPLYobject is initially set to the value of*DEFAULT-CONTENT-TYPE*.
[Accessor]
content-length &optional reply => length
(setf (content-length &optional reply) new-value)
CONTENT-LENGTHreturns the outgoingContent-Lengthhttp header.SETFofCONTENT-LENGTHchanges the current value of this header. The content length of eachREPLYobject is initially set toNIL. If you leave it like that, Hunchentoot will automatically try to compute the correct value usingLENGTH. If you set the value yourself, you must make sure that it's the correct length of the body in octets (not in characters). In this case, Hunchentoot will use the value as is which can lead to erroneous behaviour if it is wrong - so, use at your own risk.Note that setting this value explicitely doesn't mix well with URL rewriting.
[Function]
send-headers => stream
Sends the initial status line and all headers as determined by theREPLYobject*REPLY*. Returns a flexi stream to which the body of the reply can be written. Once this function has been called, further changes to*REPLY*don't have any effect. Also, automatic handling of errors (i.e. sending the corresponding status code to the browser, etc.) is turned off for this request. Likewise, functions likeREDIRECTor throwing toHANDLER-DONEwon't have the desired effect once the headers are sent.If your handlers return the full body as a string or as an array of octets, you should not call this function. If a handler calls
SEND-HEADERS, its return value is ignored.See also
REPLY-EXTERNAL-FORMAT.
Gets and sets the external format of theREPLYobjectreply. This external format is used when character content is written to the client after the headers have been sent. In particular, it is the external format of the stream returned bySEND-HEADERS(but of course you can change it because it's a flexi stream).The initial value for each request is the value of
*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*.
[Constants]
+http-continue+
+http-switching-protocols+
+http-ok+
+http-created+
+http-accepted+
+http-non-authoritative-information+
+http-no-content+
+http-reset-content+
+http-partial-content+
+http-multiple-choices+
+http-moved-permanently+
+http-moved-temporarily+
+http-see-other+
+http-not-modified+
+http-use-proxy+
+http-temporary-redirect+
+http-bad-request+
+http-authorization-required+
+http-payment-required+
+http-forbidden+
+http-not-found+
+http-method-not-allowed+
+http-not-acceptable+
+http-proxy-authentication-required+
+http-request-time-out+
+http-conflict+
+http-gone+
+http-length-required+
+http-precondition-failed+
+http-request-entity-too-large+
+http-request-uri-too-large+
+http-unsupported-media-type+
+http-requested-range-not-satisfiable+
+http-expectation-failed+
+http-internal-server-error+
+http-not-implemented+
+http-bad-gateway+
+http-service-unavailable+
+http-gateway-time-out+
+http-version-not-supported+
The values of these constants are 100, 101, 200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 307, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 500, 501, 502, 503, 504, and 505. SeeRETURN-CODE.
[Special variable]
*default-content-type*
The value of this variable is used to initialize the content type of eachREPLYobject. Its initial value is"text/html; charset=iso-8859-1". SeeCONTENT-TYPE.
REPLY object (see COOKIE-OUT and COOKIES-OUT). They are CLOS objects defined like this:
(defclass cookie ()
((name :initarg :name
:reader cookie-name
:type string
:documentation "The name of the cookie - a string.")
(value :initarg :value
:accessor cookie-value
:initform ""
:documentation "The value of the cookie. Will be URL-encoded when sent to the browser.")
(expires :initarg :expires
:initform nil
:accessor cookie-expires
:documentation "The time (a universal time) when the cookie expires (or NIL).")
(path :initarg :path
:initform nil
:accessor cookie-path
:documentation "The path this cookie is valid for (or NIL).")
(domain :initarg :domain
:initform nil
:accessor cookie-domain
:documentation "The domain this cookie is valid for (or NIL).")
(secure :initarg :secure
:initform nil
:accessor cookie-secure
:documentation "A generalized boolean denoting whether this cookie is a secure cookie.")))
The reader COOKIE-NAME and
the accessors
COOKIE-VALUE, COOKIE-EXPIRES, COOKIE-PATH, COOKIE-DOMAIN, and COOKIE-SECURE are all exported
from the HUNCHENTOOT package.
[Function]
set-cookie name &key value expires path domain secure reply => cookie
Creates aCOOKIEobject from the parameters provided to this function and adds it to the outgoing cookies of theREPLYobjectreply. If a cookie with the same name (case-sensitive) already exists, it is replaced. The default forreplyis*REPLY*. The default forvalueis the empty string.
[Function]
set-cookie* cookie &optional reply => cookie
Adds theCOOKIEobjectcookieto the outgoing cookies of theREPLYobjectreply. If a cookie with the same name (case-sensitive) already exists, it is replaced. The default forreplyis*REPLY*.
START-SESSION, Hunchentoot uses either
cookies or (if the client doesn't send the cookies back) rewrites URLs
to keep track of this client, i.e. to provide a kind of 'state' for
the stateless http protocol. The session associated with the client is
an opaque CLOS object which can be used to store arbitrary data
between requests.
Hunchentoot makes some reasonable effort to prevent eavesdroppers from hijacking sessions (see below), but this should not be considered really secure. Don't store sensitive data in sessions and rely solely on the session mechanism as a safeguard against malicious users who want to get at this data!
For each request there's one SESSION object which is accessible
to the handler via the
special variable *SESSION*. This object holds
all the information available about the session and can be accessed
with the functions described in this chapter. Note that the internal
structure of SESSION objects should be considered opaque and may change
in future releases of Hunchentoot.
Sessions are automatically verified for validity and age when
the REQUEST object is instantiated, i.e. if *SESSION* is not NIL then this
session is valid (as far as Hunchentoot is concerned) and not too old. Old sessions are automatically removed.
[Special variable]
*session*
Holds the currentSESSIONobject (if any) orNIL.
[Function]
start-session => session
Returns*SESSION*if it isn'tNIL, otherwise creates a newSESSIONobject and returns it.
This accessor can be used to associate arbitrary data with the the symbolsymbolin theSESSIONobjectsession.present-pis true if such data was found, otherwiseNIL. The default value forsessionis*SESSION*.If
SETFofSESSION-VALUEis called withsessionbeingNILthen a session is automatically instantiated withSTART-SESSION.
[Function]
delete-session-value symbol &optional session => |
Completely removes any data associated with the symbolsymbolfrom theSESSIONobjectsession. Note that this is different from usingSESSION-VALUEto set the data toNIL. The default value forsessionis*SESSION*.
[Function]
reset-sessions => |
This function unconditionally invalidates and destroys all sessions immediately.
[Function]
session-string session => string
Returns a unique string that's associated with theSESSIONobjectsession. This string is sent to the browser as a cookie value or as a GET parameter,
[Function]
session-counter session => count
Returns the number of times (requests) theSESSIONobjectsessionhas been used.
[Accessor]
session-max-time session => seconds
(setf (session-max-time session) seconds)
This gets or sets the maximum time (in seconds) theSESSIONobjectsessionshould be valid before it's invalidated: If a request associated with this session comes in and the last request for the same session was more thansecondsseconds ago than the session is deleted and a new one is started for this client. The default value is determined by*SESSION-MAX-TIME*.
[Function]
session-remote-addr session => address
Returns the 'real' remote address (seeREAL-REMOTE-ADDR) of the client for which theSESSIONobjectsessionwas initiated.
[Function]
session-user-agent session => address
Returns the 'User-Agent' http header (seeUSER-AGENT) of the client for which theSESSIONobjectsessionwas initiated.
[Special variable]
*use-remote-addr-for-sessions*
If this value is true (the default isNIL) then the 'real' remote address (seeREAL-REMOTE-ADDR) of the client will be encoded into the session identifier, i.e. if this value changes on the client side, the session will automatically be invalidated.Note that this is not secure, because it's obviously not very hard to fake an
X_FORWARDED_FORheader. On the other hand, relying on the remote address (seeREMOTE-ADDR) of the client isn't an ideal solution either, because some of your users may connect through http proxies and the proxy they use may change during the session. But then again, some proxies don't sendX_FORWARDED_FORheaders anyway. Sigh...
[Special variable]
*use-user-agent-for-sessions*
If this value is true (which is the default) then the 'User-Agent' http header (seeUSER-AGENT) of the client will be encoded into the session identifier, i.e. if this value changes on the client side the session will automatically be invalidated.While this is intended to make the life of malicious users harder, it might affect legitimate users as well: I've seen this http header change with certain browsers when the Java plug-in was used.
[Special variable]
*rewrite-for-session-urls*
If this value is true (which is the default) then content bodies sent by Hunchentoot will be rewritten (using URL-REWRITE) such that GET parameters for session handling are appended to all relevant URLs. This only happens, though, if the body's content type (seeCONTENT-TYPE) starts with one of the strings in*CONTENT-TYPES-FOR-URL-REWRITE*and unless the client has already sent a cookie named*SESSION-COOKIE-NAME*.Note that the function which rewrites the body doesn't understand Javascript, so you have to take care of URLs in Javascript code yourself.
[Special variable]
*content-types-for-url-rewrite*
This is a list of strings (the initial value is("text/html" "application/xhtml+xml")) the content-type of an outgoing body is compared with if*REWRITE-FOR-SESSION-URLS*is true. If the content-type starts with one of these strings, then url-rewriting will happen, otherwise it won't.
[Special variable]
*session-cookie-name*
This is the name that is used for the session-related cookie or GET parameter sent to the client. Its default value is"hunchentoot-session". Note that changing this name while Hunchentoot is running will invalidate existing sessions.
[Special variable]
*session-removal-hook*
The value of this variable should be a function of one argument, aSESSIONobject. This function is called directly before the session is destroyed, either byRESET-SESSIONSor when it's invalidated because it's too old.
[Special variable]
*session-max-time*
The default time (in seconds) after which a session times out - seeSESSION-MAX-TIME. This value is initially set to 1800.
[Macro]
do-sessions (var &optional result-form) statement* => result
Executes the statements withvarbound to each existingSESSIONobject consecutively. An implicit block namedNILsurrounds the body of this macro. Returns the values returned byresult-formunlessRETURNis executed. The scope of the binding ofvardoes not includeresult-form.
[Special variable]
*session-gc-frequency*
A session garbage collection (seeSESSION-GC) will happen every*SESSION-GC-FREQUENCY*requests (counting only requests which use sessions) if the value of this variable is notNIL. It's default value is 50.
[Function]
session-gc => |
Deletes sessions which are too old - seeSESSION-TOO-OLD-P. Usually, you don't call this function directly - see*SESSION-GC-FREQUENCY*.
[Function]
session-too-old-p session => generalized-boolean
Returns a true value if theSESSIONobjectsessionis too old and would be deleted during the next session GC. You don't have to check this manually for sessions in*SESSION*, but it might be useful if you want to loop through all sessions.
Furthermore, all errors happening within a handler which are not caught by the handler itself are handled by Hunchentoot - see details below.
[Accessor]
log-file => pathname
(setf (log-file) pathspec)
The functionLOG-FILEreturns a pathname designating the log file which is currently used (unless log messages are forwarded to Apache). This destination for log messages can be changed with(SETF LOG-FILE). The initial location of the log file is implementation-dependent.
[Generic function]
log-message log-level format &rest args => |
Schedules a message for the Apache log file or writes it directly to the current log file depending on the value of theuse-apache-log-pargument toSTART-SERVER.log-levelshould be one of the keywords:EMERG,:ALERT,:CRIT,:ERROR,:WARNING,:NOTICE,:INFO, or:DEBUGwhich correspond to the various Apache log levels.log-levelcan also beNILin which case mod_lisp's default log level is used. If Apache isn't used, the log level is just written to the log file unless it'sNIL.formatandargsare used as withFORMAT.
LOG-MESSAGEis a generic function, so you can specialize it or bypass it completely with an around method.
[Function]
log-message* format &rest args => |
LikeLOG-MESSAGEbut withlog-levelset to*DEFAULT-LOG-LEVEL*.
[Special variable]
*default-log-level*
The log level used byLOG-MESSAGE*. The initial value isNIL.
[Special variable]
*log-lisp-errors-p*
Whether unhandled errors in handlers should be logged. See also*LISP-ERRORS-LOG-LEVEL*. The default value isT.
[Special variable]
*lisp-errors-log-level*
The log level used to log Lisp errors. See also*LOG-LISP-ERRORS-P*. The default value is:ERROR.
[Special variable]
*log-lisp-warnings-p*
Whether unhandled warnings in handlers should be logged. See also*LISP-WARNINGS-LOG-LEVEL*. The default value isT.
[Special variable]
*lisp-warnings-log-level*
The log level used to log Lisp warnings. See also*LOG-LISP-WARNINGS-P*. The default value is:WARNING.
[Special variable]
*log-lisp-backtraces-p*
Whether backtraces should also be logged in addition to error messages and warnings. This value will only have effect if*LOG-LISP-ERRORS-P*or*LOG-LISP-WARNINGS-P*is true. The default value isNIL.
[Special variable]
*log-prefix*
All messages written to the Apache error log by Hunchentoot are prepended by a string which is the value of this variable enclosed in square brackets. If the value isNIL, however, no such prefix will be written. If the value isT(which is the default), the prefix will be"[Hunchentoot]".
[Special variable]
*show-lisp-errors-p*
Whether unhandled Lisp errors should be shown to the user. If this value isNIL(which is the default), only the message An error has occurred will be shown.
[Special variable]
*show-lisp-backtraces-p*
Whether backtraces should also be shown to the user. This value will only have effect if*SHOW-LISP-ERRORS-P*is true. The default value isNIL.
[Special variable]
*show-access-log-messages*
If this variable is true and if the value of theuse-apache-log-pargument toSTART-SERVERwasNIL, then for each request a line somewhat similar to what can be found in Apache's access log will be written to the log file. The default value of this variable isT.
[Special variable]
*http-error-handler*
This variable holdsNIL(the default) or a function designator for a function of one argument. The function gets called if the responsible handler has set a return code other than+HTTP-OK+or+HTTP-NOT-MODIFIED+and*HANDLE-HTTP-ERRORS-P*is true. It receives the return code as its argument and can return the contents of an error page orNILif it refuses to handle the error, i.e. if Hunchentoot's default error page should be shown. (Note that the function can access the request and reply data.)
[Special variable]
*handle-http-errors-p*
This variable holds a generalized boolean that determines whether return codes other than+HTTP-OK+or+HTTP-NOT-MODIFIED+are treated specially. When its value is true (the default), either a default body for the return code or the result of calling*HTTP-ERROR-HANDLER*is used. When the value isNIL, no special action is taken and you are expected to supply your own response body to describe the error.
[Function]
get-backtrace condition => backtrace
This is the function that is used internally by Hunchentoot to show or log backtraces. It accepts a condition objectconditionand returns a string with the corresponding backtrace.
One important thing you should try if you're behind mod_lisp is to use an external log file (as opposed to Apache's log) because it can reveal error messages that might otherwise get lost if something's broken in the communication between Hunchentoot and mod_lisp.
Good luck... :)
[Special variable]
*catch-errors-p*
If the value of this variable isNIL(the default isT), then errors which happen while a request is handled aren't caught as usual, but instead your Lisp's debugger is invoked. This variable should obviously always be set to a true value in a production environment.
[Symbol]
handler-done
This is a catch tag which names a catch which is active during the lifetime of a handler. The handler can at any time throw the outgoing content body (orNIL) to this catch to immediately abort handling the request. See the source code ofREDIRECTfor an example.
[Function]
no-cache => |
This function will set appropriate outgoing headers to completely prevent caching on virtually all browsers.
[Function]
handle-if-modified-since time => |
This function is designed to be used inside a handler. If the client has sent an 'If-Modified-Since' header (see RFC 2616, section 14.25) and the time specified matches the universal timetimethen the header+HTTP-NOT-MODIFIED+with no content is immediately returned to the client.Note that for this function to be useful you should usually send 'Last-Modified' headers back to the client. See the code of
CREATE-STATIC-FILE-DISPATCHER-AND-HANDLERfor an example.
[Function]
rfc-1123-date &optional time => string
This function accepts a universal timetime(default is the current time) and returns a string which encodes this time according to RFC 1123. This can be used to send a 'Last-Modified' header - seeHANDLE-IF-MODIFIED-SINCE.
[Function]
redirect target &key host protocol add-session-id permanently => |
Sends back appropriate headers to redirect the client totarget(a string). Iftargetis a full URL starting with a scheme,hostandprotocolare ignored. Otherwise,targetshould denote the path part of a URL,protocolmust be one of the keywords:HTTPor:HTTPS, and the URL to redirect to will be constructed fromhost,protocol, andtarget.If
permanentlyis true (the default isNIL), a 301 status code will be sent, otherwise a 302 status code. Ifhostis not provided, the current host (seeHOST) will be used. Ifprotocolis the keyword:HTTPS, the client will be redirected to a https URL, if it's:HTTPit'll be sent to a http URL. If bothhostandprotocolaren't provided, then the value ofprotocolwill match the current request.
[Function]
require-authorization &optional realm => |
Sends back appropriate headers to require basic HTTP authentication (see RFC 2617) for the realmrealm. The default value forrealmis"Hunchentoot".
[Function]
escape-for-html string => escaped-string
Escapes all occurrences of the characters#\<,#\>,#\',#", and#\&withinstringfor HTML output.
[Function]
url-encode string &optional external-format => url-encoded-string
URL-encodes a string using the external formatexternal-format. The default forexternal-formatis the value of*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*.
[Function]
url-decode string &optional external-format => url-encoded-string
URL-decodes a string using the external formatexternal-format, i.e. this is the inverse ofURL-ENCODE. It is assumed that you'll rarely need this function, if ever. But just in case - here it is. The default forexternal-formatis the value of*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*.
[Function]
http-token-p object => generalized-boolean
This function tests whetherobjectis a non-empty string which is a token according to RFC 2068 (i.e. whether it may be used for, say, cookie names).
[Special variable]
*tmp-directory*
This should be a pathname denoting a directory where temporary files can be stored. It is used for file uploads.
[Special variable]
*hunchentoot-default-external-format*
The (flexi stream) external format used when computing theREQUESTobject. The default value is the result of evaluating(flex:make-external-format :latin1 :eol-style :lf)
It turned out that Jeff Caldwell had worked on something similar so he emailed me and proposed to join our efforts. As I had no immediate plans to release my code (which was poorly organized, undocumented, and mostly CMUCL-specific), I gave it to Jeff and he worked towards a release. He added docstrings, refactored, added some stuff, and based it on KMRCL to make it portable across several Lisp implementations.
Unfortunately, Jeff is at least as busy as I am so he didn't find the time to finish a full release. But in spring 2004 I needed a documented version of the code for a client of mine who thought it would be good if the toolkit were publicly available under an open source license. So I took Jeff's code, refactored again (to sync with the changes I had done in the meantime), and added documentation. This resulted in TBNL 0.1.0 (which initially required mod_lisp as its front-end). Jeff's code (which includes a lot more stuff that I didn't use) is still available from his own website tbnl.org.
In March 2005, Bob Hutchinson sent patches which enabled TBNL to use other front-ends than mod_lisp. This made me aware that TBNL was already almost a full web server, so eventually I wrote Hunchentoot which was a full web server, implemented as a wrapper around TBNL. Hunchentoot 0.1.0 was released at the end of 2005 and was originally LispWorks-only.
Hunchentoot 0.4.0, released in October 2006, was the first release
which also worked with other Common Lisp implementations. It is a
major rewrite and also incorporates most of TBNL and replaces
it completely.
HUNCHENTOOT package
in alphabetical order linked to their corresponding entries:
*catch-errors-p*
*cleanup-function*
*cleanup-interval*
*content-types-for-url-rewrite*
*default-content-type*
*default-handler*
*default-log-level*
*default-read-timeout*
*default-write-timeout*
*dispatch-table*
*file-upload-hook*
*handle-http-errors-p*
*http-error-handler*
*hunchentoot-default-external-format*
*lisp-errors-log-level*
*lisp-warnings-log-level*
*log-lisp-backtraces-p*
*log-lisp-errors-p*
*log-lisp-warnings-p*
*log-prefix*
*meta-dispatcher*
*reply*
*request*
*rewrite-for-session-urls*
*server*
*session*
*session-cookie-name*
*session-gc-frequency*
*session-max-time*
*session-removal-hook*
*show-access-log-messages*
*show-lisp-backtraces-p*
*show-lisp-errors-p*
*tmp-directory*
*use-remote-addr-for-sessions*
*use-user-agent-for-sessions*
+http-accepted+
+http-authorization-required+
+http-bad-gateway+
+http-bad-request+
+http-conflict+
+http-continue+
+http-created+
+http-expectation-failed+
+http-forbidden+
+http-gateway-time-out+
+http-gone+
+http-internal-server-error+
+http-length-required+
+http-method-not-allowed+
+http-moved-permanently+
+http-moved-temporarily+
+http-multiple-choices+
+http-no-content+
+http-non-authoritative-information+
+http-not-acceptable+
+http-not-found+
+http-not-implemented+
+http-not-modified+
+http-ok+
+http-partial-content+
+http-payment-required+
+http-precondition-failed+
+http-proxy-authentication-required+
+http-request-entity-too-large+
+http-request-time-out+
+http-request-uri-too-large+
+http-requested-range-not-satisfiable+
+http-reset-content+
+http-see-other+
+http-service-unavailable+
+http-switching-protocols+
+http-temporary-redirect+
+http-unsupported-media-type+
+http-use-proxy+
+http-version-not-supported+
authorization
aux-request-value
content-length
content-type
cookie-domain
cookie-expires
cookie-in
cookie-name
cookie-out
cookie-path
cookie-secure
cookie-value
cookies-in
cookies-out
create-folder-dispatcher-and-handler
create-prefix-dispatcher
create-regex-dispatcher
create-static-file-dispatcher-and-handler
default-dispatcher
define-easy-handler
delete-aux-request-value
delete-session-value
dispatch-easy-handlers
dispatch-request
do-sessions
escape-for-html
get-backtrace
get-parameter
get-parameters
handle-if-modified-since
handle-static-file
header-in
header-out
headers-in
headers-out
host
http-token-p
log-file
log-message
log-message*
mod-lisp-id
no-cache
parameter
post-parameter
post-parameters
query-string
raw-post-data
real-remote-addr
recompute-request-parameters
redirect
referer
remote-addr
remote-port
reply-external-format
request-method
request-uri
require-authorization
reset-sessions
return-code
rfc-1123-date
script-name
send-headers
server-addr
server-address
server-local-port
server-port
server-protocol
session-counter
session-gc
session-max-time
session-too-old-p
session-remote-addr
session-string
session-user-agent
session-value
set-cookie
set-cookie*
ssl-session-id
start-server
start-session
stop-server
url-decode
url-encode
user-agent
Hunchentoot originally used code from ACL-COMPAT, specifically the chunking code from Jochen Schmidt. (This has been replaced by Chunga.) When I ported Hunchentoot to other Lisps than LispWorks, I stole code from ACL-COMPAT, KMRCL, and trivial-sockets for implementation-dependent stuff like sockets and MP.
Parts of this documentation were prepared with DOCUMENTATION-TEMPLATE, no animals were harmed.
$Header: /usr/local/cvsrep/hunchentoot/doc/index.html,v 1.53 2007/01/24 00:34:37 edi Exp $