racket/collects/web-server/docs/reference/dispatchers.scrbl
Jay McCarthy e86b8c99cc Hostnames are case-insensitive
svn: r6648
2007-06-13 21:22:10 +00:00

316 lines
14 KiB
Racket

#reader(lib "docreader.ss" "scribble")
@require["../web-server.ss"]
@title[#:tag "dispatchers"
#:style 'toc]{Dispatchers}
The @web-server is really just a particular configuration of a
dispatching server. There are a number of dispatchers that are defined
to support the @web-server . Other dispatching servers, or variants
of the @web-server , may find these useful. In particular, if you want
a peculiar processing pipeline for your @web-server installation, this
documentation will be useful.
@local-table-of-contents[]
@; ------------------------------------------------------------
@section[#:tag "dispatch.ss"]{General}
@file{dispatchers/dispatch.ss} provides a few functions for dispatchers in general.
@defthing[dispatcher? contract?]{
Equivalent to @scheme[(connection? request? . -> . void)].
}
@defproc[(dispatcher-interface-version? (any any/c)) boolean?]{
Returns @scheme[#t] if @scheme[any] is @scheme['v1]. Returns @scheme[#f] otherwise.
}
@defstruct[exn:dispatcher ()]{
An exception thrown to indicate that a dispatcher does not apply to a particular
request.
}
@defproc[(next-dispatcher) void]{
Raises a @scheme[exn:dispatcher]
}
As the @scheme[dispatcher?] contract suggests, a dispatcher is a function that takes a connection
and request object and does something to them. Mostly likely it will generate
some response and output it on the connection, but it may do something
different. For example, it may apply some test to the request object, perhaps
checking for a valid source IP address, and error if the test is not passed, and call @scheme[next-dispatcher]
otherwise.
Consider the following example dispatcher, that captures the essence of URL rewriting:
@schemeblock[
(code:comment "(url? -> url?) dispatcher? -> dispatcher?")
(lambda (rule inner)
(lambda (conn req)
(code:comment "Call the inner dispatcher...")
(inner conn
(code:comment "with a new request object...")
(copy-struct request req
(code:comment "with a new URL!")
[request-uri (rule (request-uri req))]))))
]
@; ------------------------------------------------------------
@section[#:tag "filesystem-map.ss"]{Mapping URLs to Paths}
@file{dispatchers/filesystem-map.ss} provides a means of mapping
URLs to paths on the filesystem.
@defthing[url-path? contract?]{
This contract is equivalent to @scheme[((url?) . ->* . (path? (listof path-element?)))].
The returned @scheme[path?] is the path on disk. The list is the list of
path elements that correspond to the path of the URL.}
@defproc[(make-url->path (base path?))
url-path?]{
The @scheme[url-path?] returned by this procedure considers the root
URL to be @scheme[base]. It ensures that @scheme[".."]s in the URL
do not escape the @scheme[base].}
@defproc[(make-url->valid-path (url->path url->path?))
url->path?]{
Runs the underlying @scheme[url->path], but only returns if the path
refers to a file that actually exists. If it is does not, then the suffix
elements of the URL are removed until a file is found. If this never occurs,
then an error is thrown.
This is primarily useful for dispatchers that allow path information after
the name of a service to be used for data, but where the service is represented
by a file. The most prominent example is obviously servlets.}
@; ------------------------------------------------------------
@section[#:tag "dispatch-sequencer.ss"]{Sequencing}
@file{dispatchers/dispatch-sequencer.ss} defines a dispatcher constructor
that invokes a sequence of dispatchers until one applies.
@defproc[(make (dispatcher dispatcher?) ...)
dispatcher?]{
Invokes each @scheme[dispatcher], invoking the next if the first
calls @scheme[next-dispatcher]. If no @scheme[dispatcher] applies,
then it calls @scheme[next-dispatcher] itself.
}
@; XXX Kind of timeout that is proportional to bindings
@; ------------------------------------------------------------
@section[#:tag "dispatch-timeout.ss"]{Timeouts}
@file{dispatchers/dispatch-timeout.ss} defines a dispatcher constructor
that changes the timeout on the connection and calls the next
dispatcher.
@defproc[(make [new-timeout integer?])
dispatcher?]{
Changes the timeout on the connection with @scheme[adjust-connection-timeout!]
called with @scheme[new-timeout].
}
@; ------------------------------------------------------------
@section[#:tag "dispatch-lift.ss"]{Lifting Procedures}
@file{dispatchers/dispatch-lift.ss} defines:
@defproc[(make (proc (request? . -> . response?)))
dispatcher?]{
Constructs a dispatcher that calls @scheme[proc] on the request
object, and outputs the response to the connection.
}
@; XXX Change filtering to take predicate, rather than regexp
@; ------------------------------------------------------------
@section[#:tag "dispatch-filter.ss"]{Filtering Requests}
@file{dispatchers/dispatch-filter.ss} defines a dispatcher constructor
that calls an underlying dispatcher
with all requests that pass a predicate.
@defproc[(make (regex regexp?) (inner dispatcher?))
dispatcher?]{
Calls @scheme[inner] if the URL path of the request, converted to
a string, matches @scheme[regex]. Otherwise, calls @scheme[next-dispatcher].
}
@; ------------------------------------------------------------
@section[#:tag "dispatch-pathprocedure.ss"]{Procedure Invocation upon Request}
@file{dispatchers/dispatch-pathprocedure.ss} defines a dispatcher constructor
for invoking a particular procedure when a request is given to a particular
URL path.
@defproc[(make (path string?) (proc (request? . -> . response?)))
dispatcher?]{
Checks if the request URL path as a string is equal to @scheme[path]
and if so, calls @scheme[proc] for a response.
}
This is used in the standard @web-server pipeline to provide
a URL that refreshes the password file, servlet cache, etc.
@; ------------------------------------------------------------
@section[#:tag "dispatch-log.ss"]{Logging}
@file{dispatchers/dispatch-log.ss} defines a dispatcher constructor
for transparent logging of requests.
@; XXX Take formatting procedure
@defproc[(make [#:log-format log-format symbol? 'parenthesized-default]
[#:log-path log-path (or/c path-string? false/c) #f])
dispatcher?]{
If @scheme[log-path] is not @scheme[#f] and @scheme[log-format] is
@scheme['parenthesized-default] or @scheme[extended], then the request
is logged to the @scheme[log-path]. In either case, @scheme[next-dispatcher]
is invoked after this.
If @scheme[log-format] is @scheme['parenthesized-default], then the
log looks like: @scheme[(list 'from (request-client-ip req)
'to (request-host-ip req)
'for (url->string (request-uri req)) 'at
(date->string (seconds->date (current-seconds)) #t))].
If @scheme[log-format] is @scheme['extended], then the log looks like:
@scheme[`((client-ip ,(request-client-ip req))
(host-ip ,(request-host-ip req))
(referer ,(or/c bytes? false/c))
(uri ,(url->string (request-uri req)))
(time ,(current-seconds)))].
}
@; ------------------------------------------------------------
@section[#:tag "dispatch-passwords.ss"]{Password Protection}
@file{dispatchers/dispatch-passwords.ss} defines a dispatcher constructor
that performs HTTP Basic authentication filtering.
@defproc[(make [#:password-file password-file path-string? "passwords"]
[#:authentication-responder
authentication-responder
((url url?) (header (cons/c symbol? string?)) . -> . response?)
(gen-authentication-responder "forbidden.html")])
(values (-> void)
dispatcher?)]{
The first returned value is a procedure that refreshes the password
file used by the dispatcher.
The dispatcher that is returned does the following:
Checks if the request contains Basic authentication credentials, and that
they are included in @scheme[password-file]. If they are not,
@scheme[authentication-responder] is called with a @scheme[header] that
requests credentials. If they are, then @scheme[next-dispatcher] is
invoked.
@; XXX Separate out password-file work
@scheme[password-file] is parsed as:
@schemeblock[(list ([domain : string?]
[path : string-regexp?]
(list [user : symbol?]
[pass : string?])
...)
...)]
For example:
@schemeblock['(("secret stuff" "/secret(/.*)?" (bubba "bbq") (|Billy| "BoB")))]
}
@; ------------------------------------------------------------
@section[#:tag "dispatch-host.ss"]{Virtual Hosts}
@file{dispatchers/dispatch-host.ss} defines a dispatcher constructor
that calls a different dispatcher based upon the host requested.
@defproc[(make (lookup-dispatcher (symbol? . -> . dispatcher?)))
dispatcher?]{
Extracts a host from the URL requested, or the Host HTTP header,
calls @scheme[lookup-dispatcher] with the host, and invokes the
returned dispatcher. If no host can be extracted, then @scheme['none]
is used.
}
@; ------------------------------------------------------------
@section[#:tag "dispatch-files.ss"]{Serving Files}
@file{dispatchers/dispatch-files.ss} allows files to be served.
It defines a dispatcher construction procedure:
@defproc[(make [#:url->path url->path url->path?]
[#:path->mime-type path->mime-type (path? . -> . bytes?) (lambda (path) TEXT/HTML-MIME-TYPE)]
[#:indices indices (listof string?) (list "index.html" "index.htm")])
dispatcher?]{
Uses @scheme[url->path] to extract a path from the URL in the request
object. If this path does not exist, then the dispatcher does not apply.
If the path is a directory, then the @scheme[indices] are checked in order
for an index file to serve. In that case, or in the case of a path that is
a file already, @scheme[path->mime-type] is consulted for the MIME
Type of the path. The file is then
streamed out the connection object.
This dispatcher supports HTTP Range GET requests and HEAD requests.}
@; ------------------------------------------------------------
@section[#:tag "dispatch-servlets.ss"]{Serving Scheme Servlets}
@file{dispatchers/dispatch-servlets.ss} defines a dispatcher constructor
that runs servlets written in Scheme.
@; XXX Add default manager arg
@; XXX Remove config:scripts
@defproc[(make [config:scripts (box/c cache-table?)]
[#:url->path url->path url->path?]
[#:make-servlet-namespace
make-servlet-namespace
make-servlet-namespace?
(make-make-servlet-namespace)]
[#:responders-servlet-loading
responders-servlet-loading
((url url?) (exn any/c) . -> . response?)
servlet-loading-responder]
[#:responders-servlet
responders-servlet
((url url?) (exn any/c) . -> . response?)
(gen-servlet-responder "servlet-error.html")]
[#:timeouts-default-servlet
timeouts-default-servlet
integer?
30])
(values (-> void)
dispatcher?)]{
The first returned value is a procedure that refreshes the servlet
code cache.
The dispatcher does the following:
If the request URL contains a continuation reference, then it is invoked with the
request. Otherwise, @scheme[url->path] is used to resolve the URL to a path.
The path is evaluated as a module, in a namespace constructed by @scheme[make-servlet-namespace].
If this fails then @scheme[responders-servlet-loading] is used to format a response
with the exception. If it succeeds, then @scheme[start] export of the module is invoked.
If there is an error when a servlet is invoked, then @scheme[responders-servlet] is
used to format a response with the exception.
Servlets that do not specify timeouts are given timeouts according to @scheme[timeouts-default-servlet].
}
@; ------------------------------------------------------------
@section[#:tag "dispatch-lang.ss"]{Serving Web Language Servlets}
@file{dispatchers/dispatch-lang.ss} defines a dispatcher constructor
that runs servlets written in the Web Language.
@defproc[(make [#:url->path url->path url->path?]
[#:make-servlet-namespace make-servlet-namespace
make-servlet-namespace?
(make-make-servlet-namespace)]
[#:responders-servlet-loading responders-servlet-loading servlet-loading-responder]
[#:responders-servlet responders-servlet (gen-servlet-responder "servlet-error.html")])
dispatcher?]{
If the request URL contains a serialized continuation, then it is invoked with the
request. Otherwise, @scheme[url->path] is used to resolve the URL to a path.
The path is evaluated as a module, in a namespace constructed by @scheme[make-servlet-namespace].
If this fails then @scheme[responders-servlet-loading] is used to format a response
with the exception. If it succeeds, then @scheme[start] export of the module is invoked.
If there is an error when a servlet is invoked, then @scheme[responders-servlet] is
used to format a response with the exception.
}