Handlers

A handler is a CLOS object responsible for handling requests to a given portion of the URL space. Typically, you create a subclass of HANDLER, and specialise methods on one or more of the generic functions listed in handler.lisp. HANDLE-REQUEST-RESPONSE is a good place to start. A response handler should return non-NIL, otherwise Araneida will send a 404

A dispatching handler calls authentication and authorization methods, then dispatches on the sub-handler, then logs.

The HTTP methods is a specialisable argument - it's not unusual to do different things for GET and POST at the same url.

Installation

An http-listener points at a dispatching-handler (which is usually created along with the listener); typically you install your handlers as child handlers of that root. For example,

(install-handler (http-listener-handler *my-listener*)
    (make-instance 'static-file-handler :pathname #p"/tmp/fooo")  
    "http://ww.noetbook.telent.net/tmp/logo" nil)
installs a STATIC-FILE-HANDLER to answer requests starting with http://ww.noetbook.telent.net/tmp/logo

The first argument to INSTALL-HANDLER is the parent handler: if you create your handler based on DISPATCHING-HANDLER (as distinct from the plain HANDLER) it can have subhandlers installed in it this way. CLiki is a real-world example of this technique.

Handler invocation

When a request comes in, the http-listener's root handler's handle-request method is invoked on it. This is usually a dispatching-handler, so calls whichever appropriate subhandler you've installed for the URL. If you install more dispatching-handlers, the dispatch will happen again.

At each level of dispatch, another clause is pushed onto the request's HANDLED-BY slot value. This is a list of lists

((handler index)
 (handler index)
 ...)
where index is the element after the last position in the urlstring that was used for discrimination. You can get at the unmatched part of the URL string using REQUEST-UNHANDLED-PART.

Producing output

Usually the HANDLE-REQUEST-RESPONSE stage is the one that gets to send back a response. However, any earlier stage can send one if it wants (usually in an atypical situation, like "you have supplied an incorrect password"). If it does this, it should signal RESPONSE-SENT, so that the later methods are not invoked.

An HTTP response consists of a header and a body, and it's strongly recommended that you write the header using REQUEST-SEND-HEADERS. It returns the response code. REQUEST-SEND-HEADERS copes automatically with HTTP/0.9 requests and will also help you with conditional GETs if you ask it to: if you supply :conditional t and :last-modified some-universal-time, it will check for an If-Modified-Since header in the request, and send a 304 if the response has been sent already. If it sends a 304, it will then signal RESPONSE-SENT instead of returning, so if you wish to do any further processing after sending the request be sure to wrap it in UNWIND-PROTECT. See the documentation on HTTP caching for more detail.

Errors

HTTP errors (e.g. 404, 500) can be signalled with SIGNAL; see the example. For a list of condition names, see http-errors.lisp.

Authentication and authorization methods

Identities (usernames, basically) are unique per realm

Typically, we arrange that the authentication method is responsible for checking that the supplied credentials are correct for the given identity in the given URL space. It will only produce a page if the credentials were not supplied or don't match the claimed identity (and might not necessarily even then)

The authorization method has to check if the requested action is allowed to the given identity when connecting from the given host name (though note that IP-based authorization is not implemented currently - no point as the proxy loses all the relevant information for us)

For example

0) To use the HTTP authentication methods, make sure that your URL space corresponds to an HTTP realm. You need an authentication handler that digs for the request header's Authorization: line and shoves a suitable user identifier into (request-user ) or hands out the appopriate 40x error. A null authorization handler will produce a behaviour like the apache "require valid-user" directive, or you could actually write a handler that checked this to confine it further.

How your application reports "incorrect password" (not authenticated) as distinct from "we believe it's you, but you can't do that" (not authorized) is up to you. Most browsers' user interface to this stuff makes it hard to distinguish the two cases anyway; you might find it's simplest just to put everything in the authentication handler if the users aren't going to notice the difference.

1) cookie-based authentication, where the user's browser supplies a cookie that we previously sent him. Our authentication handler checks the supplied cookie against our internal records, filling in the (request-user ) slot if it matches. Our authorization handler checks if (request-user ) is one of the users allowed here, and prints a helpful error page if not.

2) ident-based authentication - you don't want to use this for anything serious but in a secure intranet environment it might be appropriate. Suggested implementation route would involve getting apache to do the actual lookup as (a) it won't block, and (b) you'll only end up identing the apache daemon if you do it yourself anyway.

Note

1) that we don't care what you put in the request-user slot provided that your authentication and authorization handlers both agree on it. It could be a string username, user id, CLOS object defining a user, or whatever.

2) When IP-based access control becomes possible, it could conceivably be used at authentication _or_ authorization stage. We might decide for example that all requests from pc10.foo.com are from John Smith (authenticate the request as from "John Smith") or we might decide that all administrator requests must come from localhost - authentication is identifying the remote user whereas authorization is checking that he's "administrator" and connected from localhost. Clear?

Examples

Look at example.lisp for "Hello World", then static-file-handler.lisp and redirect-handler.lisp in the source distribution for some slightly more complex examples. To see really interesting stuff, grab the source for CLiki and poke around in there.