diff --git a/collects/web-server/configuration/responders.ss b/collects/web-server/configuration/responders.ss index ac6ee15352..4f4b0cd814 100644 --- a/collects/web-server/configuration/responders.ss +++ b/collects/web-server/configuration/responders.ss @@ -77,7 +77,7 @@ (lambda (in) (read-string (file-size path) in)))) (provide/contract - [error-response ((natural-number/c string? string?) (listof (cons/c symbol? string?)) . ->* . (response?))] + [error-response ((natural-number/c string? path-string?) (listof (cons/c symbol? string?)) . ->* . (response?))] [servlet-loading-responder (url? any/c . -> . response?)] [gen-servlet-not-found (path-string? . -> . (url? . -> . response?))] [gen-servlet-responder (path-string? . -> . (url? any/c . -> . response?))] diff --git a/collects/web-server/dispatchers/dispatch.ss b/collects/web-server/dispatchers/dispatch.ss index facfd5702c..69fba4526a 100644 --- a/collects/web-server/dispatchers/dispatch.ss +++ b/collects/web-server/dispatchers/dispatch.ss @@ -5,8 +5,8 @@ (define dispatcher? (connection? request? . -> . void)) - (define dispatcher-interface-version? - symbol?) + (define (dispatcher-interface-version? v) + (and (symbol? v) (eq? v 'v1))) (define-struct exn:dispatcher ()) (define (next-dispatcher) (raise (make-exn:dispatcher))) diff --git a/collects/web-server/docs/reference/configuration.scrbl b/collects/web-server/docs/reference/configuration.scrbl index e21bf918df..0abfa1132d 100644 --- a/collects/web-server/docs/reference/configuration.scrbl +++ b/collects/web-server/docs/reference/configuration.scrbl @@ -81,13 +81,14 @@ reading, writing, parsing, and printing @scheme[configuration-table] structures. @defproc[(sexpr->configuration-table (sexpr list?)) - configuration-table?] + configuration-table?]{ + This function converts a @scheme[configuration-table] from an S-expression. +} @defproc[(configuration-table->sexpr (ctable configuration-table?)) - list?] - -These functions do the expected and convert a @scheme[configuration-table] -from (to) an S-expression. The format of this S-expresssion is: + list?]{ + This function converts a @scheme[configuration-table] to an S-expression. +} @schemeblock[ `((port ,integer?) @@ -131,12 +132,14 @@ where a @scheme[host-table-sexpr] is: (password-authentication ,path-string?)))] @defproc[(read-configuration-table (path path-string?)) - configuration-table?] -@defproc[(write-configuration-table (ctable configuration-table?) (path path-string?)) - void] + configuration-table?]{ +This function reads a @scheme[configuration-table] from @scheme[path]. +} -These functions do the expected and read (write) a @scheme[configuration-table] -from (to) the given @scheme[path]. +@defproc[(write-configuration-table (ctable configuration-table?) (path path-string?)) + void]{ +This function writes a @scheme[configuration-table] to @scheme[path]. +} @; ------------------------------------------------------------ @section[#:tag "namespace"]{Servlet Namespaces} @@ -149,16 +152,23 @@ of @file{dispatchers/dispatch-servlets.ss} and @file{dispatchers/dispatch-lang.s @; XXX Use actual keyword argument syntax @; XXX Require for current-namespace +@; XXX Link to module-spec? -@defproc[(make-make-servlet-namespace (keyword to-be-copied-module-specs (listof module-spec?))) +@defproc[(make-make-servlet-namespace (#:to-be-copied-module-specs to-be-copied-module-specs (listof module-spec?))) (key-> ([additional-specs (listof module-spec?)]) - namespace?)] - + namespace?)]{ This function creates a function that when called will construct a new @scheme[namespace] that has all the modules from @scheme[to-be-copied-module-specs] and @scheme[additional-specs], as well as @scheme[mzscheme] and @scheme[(lib "mred.ss" "mred")], provided they are already attached to the @scheme[(current-namespace)] of the call-site. +Example: +@schemeblock[ + (make-make-servlet-namespace + #:to-be-copied-module-specs `((lib "database.ss" "my-module"))) + ] +} + @subsection{Why this is useful} A different namespace is needed for each servlet, so that if servlet A and servlet B both use @@ -177,4 +187,63 @@ of servlets can share different sets of modules. @; ------------------------------------------------------------ @section[#:tag "responders"]{Standard Responders} -XXX \ No newline at end of file +@file{configuration/responders.ss} provides some functions that help constructing HTTP responders. +These functions are used by the default dispatcher constructor (see @secref["web-server-unit"]) to +turn the paths given in the @scheme[configuration-table] into responders for the associated circumstance. + +@; XXX Include response/full +@; XXX Rename error-response + +@defproc[(error-response (http-code natural-number/c) (short-version string?) (text-file string?) (extra-header (cons/c symbol? string?)) ...0) + response?]{ + Generates a @scheme[response/full] with the given @scheme[http-code] and @scheme[short-version] +as the corresponding fields; with the content of the @scheme[text-file] as the body; and, with +the @scheme[extra-header]s as, you guessed it, extra headers. +} + +@defproc[(servlet-loading-responder (url url?) (exn any/c)) + response?]{ + Prints the @scheme[exn] to standard output and responds with a "Servlet didn't load." +message. +} + +@defproc[(gen-servlet-not-found (file path-string?)) + ((url url?) . -> . response?)]{ + Returns a function that generates a standard "Servlet not found." error with content from @scheme[file]. +} + +@defproc[(gen-servlet-responder (file path-string?)) + ((url url?) (exn any/c) . -> . response?)]{ + Prints the @scheme[exn] to standard output and responds with a "Servlet error." message with content from @scheme[file]. +} + +@defproc[(gen-servlets-refreshed (file path-string?)) + (-> response?)]{ + Returns a function that generates a standard "Servlet cache refreshed." message with content from @scheme[file]. +} + +@defproc[(gen-passwords-refreshed (file path-string?)) + (-> response?)]{ + Returns a function that generates a standard "Passwords refreshed." message with content from @scheme[file]. +} + +@defproc[(gen-authentication-responder (file path-string?)) + ((url url?) (header (cons/c symbol? string?)) . -> . response?)]{ + Returns a function that generates an authentication failure error with content from @scheme[file] and +@scheme[header] as the HTTP header. +} + +@defproc[(gen-protocol-responder (file path-string?)) + ((url url?) . -> . response?)]{ + Returns a function that generates a "Malformed request" error with content from @scheme[file]. +} + +@defproc[(gen-file-not-found-responder (file path-string?)) + ((req request?) . -> . response?)]{ + Returns a function that generates a standard "File not found" error with content from @scheme[file]. +} + +@defproc[(gen-collect-garbage-responder (file path-string?)) + (-> response?)]{ + Returns a function that generates a standard "Garbage collection run" message with content from @scheme[file]. +} \ No newline at end of file diff --git a/collects/web-server/docs/reference/dispatchers.scrbl b/collects/web-server/docs/reference/dispatchers.scrbl index 1e0f720356..f160d71b27 100644 --- a/collects/web-server/docs/reference/dispatchers.scrbl +++ b/collects/web-server/docs/reference/dispatchers.scrbl @@ -3,11 +3,266 @@ @title[#:style 'toc]{Dispatchers} -XXX +The @file{web-server} is really just a peculiar configuration of a +dispatching server. There are a number of dispatchers that are defined +to support the @file{web-server}. Other dispatching servers, or variants +of the @file{web-server}, may find these useful. In particular, if you want +a peculiar processing pipeline for your @file{web-server} installation, this +documentation will be useful. @local-table-of-contents[] +@; XXX Include connection? and request? + @; ------------------------------------------------------------ -@section[#:tag "example"]{Example} +@section[#:tag "dispatch"]{General} + +@file{dispatchers/dispatch.ss} provides a few functions for dispatchers in general. + +@defproc[(dispatcher? (any any/c)) + boolean?]{ + Applies the contract @scheme[(connection? request? . -> . void)] to @scheme[any]. +} + +@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. + +@; XXX Rename const to lift +@; ------------------------------------------------------------ +@section[#:tag "dispatch-const"]{Lifting Procedures} + +@file{dispatchers/dispatch-const.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. +} + +@; ------------------------------------------------------------ +@section[#:tag "dispatch-files"]{Serving Files} + +@file{dispatchers/dispatch-files.ss} allows files to be served. +It defines a dispatcher construction procedure: + +@; XXX Change mime-types-path to path->mime-type +@; XXX Include make-get-mime-type +@defproc[(make [#:url->path url->path url->path?] + [#:mime-types-path mime-types-path path-string? "mime.types"] + [#: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, the @scheme[mime-types-path] file is consulted for the MIME + Type of the path, via @scheme[make-get-mime-type]. The file is then + streamed out the connection object. + + This dispatcher supports HTTP Range GET requests and HEAD requests.} + +@; XXX Change filtering to take predicate, rather than regexp +@; ------------------------------------------------------------ +@section[#:tag "dispatch-filter"]{Filtering Requests} + +@file{dispatchers/dispatch-filter.ss} defines a dispatcher constructer +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-host"]{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['] + is used. +} + +@; ------------------------------------------------------------ +@section[#:tag "dispatch-lang"]{Serving Web Language Servlets} + +@file{dispatchers/dispatch-lang.ss} defines a dispatcher constructor +that runs servlets written in the Web Language. + +@; XXX Don't include timeout logic in here, put it outside. +@; XXX Include configuration.scrbl exports +@defproc[(make [#:url->path url->path url->path?] + [#:make-servlet-namespace make-servlet-namespace + make-servlet-namespace? + (make-make-servlet-namespace)] + [#:timeouts-servlet-connection timeouts-servlet-connection integer? (* 60 60 24)] + [#:responders-servlet-loading responders-servlet-loading servlet-loading-responder] + [#:responders-servlet responders-servlet (gen-servlet-responder "servlet-error.html")]) + dispatcher?]{ + Extends the timeout of the connection by @scheme[timeouts-servlet-connection]. + 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. +} + +@; ------------------------------------------------------------ +@section[#:tag "dispatch-log"]{Logging} + +@file{dispatchers/dispatch-log.ss} defines a dispatcher constructer +for transparent logging of requests. + +@; XXX Take formating 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"]{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"] + [#:password-connection-timeout password-connection-timeout integer? 300] + [#: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: + Extends connection timeout by @scheme[password-connection-timeout]. + 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. +} + +@; ------------------------------------------------------------ +@section[#:tag "dispatch-pathprocedure"]{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 @file{web-server} pipeline to provide +a URL that refreshes the password file, servlet cache, etc. + +@; ------------------------------------------------------------ +@section[#:tag "dispatch-sequencer"]{Sequencing} + +@file{dispatchers/dispatch-sequencer.ss} defines a dispatcher constructor +that invokes a sequence of dispatchers until one applies. + +@defproc[(make (dispatcher dispatcher?) ...0) + 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. +} + +@; ------------------------------------------------------------ +@section[#:tag "dispatch-servlets"]{Serving Scheme Servlets} + +@file{dispatchers/dispatch-servlets.ss} defines a dispatcher constructor +that runs servlets written in Scheme. + +@; XXX Remove config:instances +@; XXX Define make-servlet-namespace? +@defproc[(make [config:instances any/c] + [config:scripts (box/c cache-table?)] + [config:make-servlet-namespace make-servlet-namespace?] + [#:url->path url->path url->path?] + [#: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-servlet-connection + timeouts-servlet-connection + integer? + (* 60 60 24)] + [#: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: + Extends the timeout of the connection by @scheme[timeouts-servlet-connection]. + 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 "filesystem-map"]{Mapping URLs to Paths} XXX \ No newline at end of file diff --git a/collects/web-server/private/response.ss b/collects/web-server/private/response.ss index 43f61165d3..0f96a2fc30 100644 --- a/collects/web-server/private/response.ss +++ b/collects/web-server/private/response.ss @@ -205,7 +205,8 @@ (define ext:output-file (ext:wrap output-file)) - + + ; XXX Check method in response ;; ************************************************** ;; output-response/method: connection response/full symbol -> void ;; If it is a head request output headers only, otherwise output as usual