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), Clozure CL, and Allegro Common Lisp. (Note: You can use Hunchentoot with CLISP or with versions of CMUCL or SBCL without threads, for example on Windows, but this is not recommended except for development purposes.) Porting to other CL implementations shouldn't be too hard, see the files
port-xxx.lisp
andunix-xxx.lisp
which 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 for example used by Trip Planner, clutu, TwitterBuzz, Jalat, Heike Stephan, xOs, and the NIST.
Download shortcut: http://weitz.de/files/hunchentoot.tar.gz.
:HUNCHENTOOT-NO-SSL
to
*FEATURES*
before you compile it.
Hunchentoot itself together with this documentation can be downloaded from http://weitz.de/files/hunchentoot.tar.gz. The current version is 0.15.6.
The preferred method to compile and load Hunchentoot is via ASDF. If you think that it's too much work to find and download all the libraries mentioned above, try something like ASDF-INSTALL, clbuild, or my own Starter Pack. There's also a port for Gentoo Linux thanks to Matthew Kennedy.
A Mercurial repository of older versions is available at http://arcanes.fr.eu.org/~pierre/2007/02/weitz/ thanks to Pierre Thierry.
Luís Oliveira maintains a darcs repository of Hunchentoot at http://common-lisp.net/~loliveira/ediware/.
ProxyPass /hunchentoot http://127.0.0.1:3000/hunchentoot ProxyPassReverse /hunchentoot http://127.0.0.1:3000/hunchentootThis will tunnel all requests where the URI path begins with
"/hunchentoot"
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.
If you want to send patches, please read this first.
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.
Here are some tutorials done by others:
Here is some software which extends Hunchentoot or is based on it:
Starts a Hunchentoot server instance and returns it.port
ist the port the server will be listening on - the default is 80 (or 443 if SSL information is provided). Ifaddress
is 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"
. Ifaddress
isNIL
, then the server will receive connections to all IP addresses on the machine. This is the default.
dispatch-table
can either be a dispatch table which is to be used by this server orNIL
which means that at request time*META-DISPATCHER*
will be called to retrieve a dispatch table.
name
should be a symbol which can be used to name the server. This name can be utilized when defining easy handlers. The default name is an uninterned symbol as returned byGENSYM
.If
mod-lisp-p
is 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-p
is true (which is the default), log messages will be written to the Apache log file - this parameter has no effect ifmod-lisp-p
is NIL.If
input-chunking-p
is true (which is the default), the server will accept request bodies without aContent-Length
header 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-timeout
is the read timeout (in seconds) for the socket stream used by the server - the default value is*DEFAULT-READ-TIMEOUT*
.write-timeout
is 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, Clozure CL, and AllegroCL. You can useNIL
in both cases to denote that you don't want a timeout. Ifmod-lisp-p
is true, the timeouts are always set toNIL
.On Unix you can use
setuid
andsetgid
to 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.)setuid
andsetgid
can 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-file
for the certificate file and optionallyssl-privatekey-file
for the private key file, both files must be in PEM format. If you only provide the value forssl-certificate-file
it 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-password
keyword 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
.server
must 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 theport
andaddress
parameters ofSTART-SERVER
.
[Accessor]
server-dispatch-table
server => dispatch-table
(setf (
server-dispatch-table
server) new-value
)
These methods can be used to get and set the dispatch table of a Hunchentoot server object. The value corresponds to thedispatch-table
parameter ofSTART-SERVER
and can be changed at runtime. It can be set to NIL which means that the server doesn't have its own dispatch table and*META-DISPATCHER*
should be called instead.
[Accessor]
server-name
server => name
(setf (
server-name
server) new-value
)
These methods can be used to get and set the name of a server which must be a symbol.
[Special variable]
*default-read-timeout*
The default value for theread-timeout
keyword argument toSTART-SERVER
. The initial value is 20 (seconds).
[Special variable]
*default-write-timeout*
The default value for thewrite-timeout
keyword argument toSTART-SERVER
. The initial value is 20 (seconds).
[Special variable]
*cleanup-interval*
Should beNIL
or 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-SWEEP
2)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
NIL
and tune LispWork's GC yourself, using for exampleCOLLECT-GENERATION-2
.
test/test.lisp
for examples.)
[Special variable]
*dispatch-table*
The return value of the initial value of*META-DISPATCHER*
.This is a list of function designators for dispatch functions each of which should be a function of one argument which accepts a
REQUEST
object and, depending on this object, should either return a handler to handle the request orNIL
which 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 a 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 initial value of
*DISPATCH-TABLE*
is a list which just contains the symbolDEFAULT-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 (unless the server has its own dispatch table) 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 returnshandler
whenever 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 returnshandler
whenever 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 denoted by the pathname designatorpath
with content typecontent-type
to the client. Sets the necessary handlers. In particular the function employsHANDLE-IF-MODIFIED-SINCE
.If
content-type
isNIL
the 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-HEADERS
internally, 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 designatorpath
with content typecontent-type
if theSCRIPT-NAME
of the request matches the stringuri
. UsesHANDLE-STATIC-FILE
internally.If
content-type
isNIL
the 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-path
that is denoted by the URI of the request relative touri-prefix
.uri-prefix
must be a string ending with a slash,base-path
must be a pathname designator for an existing directory. UsesHANDLE-STATIC-FILE
internally.If
content-type
is 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 byDEFUN
and optionally registers it with a URI so that it will be found byDISPATCH-EASY-HANDLERS
.
description
is either a symbolname
or a list matching the destructuring lambda list(name &key uri server-names default-parameter-type default-request-type).lambda-list
is a list the elements of which are either a symbolvar
or 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 namename
and keyword parameters named by thevar
symbols. Eachvar
will be bound to the value of the GET or POST parameter calledreal-name
(a string) before the body of the function is executed. Ifreal-name
is not provided, it will be computed by downcasing the symbol name ofvar
.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
, ifuri
is a string and the script name of the current request isuri
, or ifuri
designates a function and applying this function to the currentREQUEST
object returns a true value.
server-names
(which is evaluated) can be a list of symbols which means that the handler will only be returned byDISPATCH-EASY-HANDLERS
in servers which have one of these names (seeSERVER-NAME
).server-names
can also be the symbolT
which means that the handler will be returned byDISPATCH-EASY-HANDLERS
in every server.Whether the GET or POST parameter (or both) will be taken into consideration, depends on
request-type
which 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
var
will 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-type
is'STRING
,var
's value remains as is. Ifparameter-type
is'INTEGER
and the parameter string consists solely of decimal digits,var
's value will be the corresponding integer, otherwiseNIL
. Ifparameter-type
is'KEYWORD
,var
's value will be the keyword obtained by interning the upcased parameter string into the keyword package. Ifparameter-type
is'CHARACTER
and the parameter string is of length one,var
's value will be the single character of this string, otherwiseNIL
. Ifparameter-type
is'BOOLEAN
,var
's value will always beT
(unless it isNIL
by the first rule above, of course). Ifparameter-type
is 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-type
can 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-name
will 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)wheren
is a non-negative integer, will be assembled into an array where then
th 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 thatVAR
will 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-TABLE
parameter type is(hash-table inner-type key-type test-function),butkey-type
andtest-function
can be left out in which case they default to'STRING
and'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)(wherekey
is a string that doesn't contain curly brackets) will become the values (after conversion toinner-type
) of a hash table with test functiontest-function
wherekey
(after conversion tokey-type
) will be the corresponding key. Note thatvar
will 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-type
is 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
var
would be bound toNIL
, theninit-form
(if provided) will be evaluated instead, andvar
will 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,var
will always be bound to the result of evaluatinginit-form
unless 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-HANDLER
makes 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 currentREQUEST
object.
[Function]
host
&optional request => string
Returns the value of the incomingHost
http header. (This corresponds to the environment variableHTTP_HOST
in 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_METHOD
in 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=bar
into his browser, this function will return"/xxx/frob.html?foo=bar"
. (This corresponds to the environment variableREQUEST_URI
in 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-URI
in front of the first question mark (if any). (This corresponds to the environment variableSCRIPT_NAME
in 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-URI
behind the first question mark (if any). (This corresponds to the environment variableQUERY_STRING
in CGI scripts.) See alsoGET-PARAMETER
andGET-PARAMETERS
.
[Function]
get-parameter
name &optional request => string
Returns the value of the GET parameter (as provided via the request URI) named by the stringname
as a string (orNIL
if 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 (orNIL
if there ain't no POST parameter with this name). If, however, the browser sent a file through amultipart/form-data
form, the value of this function is a three-element list(path file-name content-type)wherepath
is 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 bypath
will be deleted after the request has been handled - you have to move or copy it somewhere else if you want to keep it.POST parameters will only be computed if the content type of the request body was
multipart/form-data
orapplication/x-www-form-urlencoded
. Although this function is calledPOST-PARAMETER
, you can instruct Hunchentoot to compute these parameters for other request methods by setting*METHODS-FOR-POST-PARAMETERS*
.See also
POST-PARAMETERS
and*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 also
POST-PARAMETER
.
[Special variable]
*methods-for-post-parameters*
A list of the request method types (as keywords) for which Hunchentoot will try to compute "POST" parameters. The default is the list with the single element:POST
.
[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 currentREQUEST
object, but obviously you can't access the post parameters yet.
[Function]
raw-post-data
&key
request external-format force-text force-binary want-stream => 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-data
in which caseNIL
is returned). By default, the result is a string if the type of theContent-Type
media 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 thecharset
parameter 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-text
which will force Hunchentoot to act as if the type of the media type had been"text"
(withexternal-format
taking precedence if provided). Or you can provide a true value forforce-binary
which means that you want a vector of octets at any rate. (If bothforce-text
andforce-binary
are 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. The stream will have its octet position set to0
. If the client provided aContent-Length
header, the stream will also have a corresponding bound, so no matter whether the client used chunked encoding or not, you can always read until EOF.If the content type of the request was
multipart/form-data
orapplication/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-DATA
more 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 stringname
as a string (orNIL
if there ain't no parameter with this name). If both a GET and a POST parameter with the namename
exist, the GET parameter will be returned. See alsoGET-PARAMETER
andPOST-PARAMETER
.
[Function]
header-in
name &optional request => string
Returns the incoming header named by the keywordname
as a string (orNIL
if 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,
name
can 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-IN
and the remark about incoming headers there.
[Function]
authorization
&optional request => user, password
Returns as two values the user and password (if any) from the incomingAuthorization
http header. ReturnsNIL
if 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_ADDR
in 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-For
http 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-ADDR
as 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_ADDR
in 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.0
or:HTTP/1.1
. (This corresponds to the environment variableSERVER_PROTOCOL
in 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. ReturnsNIL
in 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_ID
in CGI scripts.) ReturnsNIL
in stand-alone servers.
[Function]
user-agent
&optional request => string
Returns the value of the incomingUser-Agent
http header. (This corresponds to the environment variableHTTP_USER_AGENT
in CGI scripts.)
[Function]
referer
&optional request => string
Returns the value of the incomingReferer
(sic!) http header. (This corresponds to the environment variableHTTP_REFERER
in CGI scripts.)
[Function]
cookie-in
name &optional request => string
Returns the value of the incoming cookie named by the stringname
(orNIL
if 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 symbolsymbol
in theREQUEST
objectrequest
.present-p
is true if such data was found, otherwiseNIL
.
[Function]
delete-aux-request-value
symbol &optional request => |
Completely removes any data associated with the symbolsymbol
from theREQUEST
objectrequest
. Note that this is different from usingAUX-REQUEST-VALUE
to set the data toNIL
.
[Function]
recompute-request-parameters
&key request external-format => |
Recomputes the GET and POST parameters for theREQUEST
objectrequest
. 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-urlencoded
content type.The default value for
external-format
is*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*
. Seetest/test.lisp
for 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 currentREPLY
object.
[Accessor]
header-out
name &optional reply => string
(setf (
header-out
name &optional reply) new-value
)
HEADER-OUT
returns the outgoing http header named by the keywordname
if there is one, otherwiseNIL
.SETF
ofHEADER-OUT
changes the current value of the header namedname
. If no header namedname
exists it is created. For backwards compatibility,name
can 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-Type
cannot be queried byHEADER-OUT
and must not be set bySETF
ofHEADER-OUT
. Also, there are a couple of "technical" headers likeConnection
orTransfer-Encoding
that you're not supposed to set yourself. If in doubt, consult the source code or ask on the mailing list.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, useSETF
ofHEADER-OUT
instead.
[Function]
cookie-out
name &optional reply => cookie
Returns the outgoing cookie named by the stringname
(orNIL
if there ain't no cookie with this name). See alsoCOOKIES-OUT
and 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-OUT
and the section about cookies.
[Accessor]
return-code
&optional reply => number
(setf (
return-code
&optional reply) new-value
)
RETURN-CODE
returns the http return code of the reply,SETF
ofRETURN-CODE
changes it. The return code of eachREPLY
object is initially set to+HTTP-OK+
.
[Accessor]
content-type
&optional reply => string
(setf (
content-type
&optional reply) new-value
)
CONTENT-TYPE
returns the outgoingContent-Type
http header.SETF
ofCONTENT-TYPE
changes the current value of this header. The content type of eachREPLY
object is initially set to the value of*DEFAULT-CONTENT-TYPE*
.
[Accessor]
content-length
&optional reply => length
(setf (
content-length
&optional reply) new-value
)
CONTENT-LENGTH
returns the outgoingContent-Length
http header.SETF
ofCONTENT-LENGTH
changes the current value of this header. The content length of eachREPLY
object 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 explicitly doesn't mix well with URL rewriting.
[Function]
send-headers => stream
Sends the initial status line and all headers as determined by theREPLY
object*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 likeREDIRECT
or throwing toHANDLER-DONE
won'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 theREPLY
objectreply
. 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-multi-status+
+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-failed-dependency+
+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, 207, 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, 424, 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 eachREPLY
object. 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 is a secure cookie.") (http-only :initarg :http-only :initform nil :accessor cookie-http-only :documentation "A generalized boolean denoting whether this is a HttpOnly cookie.")))The reader
COOKIE-NAME
and
the accessors
COOKIE-VALUE
,
COOKIE-EXPIRES
,
COOKIE-PATH
,
COOKIE-DOMAIN
,
COOKIE-SECURE
, and
COOKIE-HTTP-ONLY
are all exported
from the HUNCHENTOOT
package.
[Function]
set-cookie
name &key value expires path domain secure http-only reply => cookie
Creates aCOOKIE
object from the parameters provided to this function and adds it to the outgoing cookies of theREPLY
objectreply
. If a cookie with the same name (case-sensitive) already exists, it is replaced. The default forreply
is*REPLY*
. The default forvalue
is the empty string.
[Function]
set-cookie*
cookie &optional reply => cookie
Adds theCOOKIE
objectcookie
to the outgoing cookies of theREPLY
objectreply
. If a cookie with the same name (case-sensitive) already exists, it is replaced. The default forreply
is*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 currentSESSION
object (if any) orNIL
.
[Function]
start-session => session
Returns*SESSION*
if it isn'tNIL
, otherwise creates a newSESSION
object and returns it.
This accessor can be used to associate arbitrary data with the the symbolsymbol
in theSESSION
objectsession
.present-p
is true if such data was found, otherwiseNIL
. The default value forsession
is*SESSION*
.If
SETF
ofSESSION-VALUE
is called withsession
beingNIL
then a session is automatically instantiated withSTART-SESSION
.
[Function]
delete-session-value
symbol &optional session => |
Completely removes any data associated with the symbolsymbol
from theSESSION
objectsession
. Note that this is different from usingSESSION-VALUE
to set the data toNIL
. The default value forsession
is*SESSION*
.
[Function]
remove-session
session => |
Completely removes the sessionsession
from Hunchentoot's internal session database. See also*SESSION-REMOVAL-HOOK*
.
[Function]
reset-sessions => |
This function unconditionally invalidates and destroys all sessions immediately.
[Function]
session-cookie-value
session => string
Returns a unique string that's associated with theSESSION
objectsession
. 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) theSESSION
objectsession
has been used.
[Accessor]
session-max-time
session => seconds
(setf (
session-max-time
session) seconds
)
This gets or sets the maximum time (in seconds) theSESSION
objectsession
should 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 thanseconds
seconds 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 theSESSION
objectsession
was initiated.
[Function]
session-user-agent
session => address
Returns the 'User-Agent' http header (seeUSER-AGENT
) of the client for which theSESSION
objectsession
was 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_FOR
header. 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_FOR
headers 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 or the Google toolbar 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, aSESSION
object. This function is called directly before the session is destroyed, either byRESET-SESSIONS
, byREMOVE-SESSION
, or 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 withvar
bound to each existingSESSION
object consecutively. An implicit block namedNIL
surrounds the body of this macro. Returns the values returned byresult-form
unlessRETURN
is executed. The scope of the binding ofvar
does 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 theSESSION
objectsession
is 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-FILE
returns 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-p
argument toSTART-SERVER
.log-level
should be one of the keywords:EMERG
,:ALERT
,:CRIT
,:ERROR
,:WARNING
,:NOTICE
,:INFO
, or:DEBUG
which correspond to the various Apache log levels.log-level
can also beNIL
(in 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
.format
andargs
are used as withFORMAT
.
LOG-MESSAGE
is a generic function, so you can specialize it or bypass it completely with an around method.
[Function]
log-message*
format
&rest
args => |
LikeLOG-MESSAGE
but withlog-level
set 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-p
argument toSTART-SERVER
wasNIL
, 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 which is not in*APPROVED-RETURN-CODES*
and*HANDLE-HTTP-ERRORS-P*
is true. It receives the return code as its argument and can return the contents of an error page orNIL
if 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 not in*APPROVED-RETURN-CODES*
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.
[Special variable]
*approved-return-codes*
A list of return codes the server should not treat as an error - see*HANDLE-HTTP-ERRORS-P*
. The initial value is the list with the values of+HTTP-OK+
,+HTTP-NO-CONTENT+
,+HTTP-MULTI-STATUS+
, and+HTTP-NOT-MODIFIED+
.
[Function]
get-backtrace
condition => backtrace
This is the function that is used internally by Hunchentoot to show or log backtraces. It accepts a condition objectcondition
and 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. SeeMAYBE-INVOKE-DEBUGGER
if you want to fine-tune this behaviour.
[Generic function]
maybe-invoke-debugger
condition => |
This generic function is called whenever a conditioncondition
is signaled in Hunchentoot. You might want to specialize it on specific condition classes for debugging purposes. The default method invokes the debugger withcondition
if*CATCH-ERRORS-P*
isNIL
.
[Special variable]
*header-stream*
If this variable is notNIL
, it should be bound to a stream to which incoming and outgoing headers will be written for debugging purposes.
[Function]
ssl-p => generalized-boolean
Whether the current connection to the client is secure.
[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 ofREDIRECT
for 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 timetime
then 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-HANDLER
for 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 port protocol add-session-id code => |
Sends back appropriate headers to redirect the client totarget
(a string). Iftarget
is a full URL starting with a scheme,host
,port
, andprotocol
are ignored. Otherwise,target
should denote the path part of a URL,protocol
must be one of the keywords:HTTP
or:HTTPS
, and the URL to redirect to will be constructed fromhost
,port
,protocol
, andtarget
.If
code
is a 3xx redirection code, it will be sent as status code. In case ofNIL
, a 302 status code will be sent to the client. Ifhost
is not provided, the current host (seeHOST
) will be used. Ifprotocol
is the keyword:HTTPS
, the client will be redirected to a https URL, if it's:HTTP
it'll be sent to a http URL. If bothhost
andprotocol
aren't provided, then the value ofprotocol
will 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 forrealm
is"Hunchentoot"
.
[Function]
escape-for-html
string => escaped-string
Escapes all occurrences of the characters#\<
,#\>
,#\'
,#"
, and#\&
withinstring
for HTML output.
[Function]
url-encode
string
&optional
external-format => url-encoded-string
URL-encodes a string using the external formatexternal-format
. The default forexternal-format
is 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-format
is the value of*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*
.
[Function]
http-token-p
object => generalized-boolean
This function tests whetherobject
is 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 theREQUEST
object. The default value is the result of evaluating(flex:make-external-format :latin1 :eol-style :lf)
[Function]
mime-type
pathspec => string
Given a pathname designatorpathspec
returns the MIME type (as a string) corresponding to the suffix of the file denoted bypathspec
(orNIL
if none can be found). This is based on the table coming with the Apache distribution with some additions.
[Function]
reason-phrase
return-code => string
Returns a reason phrase for the HTTP return codereturn-code
(which should be an integer) orNIL
for return codes Hunchentoot doesn't know.
HUNCHENTOOT-MP
which exports a couple of MP-related symbols
(namely *CURRENT-PROCESS*
, MAKE-LOCK
, WITH-LOCK
, PROCESS-RUN-FUNCTION
,
and PROCESS-KILL
). These functions and macros have to be
in Hunchentoot's small portability shim anyway and even if you don't
spawn your own threads there might be occasions where you want to at
least use the lock-related functionality to write thread-safe portable
code. See the corresponding documentation strings and/or the source
code for more information.
Q: If you could go back and change anything, would Red Gorilla still be in business today?Having said that, my experience is that Hunchentoot doesn't have to hide when it comes to serving static files. If you really have performance problems with Hunchentoot, there are two things I'm aware of you should watch out for.A: Yes. I would start small and grow as the demand grew. That's what I'm doing now.
Back then we planned to be huge from the outset. So we built this monster platform on BEA, Sun and Oracle. We had huge dedicated connectivity pipes. We had two full racks clustered and fully redundant. We had E450's with RAID-5 and all 4 CPU slots filled, E250s, F5 load balancers... the cost of keeping that system on was enormous. The headcount to keep it humming was enormous too.
The truth is, we could have run the whole company on my laptop using a cable modem connection.
MP::STARTUP-IDLE-AND-TOP-LEVEL-LOOPS
.)
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:
*approved-return-codes*
*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*
*header-stream*
*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*
*methods-for-post-parameters*
*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-failed-dependency+
+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-multi-status+
+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-http-only
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
handler-done
header-in
header-out
headers-in
headers-out
host
http-token-p
log-file
log-message
log-message*
maybe-invoke-debugger
mime-type
mod-lisp-id
no-cache
parameter
post-parameter
post-parameters
query-string
raw-post-data
real-remote-addr
reason-phrase
recompute-request-parameters
redirect
referer
remote-addr
remote-port
remote-session
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-dispatch-table
server-local-port
server-name
server-port
server-protocol
session-cookie-value
session-counter
session-gc
session-max-time
session-too-old-p
session-remote-addr
session-user-agent
session-value
set-cookie
set-cookie*
ssl-p
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.141 2008/04/09 08:17:50 edi Exp $