diff --git a/collects/file/doc/common.ss b/collects/file/scribblings/common.ss
similarity index 100%
rename from collects/file/doc/common.ss
rename to collects/file/scribblings/common.ss
diff --git a/collects/file/doc/file.scrbl b/collects/file/scribblings/file.scrbl
similarity index 100%
rename from collects/file/doc/file.scrbl
rename to collects/file/scribblings/file.scrbl
diff --git a/collects/file/doc/gif.scrbl b/collects/file/scribblings/gif.scrbl
similarity index 100%
rename from collects/file/doc/gif.scrbl
rename to collects/file/scribblings/gif.scrbl
diff --git a/collects/file/doc/gunzip.scrbl b/collects/file/scribblings/gunzip.scrbl
similarity index 100%
rename from collects/file/doc/gunzip.scrbl
rename to collects/file/scribblings/gunzip.scrbl
diff --git a/collects/file/doc/gzip.scrbl b/collects/file/scribblings/gzip.scrbl
similarity index 100%
rename from collects/file/doc/gzip.scrbl
rename to collects/file/scribblings/gzip.scrbl
diff --git a/collects/file/doc/info.ss b/collects/file/scribblings/info.ss
similarity index 100%
rename from collects/file/doc/info.ss
rename to collects/file/scribblings/info.ss
diff --git a/collects/file/doc/md5.scrbl b/collects/file/scribblings/md5.scrbl
similarity index 100%
rename from collects/file/doc/md5.scrbl
rename to collects/file/scribblings/md5.scrbl
diff --git a/collects/file/doc/tar.scrbl b/collects/file/scribblings/tar.scrbl
similarity index 100%
rename from collects/file/doc/tar.scrbl
rename to collects/file/scribblings/tar.scrbl
diff --git a/collects/file/doc/zip.scrbl b/collects/file/scribblings/zip.scrbl
similarity index 100%
rename from collects/file/doc/zip.scrbl
rename to collects/file/scribblings/zip.scrbl
diff --git a/collects/graphics/doc/graphics.scrbl b/collects/graphics/scribblings/graphics.scrbl
similarity index 100%
rename from collects/graphics/doc/graphics.scrbl
rename to collects/graphics/scribblings/graphics.scrbl
diff --git a/collects/graphics/doc/info.ss b/collects/graphics/scribblings/info.ss
similarity index 100%
rename from collects/graphics/doc/info.ss
rename to collects/graphics/scribblings/info.ss
diff --git a/collects/mrlib/doc/aligned-pasteboard/aligned-editor-canvas-class.scrbl b/collects/mrlib/scribblings/aligned-pasteboard/aligned-editor-canvas-class.scrbl
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/aligned-editor-canvas-class.scrbl
rename to collects/mrlib/scribblings/aligned-pasteboard/aligned-editor-canvas-class.scrbl
diff --git a/collects/mrlib/doc/aligned-pasteboard/aligned-editor-snip-class.scrbl b/collects/mrlib/scribblings/aligned-pasteboard/aligned-editor-snip-class.scrbl
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/aligned-editor-snip-class.scrbl
rename to collects/mrlib/scribblings/aligned-pasteboard/aligned-editor-snip-class.scrbl
diff --git a/collects/mrlib/doc/aligned-pasteboard/aligned-pasteboard-intf.scrbl b/collects/mrlib/scribblings/aligned-pasteboard/aligned-pasteboard-intf.scrbl
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/aligned-pasteboard-intf.scrbl
rename to collects/mrlib/scribblings/aligned-pasteboard/aligned-pasteboard-intf.scrbl
diff --git a/collects/mrlib/doc/aligned-pasteboard/aligned-pasteboard-parent-intf.scrbl b/collects/mrlib/scribblings/aligned-pasteboard/aligned-pasteboard-parent-intf.scrbl
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/aligned-pasteboard-parent-intf.scrbl
rename to collects/mrlib/scribblings/aligned-pasteboard/aligned-pasteboard-parent-intf.scrbl
diff --git a/collects/mrlib/doc/aligned-pasteboard/aligned-pasteboard.scrbl b/collects/mrlib/scribblings/aligned-pasteboard/aligned-pasteboard.scrbl
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/aligned-pasteboard.scrbl
rename to collects/mrlib/scribblings/aligned-pasteboard/aligned-pasteboard.scrbl
diff --git a/collects/mrlib/doc/aligned-pasteboard/common.ss b/collects/mrlib/scribblings/aligned-pasteboard/common.ss
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/common.ss
rename to collects/mrlib/scribblings/aligned-pasteboard/common.ss
diff --git a/collects/mrlib/doc/aligned-pasteboard/horizontal-pasteboard-class.scrbl b/collects/mrlib/scribblings/aligned-pasteboard/horizontal-pasteboard-class.scrbl
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/horizontal-pasteboard-class.scrbl
rename to collects/mrlib/scribblings/aligned-pasteboard/horizontal-pasteboard-class.scrbl
diff --git a/collects/mrlib/doc/aligned-pasteboard/stretchable-snip-intf.scrbl b/collects/mrlib/scribblings/aligned-pasteboard/stretchable-snip-intf.scrbl
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/stretchable-snip-intf.scrbl
rename to collects/mrlib/scribblings/aligned-pasteboard/stretchable-snip-intf.scrbl
diff --git a/collects/mrlib/doc/aligned-pasteboard/vertical-pasteboard-class.scrbl b/collects/mrlib/scribblings/aligned-pasteboard/vertical-pasteboard-class.scrbl
similarity index 100%
rename from collects/mrlib/doc/aligned-pasteboard/vertical-pasteboard-class.scrbl
rename to collects/mrlib/scribblings/aligned-pasteboard/vertical-pasteboard-class.scrbl
diff --git a/collects/mrlib/doc/bitmap-label.scrbl b/collects/mrlib/scribblings/bitmap-label.scrbl
similarity index 100%
rename from collects/mrlib/doc/bitmap-label.scrbl
rename to collects/mrlib/scribblings/bitmap-label.scrbl
diff --git a/collects/mrlib/doc/cache-image-snip.scrbl b/collects/mrlib/scribblings/cache-image-snip.scrbl
similarity index 100%
rename from collects/mrlib/doc/cache-image-snip.scrbl
rename to collects/mrlib/scribblings/cache-image-snip.scrbl
diff --git a/collects/mrlib/doc/common.ss b/collects/mrlib/scribblings/common.ss
similarity index 100%
rename from collects/mrlib/doc/common.ss
rename to collects/mrlib/scribblings/common.ss
diff --git a/collects/mrlib/doc/gif.scrbl b/collects/mrlib/scribblings/gif.scrbl
similarity index 100%
rename from collects/mrlib/doc/gif.scrbl
rename to collects/mrlib/scribblings/gif.scrbl
diff --git a/collects/mrlib/doc/graph/common.ss b/collects/mrlib/scribblings/graph/common.ss
similarity index 100%
rename from collects/mrlib/doc/graph/common.ss
rename to collects/mrlib/scribblings/graph/common.ss
diff --git a/collects/mrlib/doc/graph/graph-pasteboard-intf.scrbl b/collects/mrlib/scribblings/graph/graph-pasteboard-intf.scrbl
similarity index 100%
rename from collects/mrlib/doc/graph/graph-pasteboard-intf.scrbl
rename to collects/mrlib/scribblings/graph/graph-pasteboard-intf.scrbl
diff --git a/collects/mrlib/doc/graph/graph-pasteboard-mixin.scrbl b/collects/mrlib/scribblings/graph/graph-pasteboard-mixin.scrbl
similarity index 100%
rename from collects/mrlib/doc/graph/graph-pasteboard-mixin.scrbl
rename to collects/mrlib/scribblings/graph/graph-pasteboard-mixin.scrbl
diff --git a/collects/mrlib/doc/graph/graph-snip-intf.scrbl b/collects/mrlib/scribblings/graph/graph-snip-intf.scrbl
similarity index 100%
rename from collects/mrlib/doc/graph/graph-snip-intf.scrbl
rename to collects/mrlib/scribblings/graph/graph-snip-intf.scrbl
diff --git a/collects/mrlib/doc/graph/graph-snip-mixin.scrbl b/collects/mrlib/scribblings/graph/graph-snip-mixin.scrbl
similarity index 100%
rename from collects/mrlib/doc/graph/graph-snip-mixin.scrbl
rename to collects/mrlib/scribblings/graph/graph-snip-mixin.scrbl
diff --git a/collects/mrlib/doc/graph/graph.scrbl b/collects/mrlib/scribblings/graph/graph.scrbl
similarity index 100%
rename from collects/mrlib/doc/graph/graph.scrbl
rename to collects/mrlib/scribblings/graph/graph.scrbl
diff --git a/collects/mrlib/doc/include-bitmap.scrbl b/collects/mrlib/scribblings/include-bitmap.scrbl
similarity index 100%
rename from collects/mrlib/doc/include-bitmap.scrbl
rename to collects/mrlib/scribblings/include-bitmap.scrbl
diff --git a/collects/mrlib/doc/info.ss b/collects/mrlib/scribblings/info.ss
similarity index 100%
rename from collects/mrlib/doc/info.ss
rename to collects/mrlib/scribblings/info.ss
diff --git a/collects/mrlib/doc/interactive-value-port.scrbl b/collects/mrlib/scribblings/interactive-value-port.scrbl
similarity index 100%
rename from collects/mrlib/doc/interactive-value-port.scrbl
rename to collects/mrlib/scribblings/interactive-value-port.scrbl
diff --git a/collects/mrlib/doc/mrlib.scrbl b/collects/mrlib/scribblings/mrlib.scrbl
similarity index 100%
rename from collects/mrlib/doc/mrlib.scrbl
rename to collects/mrlib/scribblings/mrlib.scrbl
diff --git a/collects/mrlib/doc/name-message.scrbl b/collects/mrlib/scribblings/name-message.scrbl
similarity index 100%
rename from collects/mrlib/doc/name-message.scrbl
rename to collects/mrlib/scribblings/name-message.scrbl
diff --git a/collects/mrlib/doc/path-dialog.scrbl b/collects/mrlib/scribblings/path-dialog.scrbl
similarity index 100%
rename from collects/mrlib/doc/path-dialog.scrbl
rename to collects/mrlib/scribblings/path-dialog.scrbl
diff --git a/collects/mrlib/doc/plot.scrbl b/collects/mrlib/scribblings/plot.scrbl
similarity index 100%
rename from collects/mrlib/doc/plot.scrbl
rename to collects/mrlib/scribblings/plot.scrbl
diff --git a/collects/net/doc/cgi.scrbl b/collects/net/scribblings/cgi.scrbl
similarity index 100%
rename from collects/net/doc/cgi.scrbl
rename to collects/net/scribblings/cgi.scrbl
diff --git a/collects/net/doc/common.ss b/collects/net/scribblings/common.ss
similarity index 100%
rename from collects/net/doc/common.ss
rename to collects/net/scribblings/common.ss
diff --git a/collects/net/doc/info.ss b/collects/net/scribblings/info.ss
similarity index 100%
rename from collects/net/doc/info.ss
rename to collects/net/scribblings/info.ss
diff --git a/collects/net/doc/net.scrbl b/collects/net/scribblings/net.scrbl
similarity index 100%
rename from collects/net/doc/net.scrbl
rename to collects/net/scribblings/net.scrbl
diff --git a/collects/net/doc/sendmail.scrbl b/collects/net/scribblings/sendmail.scrbl
similarity index 100%
rename from collects/net/doc/sendmail.scrbl
rename to collects/net/scribblings/sendmail.scrbl
diff --git a/collects/net/doc/sendurl.scrbl b/collects/net/scribblings/sendurl.scrbl
similarity index 100%
rename from collects/net/doc/sendurl.scrbl
rename to collects/net/scribblings/sendurl.scrbl
diff --git a/collects/net/doc/smtp.scrbl b/collects/net/scribblings/smtp.scrbl
similarity index 100%
rename from collects/net/doc/smtp.scrbl
rename to collects/net/scribblings/smtp.scrbl
diff --git a/collects/net/doc/url.scrbl b/collects/net/scribblings/url.scrbl
similarity index 100%
rename from collects/net/doc/url.scrbl
rename to collects/net/scribblings/url.scrbl
diff --git a/collects/scribblings/guide/guide.scrbl b/collects/scribblings/guide/guide.scrbl
index 5face77e50..db540dde6e 100644
--- a/collects/scribblings/guide/guide.scrbl
+++ b/collects/scribblings/guide/guide.scrbl
@@ -118,7 +118,7 @@ executable.
"top"]} describes tools for using Scheme to access libraries that are
normally used by C programs.
-@italic{@secref[#:doc '(lib "web-server/doc/web-server.scrbl")
+@italic{@secref[#:doc '(lib "web-server/scribblings/web-server.scrbl")
"top"]} describes the PLT Scheme web server, which supports servlets
implemented in Scheme.
diff --git a/collects/web-server/scribblings/configuration.scrbl b/collects/web-server/scribblings/configuration.scrbl
new file mode 100644
index 0000000000..80d61d8683
--- /dev/null
+++ b/collects/web-server/scribblings/configuration.scrbl
@@ -0,0 +1,260 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "configuration"
+ #:style 'toc]{Configuration}
+
+There are a number of libraries and utilities useful for
+configuring the @web-server .
+
+@local-table-of-contents[]
+
+@; ------------------------------------------------------------
+@section[#:tag "configuration-table-structs.ss"]{Configuration Table Structure}
+@require[(for-label web-server/configuration/configuration-table-structs)]
+
+@defmodule[web-server/configuration/configuration-table-structs]
+
+@filepath{configuration/configuration-table-structs.ss} provides the following structures that
+represent a standard configuration (see @secref["web-server-unit.ss"]) of the @web-server .
+The contracts on this structure influence the valid types of values in
+the configuration table S-expression file format described in
+@secref["configuration-table.ss"].
+
+@defstruct[configuration-table
+ ([port port-number?]
+ [max-waiting natural-number/c]
+ [initial-connection-timeout natural-number/c]
+ [default-host host-table?]
+ [virtual-hosts (listof (cons/c string? host-table?))])]
+
+@defstruct[host-table
+ ([indices (listof string?)]
+ [log-format symbol?]
+ [messages messages?]
+ [timeouts timeouts?]
+ [paths paths?])]
+
+@defstruct[host
+ ([indices (listof string?)]
+ [log-format symbol?]
+ [log-path (or/c false/c path-string?)]
+ [passwords (or/c false/c path-string?)]
+ [responders responders?]
+ [timeouts timeouts?]
+ [paths paths?])]
+
+@defstruct[responders
+ ([servlet (url? any/c . -> . response?)]
+ [servlet-loading (url? any/c . -> . response?)]
+ [authentication (url? (cons/c symbol? string?) . -> . response?)]
+ [servlets-refreshed (-> response?)]
+ [passwords-refreshed (-> response?)]
+ [file-not-found (request? . -> . response?)]
+ [protocol (url? . -> . response?)]
+ [collect-garbage (-> response?)])]
+
+@defstruct[messages
+ ([servlet string?]
+ [authentication string?]
+ [servlets-refreshed string?]
+ [passwords-refreshed string?]
+ [file-not-found string?]
+ [protocol string?]
+ [collect-garbage string?])]
+
+@defstruct[timeouts
+ ([default-servlet number?]
+ [password number?]
+ [servlet-connection number?]
+ [file-per-byte number?]
+ [file-base number?])]
+
+@defstruct[paths
+ ([conf (or/c false/c path-string?)]
+ [host-base (or/c false/c path-string?)]
+ [log (or/c false/c path-string?)]
+ [htdocs (or/c false/c path-string?)]
+ [servlet (or/c false/c path-string?)]
+ [mime-types (or/c false/c path-string?)]
+ [passwords (or/c false/c path-string?)])]
+
+@; ------------------------------------------------------------
+@section[#:tag "configuration-table.ss"]{Configuration Table}
+@require[(for-label web-server/configuration/configuration-table)]
+
+@defmodule[web-server/configuration/configuration-table]
+
+@filepath{configuration/configuration-table.ss} provides functions for
+reading, writing, parsing, and printing @scheme[configuration-table]
+structures.
+
+@defthing[default-configuration-table-path path?]{The default configuration table S-expression file.}
+
+@defproc[(sexpr->configuration-table (sexpr list?))
+ configuration-table?]{
+ This function converts a @scheme[configuration-table] from an S-expression.
+}
+
+@defproc[(configuration-table->sexpr (ctable configuration-table?))
+ list?]{
+ This function converts a @scheme[configuration-table] to an S-expression.
+}
+
+@schemeblock[
+`((port ,integer?)
+ (max-waiting ,integer?)
+ (initial-connection-timeout ,integer?)
+ (default-host-table
+ ,host-table-sexpr?)
+ (virtual-host-table
+ (list ,symbol? ,host-table-sexpr?)
+ ...))]
+
+where a @scheme[host-table-sexpr] is:
+
+@; XXX Allowable log-formats?
+@; XXX Where the paths are resolved relative to
+@schemeblock[
+`(host-table
+ (default-indices ,string? ...)
+ (log-format ,symbol?)
+ (messages
+ (servlet-message ,path-string?)
+ (authentication-message ,path-string?)
+ (servlets-refreshed ,path-string?)
+ (passwords-refreshed ,path-string?)
+ (file-not-found-message ,path-string?)
+ (protocol-message ,path-string?)
+ (collect-garbage ,path-string?))
+ (timeouts
+ (default-servlet-timeout ,integer?)
+ (password-connection-timeout ,integer?)
+ (servlet-connection-timeout ,integer?)
+ (file-per-byte-connection-timeout ,integer?)
+ (file-base-connection-timeout ,integer))
+ (paths
+ (configuration-root ,path-string?)
+ (host-root ,path-string?)
+ (log-file-path ,path-string?)
+ (file-root ,path-string?)
+ (servlet-root ,path-string?)
+ (mime-types ,path-string?)
+ (password-authentication ,path-string?)))]
+
+@defproc[(read-configuration-table (path path-string?))
+ configuration-table?]{
+This function reads a @scheme[configuration-table] from @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.ss"]{Servlet Namespaces}
+@require[(for-label web-server/configuration/namespace)]
+
+@defmodule[web-server/configuration/namespace]
+
+@filepath{configuration/namespace.ss} provides a function to help create the
+@scheme[make-servlet-namespace] procedure needed by the @scheme[make] functions
+of @filepath{dispatchers/dispatch-servlets.ss} and @filepath{dispatchers/dispatch-lang.ss}.
+
+@; XXX Define make-servlet-namespace?
+@; XXX Use actual keyword argument syntax
+
+@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?)]{
+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
+a stateful module C, they will be isolated from one another. We see the @web-server as
+an operating system for servlets, so we inherit the isolation requirement on operating systems.
+
+However, there are some modules which must be shared. If they were not, then structures cannot
+be passed from the @web-server to the servlets, due to a subtlety in the way MzScheme
+implements structures.
+
+Since, on occasion, a user will actually wanted servlets A and B to interact through module C.
+A custom @scheme[make-servlet-namespace] can be created, through this procedure, that attaches
+module C to all servlet namespaces. Through other means (see @secref["dispatchers"]) different sets
+of servlets can share different sets of modules.
+
+@; ------------------------------------------------------------
+@section[#:tag "responders.ss"]{Standard Responders}
+@require[(for-label web-server/configuration/responders)]
+
+@defmodule[web-server/configuration/responders]
+
+@filepath{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.ss"]) to
+turn the paths given in the @scheme[configuration-table] into responders for the associated circumstance.
+
+@defproc[(file-response (http-code natural-number/c) (short-version string?) (text-file string?) (header header?) ...)
+ 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[header]s as, you guessed it, 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 header?) . -> . 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].
+}
diff --git a/collects/web-server/scribblings/dispatchers.scrbl b/collects/web-server/scribblings/dispatchers.scrbl
new file mode 100644
index 0000000000..c7f11dd0a7
--- /dev/null
+++ b/collects/web-server/scribblings/dispatchers.scrbl
@@ -0,0 +1,376 @@
+#lang scribble/doc
+@(require "web-server.ss"
+ (for-syntax scheme/base))
+
+@(define-syntax (a-dispatcher stx)
+ (syntax-case stx ()
+ [(_ lib-name lib-desc . rest)
+ ;; This macro plays a standard trick for limiting the scope of
+ ;; `require'd bindings: it puts the require and the scope of the
+ ;; require into a macro, which introduces both together
+ #'(begin
+ (define-syntax-rule (intro)
+ ((... ...)
+ (begin
+ (require (for-label lib-name))
+ (defmodule lib-name
+ "The " (schememodname lib-name) " module " lib-desc)
+ . rest)))
+ (intro))]))
+
+@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}
+@require[(for-label web-server/dispatchers/dispatch)]
+
+@defmodule[web-server/dispatchers/dispatch]
+
+@filepath{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}
+@require[(for-label web-server/dispatchers/filesystem-map)]
+
+@defmodule[web-server/dispatchers/filesystem-map]
+
+@filepath{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] and removes them silently otherwise.}
+
+@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}
+@a-dispatcher[web-server/dispatchers/dispatch-sequencer
+ @elem{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}
+@a-dispatcher[web-server/dispatchers/dispatch-timeout
+ @elem{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}
+@a-dispatcher[web-server/dispatchers/dispatch-lift
+ @elem{defines a dispatcher constructor.}]{
+
+@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}
+@a-dispatcher[web-server/dispatchers/dispatch-filter
+ @elem{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}
+@a-dispatcher[web-server/dispatchers/dispatch-pathprocedure
+ @elem{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}
+@a-dispatcher[web-server/dispatchers/dispatch-log
+ @elem{defines a dispatcher constructor
+ for transparent logging of requests.}]{
+
+@defthing[format-req/c contract?]{
+ Equivalent to @scheme[(request? . -> . string?)].
+}
+
+@defthing[paren-format format-req/c]{
+ Formats a request by:
+ @schemeblock[
+ (format "~s~n"
+ (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)))
+ ]}
+
+@defthing[extended-format format-req/c]{
+ Formats a request by:
+ @schemeblock[
+ (format "~s~n"
+ `((client-ip ,(request-client-ip req))
+ (host-ip ,(request-host-ip req))
+ (referer ,(let ([R (headers-assq* #"Referer" (request-headers/raw req))])
+ (if R
+ (header-value R)
+ #f)))
+ (uri ,(url->string (request-uri req)))
+ (time ,(current-seconds))))
+ ]}
+
+@defthing[apache-default-format format-req/c]{
+ Formats a request like Apache's default.
+}
+
+@defproc[(log-format->format [id symbol?])
+ format-req/c]{
+ Maps @scheme['parenthesized-default] to @scheme[paren-format],
+ @scheme['extended] to @scheme[extended-format], and
+ @scheme['apache-default] to @scheme[apache-default-format].
+}
+
+@defproc[(make [#:format format format-req/c paren-format]
+ [#:log-path log-path path-string? "log"])
+ dispatcher?]{
+ Logs requests to @scheme[log-path] by using @scheme[format] to format the requests.
+ Then invokes @scheme[next-dispatcher].
+}}
+
+@; ------------------------------------------------------------
+@section[#:tag "dispatch-passwords.ss"]{Password Protection}
+@a-dispatcher[web-server/dispatchers/dispatch-passwords
+ @elem{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}
+@a-dispatcher[web-server/dispatchers/dispatch-host
+ @elem{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}
+@a-dispatcher[web-server/dispatchers/dispatch-files
+ @elem{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}
+@a-dispatcher[web-server/dispatchers/dispatch-servlets
+ @elem{defines a dispatcher constructor
+ that runs servlets written in Scheme.}]{
+
+@; 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}
+@a-dispatcher[web-server/dispatchers/dispatch-lang
+ @elem{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.
+}}
+
+@; ------------------------------------------------------------
+@section[#:tag "dispatch-stat.ss"]{Statistics}
+@a-dispatcher[web-server/dispatchers/dispatch-stat
+ @elem{provides services related to performance
+ statistics.}]{
+
+@defproc[(make-gc-thread [time integer?])
+ thread?]{
+ Starts a thread that calls @scheme[(collect-garbage)] every @scheme[time] seconds.
+}
+
+@defproc[(make)
+ dispatcher?]{
+ Returns a dispatcher that prints memory usage on every request.
+}}
diff --git a/collects/web-server/scribblings/dummy-language-servlet.ss b/collects/web-server/scribblings/dummy-language-servlet.ss
new file mode 100644
index 0000000000..95ce4bd630
--- /dev/null
+++ b/collects/web-server/scribblings/dummy-language-servlet.ss
@@ -0,0 +1,6 @@
+#lang scheme/base
+
+(define start #f)
+
+(provide (all-defined-out))
+
diff --git a/collects/web-server/scribblings/dummy-servlet.ss b/collects/web-server/scribblings/dummy-servlet.ss
new file mode 100644
index 0000000000..f4e3347977
--- /dev/null
+++ b/collects/web-server/scribblings/dummy-servlet.ss
@@ -0,0 +1,9 @@
+#lang scheme/base
+
+(define interface-version #f)
+(define timeout #f)
+(define start #f)
+(define manager #f)
+
+(provide (all-defined-out))
+
diff --git a/collects/web-server/scribblings/faq.scrbl b/collects/web-server/scribblings/faq.scrbl
new file mode 100644
index 0000000000..0f0053cde1
--- /dev/null
+++ b/collects/web-server/scribblings/faq.scrbl
@@ -0,0 +1,13 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title{Troubleshooting}
+
+@section{General}
+
+@subsection{IE ignores my CSS or behaves strange in other ways}
+
+In quirks mode, IE does not parse your page as XML, in particular it will not recognize many instances of
+"empty tag shorthand", e.g. "
", whereas the @web-server uses @scheme[(lib "xml.ss" "xml")]
+to format XML, which uses empty tag shorthand by default. You can change the default with the @scheme[empty-tag-shorthand]
+parameter: @scheme[(empty-tag-shorthand 'never)].
diff --git a/collects/web-server/scribblings/info.ss b/collects/web-server/scribblings/info.ss
new file mode 100644
index 0000000000..839b578989
--- /dev/null
+++ b/collects/web-server/scribblings/info.ss
@@ -0,0 +1,3 @@
+(module info setup/infotab
+ (define name "Web Server documentation"))
+
diff --git a/collects/web-server/scribblings/lang.scrbl b/collects/web-server/scribblings/lang.scrbl
new file mode 100644
index 0000000000..626110c749
--- /dev/null
+++ b/collects/web-server/scribblings/lang.scrbl
@@ -0,0 +1,292 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "lang"
+ #:style 'toc]{Web Language Servlets}
+
+The @web-server allows servlets to be written in a special Web
+language that is nearly identical to Scheme. Herein we discuss how it
+is different and what API is provided.
+
+@local-table-of-contents[]
+
+@; ------------------------------------------------------------
+@section[#:tag "lang-servlets"]{Definition}
+@require[(for-label "dummy-language-servlet.ss")] ; to give a binding context
+
+@declare-exporting[web-server/scribblings/dummy-language-servlet]
+
+A @defterm{Web language servlet} is a module written in the
+@scheme[(lib "lang.ss" "web-server")] module language. It should provide
+the following identifier:
+
+@defproc[(start [initial-request request?])
+ response?]{
+ This function is called when this servlet is invoked.
+ The argument is the HTTP request that initiated the servlet.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "considerations"]{Usage Considerations}
+
+A servlet has the following process performed on it automatically:
+@itemize[
+ @item{All uses of @scheme[letrec] are removed and replaced with equivalent uses of
+ @scheme[let] and imperative features. (@filepath{lang/elim-letrec.ss})}
+ @item{The program is converted into ANF (Administrative Normal Form),
+ making all continuations explicit. (@filepath{lang/anormal.ss})}
+ @item{All continuations (and other continuations marks) are recorded in the
+ continuation marks of the expression
+ they are the continuation of. (@filepath{lang/elim-callcc.ss})}
+ @item{All calls to external modules are identified and marked.
+ (@filepath{lang/elim-callcc.ss})}
+ @item{All uses of @scheme[call/cc] are removed and replaced with
+ equivalent gathering of the continuations through the continuation-marks.
+ (@filepath{lang/elim-callcc.ss})}
+ @item{The program is defunctionalized with a serializable data-structure for each
+ anonymous lambda. (@filepath{lang/defun.ss})}
+]
+
+This process allows the continuations captured by your servlet to be serialized.
+This means they may be stored on the client's browser or the server's disk.
+Thus, your servlet has no cost to the server other than execution. This is
+very attractive if you've used Scheme servlets and had memory problems.
+
+This process IS defined on all of PLT Scheme and occurs AFTER macro-expansion,
+so you are free to use all interesting features of PLT Scheme. However, there
+are some considerations you must make.
+
+First, this process drastically changes the structure of your program. It
+will create an immense number of lambdas and structures your program
+did not normally contain. The performance implication of this has not been
+studied with PLT Scheme. However, it is theoretically a benefit. The main
+implications would be due to optimizations MzScheme attempts to perform
+that will no longer apply. Ideally, your program should be optimized first.
+
+Second, the defunctionalization process is sensitive to the syntactic structure
+of your program. Therefore, if you change your program in a trivial way, for example,
+changing a constant, then all serialized continuations will be obsolete and will
+error when deserialization is attempted. This is a feature, not a bug!
+
+Third, the values in the lexical scope of your continuations must be serializable
+for the continuations itself to be serializable. This means that you must use
+@scheme[define-serializable-struct] rather than @scheme[define-struct], and take
+care to use modules that do the same. Similarly, you may not use @scheme[parameterize],
+because parameterizations are not serializable.
+
+Fourth, and related, this process only runs on your code, not on the code you
+@scheme[require]. Thus, your continuations---to be capturable---must not
+be in the context of another module. For example, the following will not work:
+@schemeblock[
+ (define requests
+ (map (lambda (rg) (send/suspend/url rg))
+ response-generators))
+]
+because @scheme[map] is not transformed by the process. However, if you defined
+your own @scheme[map] function, there would be no problem.
+
+Fifth, the store is NOT serialized. If you rely on the store you will
+be taking huge risks. You will be assuming that the serialized continuation
+is invoked before the server is restarted or the memory is garbage collected.
+
+This process is derived from the paper
+@href-link["http://www.cs.brown.edu/~sk/Publications/Papers/Published/pcmkf-cont-from-gen-stack-insp/" "\"Continuations from Generalized Stack Inspection\""].
+We thank Greg Pettyjohn for his initial implementation of this algorithm.
+
+@; ------------------------------------------------------------
+@section[#:tag "reprovided"]{Reprovided API}
+
+The APIs from @scheme[(lib "url.ss" "net")], @secref["request-structs.ss"],
+@secref["response-structs.ss"], and @secref["helpers.ss"] are reprovided
+by the Web language API.
+
+@; ------------------------------------------------------------
+@section[#:tag "lang/web.ss"]{Web}
+@require[(for-label web-server/lang/web)]
+
+@defmodule[web-server/lang/web]
+
+@filepath{lang/web.ss} provides the most basic Web functionality.
+
+@defproc[(send/suspend/url [response-generator (url? . -> . response?)])
+ request?]{
+ Captures the current continuation. Serializes it and stuffs it into
+ a URL. Calls @scheme[response-generator] with this URL and delivers
+ the response to the client. If the URL is invoked
+ the request is returned to this continuation.
+}
+
+@defproc[(send/suspend/hidden [response-generator (url? xexpr? . -> . response?)])
+ request?]{
+ Captures the current continuation. Serializes it and generates an INPUT
+ form that includes the serialization as a hidden form.
+ Calls @scheme[response-generator] with this URL and form field and delivers
+ the response to the client. If the URL is invoked with form data containing
+ the hidden form,
+ the request is returned to this continuation.
+
+ Note: The continuation is NOT stuffed.
+}
+
+@defproc[(embed-proc/url [k-url url?]
+ [proc (request? . -> . any/c)])
+ url?]{
+ Serializes and stuffs @scheme[proc] into @scheme[k-url]. For use with
+ @scheme[extract-proc/url].
+}
+
+@defproc[(extract-proc/url [req request?])
+ any/c]{
+ Inspects the URL of @scheme[req] and attempts to extract the procedure
+ embedded with @scheme[embed-proc/url]. If successful, it is invoked with
+ @scheme[req] as an argument.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "lang/stuff-url.ss"]{Stuff URL}
+@require[(for-label web-server/lang/stuff-url)]
+
+@defmodule[web-server/lang/stuff-url]
+
+@filepath{lang/stuff-url.ss} provides an interface for "stuffing"
+serializable values into URLs. Currently there is a particular
+hard-coded behavior, but we hope to make it more flexible in
+the future.
+
+@defproc[(stuff-url [v serializable?]
+ [u url?])
+ url?]{
+ Serializes @scheme[v] and computes the MD5 of the serialized
+ representation. The serialization of @scheme[v] is written to
+ @filepath{$HOME/.urls/M} where `M' is the MD5. `M' is then
+ placed in @scheme[u] as a URL param.
+}
+
+@defproc[(stuffed-url? [u url?])
+ boolean?]{
+ Checks if @scheme[u] appears to be produced by @scheme[stuff-url].
+}
+
+@defproc[(unstuff-url [u url?])
+ serializable?]{
+ Extracts the value previously serialized into @scheme[u] by @scheme[stuff-url].
+}
+
+In the future, we will offer the facilities to:
+@itemize[
+ @item{Optionally use the content-addressed storage.}
+ @item{Use different hashing algorithms for the CAS.}
+ @item{Encrypt the serialized value.}
+ @item{Only use the CAS if the URL would be too long. (URLs may only be 1024 characters.)}
+]
+
+@; ------------------------------------------------------------
+@section[#:tag "lang/web-extras.ss"]{Web Extras}
+@require[(for-label web-server/lang/web-extras)]
+
+@defmodule[web-server/lang/web-extras]{The
+@schememodname[web-server/lang/web-extras] library provides
+@scheme[send/suspend/dispatch] and @scheme[redirect/get] as
+@schememodname[web-server/servlet/web], except they use
+@scheme[embed-proc/url] plus @scheme[extract-proc/url] and
+@scheme[send/suspend/url], respectively.}
+
+@deftogether[(
+@defform[(send/suspend/dispatch response-proc-expr)]
+@defproc[(redirect/get) request?]
+)]{
+
+See @schememodname[web-server/servlet/web].}
+
+@; ------------------------------------------------------------
+@section[#:tag "lang/file-box.ss"]{File Boxes}
+@require[(for-label web-server/lang/file-box)]
+
+@defmodule[web-server/lang/file-box]
+
+As mentioned earlier, it is dangerous to rely on the store in
+Web Language servlets, due to the deployment scenarios available
+to them. @filepath{lang/file-box.ss} provides a simple API to replace
+boxes in a safe way.
+
+@defproc[(file-box? [v any/c])
+ boolean?]{Checks if @scheme[v] is a file-box.}
+
+@defproc[(file-box [p path?]
+ [v serializable?])
+ file-box?]{
+ Creates a file-box that is stored at @scheme[p], with the default
+ contents of @scheme[v].
+}
+
+@defproc[(file-unbox [fb file-box?])
+ serializable?]{
+ Returns the value inside @scheme[fb]
+}
+
+@defproc[(file-box-set? [fb file-box?])
+ boolean?]{
+ Returns @scheme[#t] if @scheme[fb] contains a value.
+}
+
+@defproc[(file-box-set! [fb file-box?]
+ [v serializable?])
+ void]{
+ Saves @scheme[v] in the file represented by @scheme[fb].
+}
+
+@warning{If you plan on using a load-balancer, make sure your file-boxes
+are on a shared medium.}
+
+@; ------------------------------------------------------------
+@section[#:tag "lang/web-param.ss"]{Web Parameters}
+@require[(for-label web-server/lang/web-param)]
+
+@defmodule[web-server/lang/web-param]
+
+As mentioned earlier, it is not easy to use @scheme[parameterize] in the
+Web Language. @filepath{lang/web-param.ss} provides (roughly) the same
+functionality in a way that is serializable. Like other serializable
+things in the Web Language, they are sensitive to source code modification.
+
+@defform[(make-web-parameter default)]{
+ Expands to the definition of a web-parameter with
+ @scheme[default] as the default value. A web-parameter is
+ a procedure that, when called with zero arguments, returns @scheme[default]
+ or the last value @scheme[web-parameterize]d in the dynamic context
+ of the call.
+}
+
+@defproc[(web-parameter? [v any/c])
+ boolean?]{
+ Checks if @scheme[v] appears to be a web-parameter.
+}
+
+@defform[(web-parameterize ([web-parameter-expr value-expr] ...) expr ...)]{
+ Runs @scheme[(begin expr ...)] such that the web-parameters that
+ the @scheme[web-parameter-expr]s evaluate to are bound to the @scheme[value-expr]s.
+ From the perspective of the @scheme[value-expr]s, this is like @scheme[let].
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "lang/web-cells.ss"]{Web Cells}
+@require[(for-label web-server/lang/web-cells)]
+
+@defmodule[web-server/lang/web-cells]{The
+@schememodname[web-server/lang/web-cells] library provides the same
+API as @schememodname[web-server/servlet/web-cells], but in a way
+compatible with the Web Language. The one difference is that
+@scheme[make-web-cell] is syntax, rather than a function.}
+
+@deftogether[(
+@defproc[(web-cell? [v any/c])
+ boolean?]
+@defform[(make-web-cell default-expr)]
+@defproc[(web-cell-ref [wc web-cell?])
+ any/c]
+@defproc[(web-cell-shadow [wc web-cell?]
+ [v any/c])
+ void]
+)]{
+
+See @schememodname[web-server/servlet/web-cells].}
diff --git a/collects/web-server/scribblings/managers.scrbl b/collects/web-server/scribblings/managers.scrbl
new file mode 100644
index 0000000000..3c4e45ad39
--- /dev/null
+++ b/collects/web-server/scribblings/managers.scrbl
@@ -0,0 +1,160 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "managers"
+ #:style 'toc]{Continuation Managers}
+
+Since Scheme servlets store their continuations on the server, they take
+up memory on the server. Furthermore, garbage collection can not be used
+to free this memory, because there are roots outside the system: users'
+browsers, bookmarks, brains, and notebooks. Therefore, some other strategy
+must be used if memory usage is to be controlled. This functionality is
+pluggable through the manager interface.
+
+@local-table-of-contents[]
+
+@; ------------------------------------------------------------
+@section[#:tag "manager.ss"]{General}
+@require[(for-label web-server/managers/manager)]
+
+@defmodule[web-server/managers/manager]
+
+@filepath{managers/manager.ss} defines the manager interface. It is required by
+the users and implementers of managers.
+
+@defstruct[manager ([create-instance ((-> void) . -> . number?)]
+ [adjust-timeout! (number? number? . -> . void)]
+ [clear-continuations! (number? . -> . void)]
+ [continuation-store! (number? any/c expiration-handler? . -> . (list/c number? number?))]
+ [continuation-lookup (number? number? number? . -> . any/c)])]{
+ @scheme[create-instance] is called to initialize a instance, to hold the
+ continuations of one servlet session. It is passed
+ a function to call when the instance is expired. It runs the id of the
+ instance.
+
+ @scheme[adjust-timeout!] is a to-be-deprecated function that takes an
+ instance-id and a number. It is specific to the timeout-based manager
+ and will be removed.
+
+ @scheme[clear-continuations!] expires all the continuations of an instance.
+
+ @scheme[continuation-store!] is given an instance-id, a continuation value,
+ and a function to include in the exception thrown if the continuation is
+ looked up and has been expired. The two numbers returned are a
+ continuation-id and a nonce.
+
+ @scheme[continuation-lookup] finds the continuation value associated with
+ the instance-id, continuation-id, and nonce triple it is given.
+}
+
+@defstruct[(exn:fail:servlet-manager:no-instance exn:fail)
+ ([expiration-handler expiration-handler?])]{
+ This exception should be thrown by a manager when an instance is looked
+ up that does not exist.
+}
+
+@defstruct[(exn:fail:servlet-manager:no-continuation exn:fail)
+ ([expiration-handler expiration-handler?])]{
+ This exception should be thrown by a manager when a continuation is
+ looked up that does not exist.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "none.ss"]{No Continuations}
+@require[(for-label web-server/managers/none)]
+
+@defmodule[web-server/managers/none]
+
+@filepath{managers/none.ss} defines a manager constructor:
+
+@defproc[(create-none-manager (instance-expiration-handler expiration-handler?))
+ manager?]{
+ This manager does not actually store any continuation or instance data.
+ You could use it if you know your servlet does not use the continuation
+ capturing functions and want the server to not allocate meta-data
+ structures for each instance.
+}
+
+If you are considering using this manager, also consider using the
+Web Language. (See @secref["lang"].)
+
+@; ------------------------------------------------------------
+@section[#:tag "timeouts.ss"]{Timeouts}
+@require[(for-label web-server/managers/timeouts)]
+
+@defmodule[web-server/managers/timeouts]
+
+@filepath{managers/timeouts.ss} defines a manager constructor:
+
+@defproc[(create-timeout-manager [instance-exp-handler expiration-handler?]
+ [instance-timeout number?]
+ [continuation-timeout number?])
+ manager?]{
+ Instances managed by this manager will be expired @scheme[instance-timeout]
+ seconds after the last time it is accessed. If an expired instance is
+ looked up, the @scheme[exn:fail:servlet-manager:no-instance] exception
+ is thrown with @scheme[instance-exp-handler] as the expiration handler.
+
+ Continuations managed by this manager will be expired @scheme[continuation-timeout]
+ seconds after the last time it is accessed. If an expired continuation is looked
+ up, the @scheme[exn:fail:servlet-manager:no-continuation] exception
+ is thrown with @scheme[instance-exp-handler] as the expiration handler, if
+ no expiration-handler was passed to @scheme[continuation-store!].
+
+ @scheme[adjust-timeout!] corresponds to @scheme[reset-timer!] on the timer
+ responsible for the servlet instance.
+}
+
+This manager has been found to be... problematic... in large-scale
+deployments of the @web-server .
+
+@; ------------------------------------------------------------
+@section[#:tag "lru.ss"]{LRU}
+@require[(for-label web-server/managers/lru)]
+
+@defmodule[web-server/managers/lru]
+
+@filepath{managers/lru.ss} defines a manager constructor:
+
+@defproc[(create-LRU-manager
+ [instance-expiration-handler expiration-handler?]
+ [check-interval integer?]
+ [collect-interval integer?]
+ [collect? (-> boolean?)]
+ [#:initial-count initial-count integer? 1]
+ [#:inform-p inform-p (integer? . -> . void) (lambda _ (void))])
+ manager?]{
+ Instances managed by this manager will be expired if there are no
+ continuations associated with them, after the instance is unlocked.
+ If an expired instance is looked up, the
+ @scheme[exn:fail:servlet-manager:no-instance] exception
+ is thrown with @scheme[instance-exp-handler] as the expiration handler.
+
+ Continuations managed by this manager are given a "Life Count" of
+ @scheme[initial-count] initially. If an expired continuation is looked
+ up, the @scheme[exn:fail:servlet-manager:no-continuation] exception
+ is thrown with @scheme[instance-exp-handler] as the expiration handler, if
+ no expiration-handler was passed to @scheme[continuation-store!].
+
+ Every @scheme[check-interval] seconds @scheme[collect?] is called to determine
+ if the collection routine should be run. Every @scheme[collect-interval] seconds
+ the collection routine is run.
+
+ Every time the collection routine runs, the "Life Count" of every
+ continuation is decremented by @scheme[1]. If a continuation's count
+ reaches @scheme[0], it is expired. The @scheme[inform-p] function
+ is called if any continuations are expired, with the number of
+ continuations expired.
+}
+
+The recommended use of this manager is to pass, as @scheme[collect?], a
+function that checks the memory usage of the system, through
+@scheme[current-memory-use]. Then, @scheme[collect-interval] should be sufficiently
+large compared to @scheme[check-interval]. This way, if the load on the server
+spikes---as indicated by memory usage---the server will quickly expire
+continuations, until the memory is back under control. If the load
+stays low, it will still efficiently expire old continuations.
+
+With @href-link["http://continue.cs.brown.edu/" "Continue"], we went from needing to restart the server a few times
+a week and having many complaints under load, to not having these complaints
+and not needing to restart the server for performance reasons.
diff --git a/collects/web-server/scribblings/private.scrbl b/collects/web-server/scribblings/private.scrbl
new file mode 100644
index 0000000000..c2a4c245f2
--- /dev/null
+++ b/collects/web-server/scribblings/private.scrbl
@@ -0,0 +1,422 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "private"
+ #:style 'toc]{Internal}
+
+The @web-server is a complicated piece of software and as a result,
+defines a number of interesting and independently useful sub-components.
+Some of these are documented here.
+
+@local-table-of-contents[]
+
+
+@; ------------------------------------------------------------
+@section[#:tag "timer.ss"]{Timers}
+@require[(for-label web-server/private/timer)]
+
+@defmodule[web-server/private/timer]
+
+@filepath{private/timer.ss} provides a functionality for running
+procedures after a given amount of time, that may be extended.
+
+@defstruct[timer ([evt evt?]
+ [expire-seconds number?]
+ [action (-> void)])]{
+ @scheme[evt] is an @scheme[alarm-evt] that is ready at @scheme[expire-seconds].
+ @scheme[action] should be called when this @scheme[evt] is ready.
+}
+
+@defproc[(start-timer-manager [cust custodian?])
+ void]{
+ Handles the execution and management of timers. Resources are charged to
+ @scheme[cust].
+}
+
+@defproc[(start-timer [s number?]
+ [action (-> void)])
+ timer?]{
+ Registers a timer that runs @scheme[action] after @scheme[s] seconds.
+}
+
+@defproc[(reset-timer! [t timer?]
+ [s number?])
+ void]{
+ Changes @scheme[t] so that it will fire after @scheme[s] seconds.
+}
+
+@defproc[(increment-timer! [t timer?]
+ [s number?])
+ void]{
+ Changes @scheme[t] so that it will fire after @scheme[s] seconds from when
+ it does now.
+}
+
+@defproc[(cancel-timer! [t timer?])
+ void]{
+ Cancels the firing of @scheme[t] ever and frees resources used by @scheme[t].
+}
+
+
+@; XXX Generalize
+@; ------------------------------------------------------------
+@section[#:tag "connection-manager.ss"]{Connection Manager}
+@require[(for-label web-server/private/connection-manager)]
+
+@defmodule[web-server/private/connection-manager]
+
+@filepath{private/connection-manager.ss} provides functionality for managing pairs of
+input and output ports. We have plans to allow a number of different strategies
+for doing this.
+
+@defstruct[connection
+ ([timer timer?]
+ [i-port input-port?] [o-port output-port?] [custodian custodian?]
+ [close? boolean?])]{
+ A connection is a pair of ports (@scheme[i-port] and @scheme[o-port]) that is
+ ready to close after the current job if @scheme[close?] is @scheme[#t]. Resources
+ associated with the connection should be allocated under @scheme[custodian].
+ The connection will last until @scheme[timer] triggers.
+}
+
+@; XXX Don't pass in parent-cust
+@defproc[(start-connection-manager [parent-cust custodian?])
+ void]{
+ Runs the connection manager (now just the timer manager) will @scheme[parent-cust]
+ as the custodian.
+}
+
+@defproc[(new-connection [timeout number?]
+ [i-port input-port?]
+ [o-port output-port?]
+ [cust custodian?]
+ [close? boolean?])
+ connection?]{
+ Constructs a connection with a timer with a trigger of @scheme[timeout] that calls
+ @scheme[kill-connection!].
+}
+
+@defproc[(kill-connection! [c connection?])
+ void]{
+ Closes the ports associated with @scheme[c], kills the timer, and shuts down
+ the custodian.
+}
+
+@defproc[(adjust-connection-timeout! [c connection?]
+ [t number?])
+ void]{
+ Calls @scheme[reset-timer!] with the timer behind @scheme[c] with @scheme[t].
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "dispatch-server-unit.ss"]{Dispatching Server}
+@require[(for-label web-server/private/dispatch-server-unit)]
+@require[(for-label web-server/private/dispatch-server-sig)]
+@require[(for-label web-server/web-server-sig)]
+
+The @web-server is just a configuration of a dispatching server.
+This dispatching server component is useful on its own.
+
+@subsection{Dispatching Server Signatures}
+
+@defmodule[web-server/private/dispatch-server-sig]
+
+The @schememodname[web-server/private/dispatch-server-sig] library
+provides two signatures.
+
+@defsignature[dispatch-server^ ()]{
+
+The @scheme[dispatch-server^] signature is an alias for
+@scheme[web-server^].
+
+ @defproc[(serve) (-> void)]{
+ Runs the server and returns a procedure that shuts down the server.
+ }
+
+ @defproc[(serve-ports [ip input-port?]
+ [op output-port?])
+ void]{
+ Serves a single connection represented by the ports @scheme[ip] and
+ @scheme[op].
+ }
+}
+
+@defsignature[dispatch-server-config^ ()]{
+
+ @defthing[port port?]{Specifies the port to serve on.}
+ @defthing[listen-ip string?]{Passed to @scheme[tcp-accept].}
+ @defthing[max-waiting integer?]{Passed to @scheme[tcp-accept].}
+ @defthing[initial-connection-timeout integer?]{Specifies the initial timeout given to a connection.}
+ @defproc[(read-request [c connection?]
+ [p port?]
+ [port-addresses port-addresses?])
+ any/c]{
+ Defines the way the server reads requests off connections to be passed
+ to @scheme[dispatch].
+ }
+ @defthing[dispatch dispatcher?]{How to handle requests.}
+}
+
+
+@subsection{Dispatching Server Unit}
+
+@defmodule[web-server/private/dispatch-server-unit]
+
+The @schememodname[web-server/private/dispatch-server-unit] module
+provides the unit that actually implements a dispatching server.
+
+@; XXX Talk about how threads and custodians are used.
+
+@defthing[dispatch-server\@ (unit/c (tcp^ dispatch-server-config^)
+ (dispatch-server^))]{
+ Runs the dispatching server config in a very basic way, except that it uses
+ @secref["connection-manager.ss"] to manage connections.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "closure.ss"]{Serializable Closures}
+@require[(for-label web-server/private/closure)]
+@require[(for-label web-server/private/define-closure)]
+
+@defmodule[web-server/private/closure]
+
+The defunctionalization process of the Web Language (see @secref["lang"])
+requires an explicit representation of closures that is serializable.
+@filepath{private/closure.ss} is this representation. It provides:
+
+@defproc[(make-closure-definition-syntax [tag syntax?]
+ [fvars (listof identifier?)]
+ [proc syntax?])
+ syntax?]{
+ Outputs a syntax object that defines a serializable structure,
+ with @scheme[tag] as the tag, that represents a closure over
+ @scheme[fvars], that acts a procedure and when invoked calls
+ @scheme[proc], which is assumed to be syntax of @scheme[lambda]
+ or @scheme[case-lambda].
+}
+
+@defproc[(closure->deserialize-name [c closure?])
+ symbol?]{
+ Extracts the unique tag of a closure @scheme[c]
+}
+
+These are difficult to use directly, so @filepath{private/define-closure.ss}
+defines a helper form:
+
+@subsection[#:style 'hidden]{Define Closure}
+@defmodule[web-server/private/define-closure]
+
+@defform[(define-closure tag formals (free-vars ...) body)]{
+ Defines a closure, constructed with @scheme[make-tag] that accepts
+ @scheme[freevars ...], that when invoked with @scheme[formals]
+ executes @scheme[body].
+}
+
+@; XXX Example
+
+@; ------------------------------------------------------------
+@section[#:tag "cache-table.ss"]{Cache Table}
+@require[(for-label web-server/private/cache-table)]
+
+@defmodule[web-server/private/cache-table]
+
+@filepath{private/cache-table.ss} provides a set of caching hash table
+functions.
+
+@defproc[(make-cache-table)
+ cache-table?]{
+ Constructs a cache-table.
+}
+
+@defproc[(cache-table-lookup! [ct cache-table?]
+ [id symbol?]
+ [mk (-> any/c)])
+ any/c]{
+ Looks up @scheme[id] in @scheme[ct]. If it is not present, then @scheme[mk] is
+ called to construct the value and add it to @scheme[ct].
+}
+
+@defproc[(cache-table-clear! [ct cache-table?])
+ void?]{
+ Clears all entries in @scheme[ct].
+}
+
+@defproc[(cache-table? [v any/c])
+ boolean?]{
+ Determines if @scheme[v] is a cache table.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "mime-types.ss"]{MIME Types}
+@require[(for-label web-server/private/mime-types)]
+
+@defmodule[web-server/private/mime-types]
+
+@filepath{private/mime-types.ss} provides function for dealing with @filepath{mime.types}
+files.
+
+@defproc[(read-mime-types [p path?])
+ (hash-table/c symbol? bytes?)]{
+ Reads the @filepath{mime.types} file from @scheme[p] and constructs a
+ hash table mapping extensions to MIME types.
+}
+
+@defproc[(make-path->mime-type [p path?])
+ (path? . -> . bytes?)]{
+ Uses a @scheme[read-mime-types] with @scheme[p] and constructs a
+ function from paths to their MIME type.
+}
+
+@; XXX Rename mod-map.ss
+@; ------------------------------------------------------------
+@section[#:tag "mod-map.ss"]{Serialization Utilities}
+@require[(for-label web-server/private/mod-map)]
+
+@defmodule[web-server/private/mod-map]
+
+The @schememodname[scheme/serialize] library provides the
+functionality of serializing values. @filepath{private/mod-map.ss}
+compresses the serialized representation.
+
+@defproc[(compress-serial [sv serialized-value?])
+ compressed-serialized-value?]{
+ Collapses multiple occurrences of the same module in the module
+ map of the serialized representation, @scheme[sv].
+}
+
+@defproc[(decompress-serial [csv compressed-serialized-value?])
+ serialized-value?]{
+ Expands multiple occurrences of the same module in the module
+ map of the compressed serialized representation, @scheme[csv].
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "url-param.ss"]{URL Param}
+@require[(for-label web-server/private/url-param)]
+
+@defmodule[web-server/private/url-param]
+
+The @web-server needs to encode information in URLs. If this data
+is stored in the query string, than it will be overridden by browsers that
+make GET requests to those URLs with more query data. So, it must be encoded
+in URL params. @filepath{private/url-param.ss} provides functions for helping
+with this process.
+
+@defproc[(insert-param [u url?]
+ [k string?]
+ [v string?])
+ url?]{
+ Associates @scheme[k] with @scheme[v] in the final URL param of @scheme[u],
+ overwritting any current binding for @scheme[k].
+}
+
+@defproc[(extract-param [u url?]
+ [k string?])
+ (or/c string? false/c)]{
+ Extracts the string associated with @scheme[k] in the final URL param of
+ @scheme[u], if there is one, returning @scheme[#f] otherwise.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "util.ss"]{Miscellaneous Utilities}
+@require[(for-label web-server/private/util)]
+
+@defmodule[web-server/private/util]
+
+There are a number of other miscellaneous utilities the @web-server
+needs. They are provided by @filepath{private/util.ss}.
+
+@subsection{Contracts}
+@defthing[port-number? contract?]{Equivalent to @scheme[(between/c 1 65535)].}
+@defthing[path-element? contract?]{Equivalent to @scheme[(or/c string? path? (symbols 'up 'same))].}
+
+@subsection{Lists}
+@defproc[(list-prefix? [l list?]
+ [r list?])
+ boolean?]{
+ True if @scheme[l] is a prefix of @scheme[r].
+}
+
+@subsection{URLs}
+
+@defproc[(url-replace-path [proc ((listof path/param?) . -> . (listof path/param?))]
+ [u url?])
+ url?]{
+ Replaces the URL path of @scheme[u] with @scheme[proc] of the former path.
+}
+
+@; XXX Remove use or take url?
+@defproc[(url-path->string [url-path (listof path/param?)])
+ string?]{
+ Formats @scheme[url-path] as a string with @scheme["/"] as a delimiter
+ and no params.
+}
+
+@subsection{Paths}
+@defproc[(explode-path* [p path?])
+ (listof path-element?)]{
+ Like @scheme[normalize-path], but does not resolve symlinks.
+}
+
+@defproc[(path-without-base [base path?]
+ [p path?])
+ (listof path-element?)]{
+ Returns, as a list, the portion of @scheme[p] after @scheme[base],
+ assuming @scheme[base] is a prefix of @scheme[p].
+}
+
+@defproc[(directory-part [p path?])
+ path?]{
+ Returns the directory part of @scheme[p], returning @scheme[(current-directory)]
+ if it is relative.
+}
+
+@defproc[(build-path-unless-absolute [base path-string?]
+ [p path-string?])
+ path?]{
+ Prepends @scheme[base] to @scheme[p], unless @scheme[p] is absolute.
+}
+
+@defproc[(strip-prefix-ups [p (listof path-element?)])
+ (listof path-element?)]{
+ Removes all the prefix @scheme[".."]s from @scheme[p].
+}
+
+@subsection{Exceptions}
+
+@defproc[(pretty-print-invalid-xexpr [exn exn:invalid-xexpr?]
+ [v any/c])
+ void]{
+ Prints @scheme[v] as if it were almost an X-expression highlighting the error
+ according to @scheme[exn].
+}
+
+@; XXX Remove
+@defproc[(network-error [s symbol?]
+ [fmt string?]
+ [v any/c] ...)
+ void]{
+ Like @scheme[error], but throws a @scheme[exn:fail:network].
+}
+
+@defproc[(exn->string [exn (or/c exn? any/c)])
+ string?]{
+ Formats @scheme[exn] with @scheme[(error-display-handler)] as a string.
+}
+
+@subsection{Strings}
+
+@defproc[(lowercase-symbol! [sb (or/c string? bytes?)])
+ symbol?]{
+ Returns @scheme[sb] as a lowercase symbol.
+}
+
+@defproc[(read/string [s string?])
+ serializable?]{
+ @scheme[read]s a value from @scheme[s] and returns it.
+}
+
+@defproc[(write/string [v serializable?])
+ string?]{
+ @scheme[write]s @scheme[v] to a string and returns it.
+}
diff --git a/collects/web-server/scribblings/running.scrbl b/collects/web-server/scribblings/running.scrbl
new file mode 100644
index 0000000000..262f88f318
--- /dev/null
+++ b/collects/web-server/scribblings/running.scrbl
@@ -0,0 +1,80 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "run.ss"
+ #:style 'toc]{Running the Web Server}
+
+There are a number of ways to run the Web Server. The two primary ways
+are through a command-line tool or through a function call.
+
+@local-table-of-contents[]
+
+@; ------------------------------------------------------------
+@section[#:tag "command-line-tools"]{Command-line Tools}
+
+One command-line utility is provided with the @|web-server|:
+
+@commandline{plt-web-server [-f -p -a ]}
+
+The optional file-name argument specifies the path to a
+@scheme[configuration-table] S-expression (see
+@secref["configuration-table.ss"].) If this is not provided, the
+default configuration shipped with the server is used. The optional
+port and ip-address arguments override the corresponding portions of
+the @scheme[configuration-table].
+
+The @scheme[configuration-table] is given to @scheme[configuration-table->web-config\@]
+and used to construct a @scheme[web-config^] unit,
+and is linked with the @scheme[web-server\@] unit. The resulting unit is invoked, and
+the server runs until the process is killed.
+
+To run the web server with MrEd, use
+
+@commandline{mred -l- web-server/gui [-f -p -a ]}
+
+@; ------------------------------------------------------------
+@section[#:tag "web-server.ss"]{Functional}
+@require[(for-label web-server/web-server)]
+
+@defmodule[web-server/web-server]
+
+@filepath{web-server.ss} provides a number of functions for easing embedding
+of the @web-server in other applications, or loading a custom
+dispatcher. See @filepath{run.ss} for an example of such a script.
+
+@defproc[(serve [#:dispatch dispatch dispatcher?]
+ [#:tcp\@ tcp\@ tcp-unit^ raw:tcp\@]
+ [#:port port integer? 80]
+ [#:listen-ip listen-ip (or/c string? false/c) #f]
+ [#:max-waiting max-waiting integer? 40]
+ [#:initial-connection-timeout initial-connection-timeout integer? 60])
+ (-> void)]{
+ Constructs an appropriate @scheme[dispatch-config^], invokes the @scheme[dispatch-server\@],
+ and calls its @scheme[serve] function.
+}
+
+@defproc[(serve/ports [#:dispatch dispatch dispatcher?]
+ [#:tcp\@ tcp\@ tcp-unit^ raw:tcp\@]
+ [#:ports ports (listof integer?) (list 80)]
+ [#:listen-ip listen-ip (or/c string? false/c) #f]
+ [#:max-waiting max-waiting integer? 40]
+ [#:initial-connection-timeout initial-connection-timeout integer? 60])
+ (-> void)]{
+ Calls @scheme[serve] multiple times, once for each @scheme[port], and returns
+ a function that shuts down all of the server instances.
+}
+
+@defproc[(serve/ips+ports [#:dispatch dispatch dispatcher?]
+ [#:tcp\@ tcp\@ tcp-unit^ raw:tcp\@]
+ [#:ips+ports ips+ports (listof (cons/c (or/c string? false/c) (listof integer?))) (list (cons #f (list 80)))]
+ [#:max-waiting max-waiting integer? 40]
+ [#:initial-connection-timeout initial-connection-timeout integer? 60])
+ (-> void)]{
+ Calls @scheme[serve/ports] multiple times, once for each @scheme[ip], and returns
+ a function that shuts down all of the server instances.
+}
+
+@defproc[(do-not-return) void]{
+ This function does not return. If you are writing a script to load the @web-server
+ you are likely to want to call this functions at the end of your script.
+}
diff --git a/collects/web-server/scribblings/servlet-env.scrbl b/collects/web-server/scribblings/servlet-env.scrbl
new file mode 100644
index 0000000000..40f3d853ca
--- /dev/null
+++ b/collects/web-server/scribblings/servlet-env.scrbl
@@ -0,0 +1,31 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "servlet-env.ss"
+ #:style 'toc]{Environment}
+@require[(for-label web-server/servlet-env)]
+
+@defmodule[web-server/servlet-env]
+
+The @web-server provides a means of running Scheme servlets
+from within DrScheme, or any other REPL.
+
+@filepath{servlet-env.ss} provides the servlet API from @filepath{servlet.ss}
+as well as the following:
+
+@defthing[send-url (parameter/c ([url string?] [separate-window? boolean?] . -> . void))]{
+ Should open @scheme[url]. In another window if @scheme[separate-window?] is true.
+ By default this is from @scheme[(lib "sendurl.ss" "net")].
+}
+
+@defform*[[(on-web servlet-expr)
+ (on-web port servlet-expr)]]{
+
+ The first form expands to @scheme[(on-web 8000 servlet-expr)].
+
+ Constructs a small servlet, where the body of the @scheme[start] procedure is
+ @scheme[servlet-expr], runs the @web-server on port @scheme[port], and calls
+ @scheme[send-url] with a URL for the constructed servlet. The call blocks until the
+ servlet finishes its computation, i.e. @scheme[servlet-expr] is evaluated, and
+ returns its result. @scheme[servlet-expr] may use the entire Scheme servlet API.
+}
diff --git a/collects/web-server/scribblings/servlet.scrbl b/collects/web-server/scribblings/servlet.scrbl
new file mode 100644
index 0000000000..ceefac877c
--- /dev/null
+++ b/collects/web-server/scribblings/servlet.scrbl
@@ -0,0 +1,429 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "servlet"
+ #:style 'toc]{Scheme Servlets}
+
+The @web-server allows servlets to be written in Scheme. It
+provides the supporting API, described below, for the construction
+of these servlets. This API is provided by @filepath{servlet.ss}.
+
+@local-table-of-contents[]
+
+@; ------------------------------------------------------------
+@section[#:tag "module-servlets"]{Definition}
+@require[(for-label "dummy-servlet.ss")] ; to give a binding context
+
+@declare-exporting[web-server/scribblings/dummy-servlet]
+
+A @defterm{servlet} is a module that provides the following:
+
+@defthing[interface-version (one-of/c 'v1 'v2)]{
+ A symbol indicating the servlet interface the servlet conforms
+ to. This influences the other provided identifiers.
+}
+
+@defthing[timeout integer?]{
+ Only if @scheme[interface-version] is @scheme['v1].
+
+ This number is used as the @scheme[continuation-timeout] argument to
+ a timeout-based continuation manager used for this servlet. (See
+ @secref["timeouts.ss"].) (i.e., you do not have a choice of the manager
+ for this servlet and will be given a timeout-based manager.)
+}
+
+@defthing[manager manager?]{
+ Only if @scheme[interface-version] is @scheme['v2].
+
+ The manager for the continuations of this servlet.
+}
+
+@defproc[(start [initial-request request?])
+ response?]{
+ This function is called when an instance of this servlet is started.
+ The argument is the HTTP request that initiated the instance.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "servlet-structs.ss"]{Contracts}
+@require[(for-label web-server/servlet/servlet-structs)]
+
+@defmodule[web-server/servlet/servlet-structs]
+
+@filepath{servlet/servlet-structs.ss} provides a number of contracts
+for use in servlets.
+
+@defthing[k-url? contract?]{Equivalent to @scheme[string?].}
+
+@defthing[response-generator? contract?]{Equivalent to @scheme[(k-url? . -> . response?)].}
+
+@defthing[url-transform? contract?]{Equivalent to @scheme[(k-url? . -> . k-url?)].}
+
+@defthing[expiration-handler? contract?]{Equivalent to @scheme[(or/c false/c (request? . -> . response?))].}
+
+@defthing[embed/url? contract?]{Equivalent to @scheme[(((request? . -> . any/c)) (expiration-handler?) . opt-> . string?)].}
+
+@; ------------------------------------------------------------
+@section[#:tag "request-structs.ss"]{HTTP Requests}
+@require[(for-label web-server/private/request-structs)]
+
+@defmodule[web-server/private/request-structs]
+
+@; XXX Create http sub-directory
+@; XXX Have this include read-request and write-response
+@filepath{private/request-structs.ss} provides a number of structures and functions
+related to HTTP request data structures.
+
+@defstruct[header ([field bytes?]
+ [value bytes?])]{
+ Represents a header of @scheme[field] to @scheme[value].
+}
+
+@defproc[(headers-assq [id bytes?] [heads (listof header?)])
+ (or/c false/c header?)]{
+ Returns the header with a field equal to @scheme[id] from @scheme[heads] or @scheme[#f].
+}
+
+@defproc[(headers-assq* [id bytes?] [heads (listof header?)])
+ (or/c false/c header?)]{
+ Returns the header with a field case-insensitively equal to @scheme[id] from @scheme[heads] or @scheme[#f].
+}
+
+@defstruct[binding ([id bytes?])]{Represents a binding of @scheme[id].}
+
+@defstruct[(binding:form binding) ([value bytes?])]{
+ Represents a form binding of @scheme[id] to @scheme[value].
+}
+
+@defstruct[(binding:file binding) ([filename bytes?]
+ [content bytes?])]{
+ Represents the uploading of the file @scheme[filename] with the id @scheme[id]
+ and the content @scheme[content].
+}
+
+@defproc[(bindings-assq [binds (listof binding?)])
+ (or/c false/c binding?)]{
+ Returns the binding with an id equal to @scheme[id] from @scheme[binds] or @scheme[#f].
+}
+
+@defstruct[request ([method symbol?]
+ [uri url?]
+ [headers/raw (listof header?)]
+ [bindings/raw (listof binding?)]
+ [post-data/raw (or/c false/c bytes?)]
+ [host-ip string?]
+ [host-port number?]
+ [client-ip string?])]{
+ An HTTP @scheme[method] request to @scheme[uri] from @scheme[client-ip]
+ to the server at @scheme[host-ip]:@scheme[host-port] with @scheme[headers/raw]
+ headers, @scheme[bindings/raw] GET and POST queries and @scheme[post-data/raw]
+ POST data.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "bindings.ss"]{Request Bindings}
+@require[(for-label web-server/servlet/bindings)]
+
+@defmodule[web-server/servlet/bindings]
+
+@filepath{servlet/bindings.ss} provides a number of helper functions
+for accessing request bindings.
+
+@defproc[(request-bindings [req request?])
+ (listof (or/c (cons/c symbol? string?)
+ (cons/c symbol? bytes?)))]{
+ Translates the @scheme[request-bindings/raw] of @scheme[req] by
+ interpreting @scheme[bytes?] as @scheme[string?]s, except in the case
+ of @scheme[binding:file] bindings, which are left as is. Ids are then
+ translated into lowercase symbols.
+}
+
+@defproc[(request-headers [req request?])
+ (listof (cons/c symbol? string?))]{
+ Translates the @scheme[request-headers/raw] of @scheme[req] by
+ interpreting @scheme[bytes?] as @scheme[string?]s. Ids are then
+ translated into lowercase symbols.
+}
+
+@defproc[(extract-binding/single [id symbol?]
+ [binds (listof (cons/c symbol? string?))])
+ string?]{
+ Returns the single binding associated with @scheme[id] in the a-list @scheme[binds]
+ if there is exactly one binding. Otherwise raises @scheme[exn:fail].
+}
+
+@defproc[(extract-bindings [id symbol?]
+ [binds (listof (cons/c symbol? string?))])
+ (listof string?)]{
+ Returns a list of all the bindings of @scheme[id] in the a-list @scheme[binds].
+}
+
+@defproc[(exists-binding? [id symbol?]
+ [binds (listof (cons/c symbol? string))])
+ boolean?]{
+ Returns @scheme[#t] if @scheme[binds] contains a binding for @scheme[id].
+ Otherwise, @scheme[#f].
+}
+
+These functions, while convenient, could introduce subtle bugs into your
+application. Examples: that they are case-insensitive could introduce
+a bug; if the data submitted is not in UTF-8 format, then the conversion
+to a string will fail; if an attacked submits a form field as if it were
+a file, when it is not, then the @scheme[request-bindings] will hold a
+@scheme[bytes?] object and your program will error; and, for file uploads
+you lose the filename.
+
+@; ------------------------------------------------------------
+@section[#:tag "response-structs.ss"]{HTTP Responses}
+@require[(for-label web-server/private/response-structs)]
+
+@defmodule[web-server/private/response-structs]
+
+@filepath{private/response-structs.ss} provides structures and functions related to
+HTTP responses.
+
+@; XXX Only use bytes
+@defstruct[response/basic
+ ([code number?]
+ [message string?]
+ [seconds number?]
+ [mime bytes?]
+ [headers (listof header?)])]{
+ A basic HTTP response containing no body. @scheme[code] is the response code,
+ @scheme[message] the message, @scheme[seconds] the generation time, @scheme[mime]
+ the MIME type of the file, and @scheme[extras] are the extra headers, in addition
+ to those produced by the server.
+}
+
+@; XXX Rename string? option
+@defstruct[(response/full response/basic)
+ ([body (listof (or/c string? bytes?))])]{
+ As with @scheme[response/basic], except with @scheme[body] as the response
+ body.
+}
+
+@defstruct[(response/incremental response/basic)
+ ([generator ((() (listof (or/c bytes? string?)) . ->* . any) . -> . any)])]{
+ As with @scheme[response/basic], except with @scheme[generator] as a function that is
+ called to generate the response body, by being given an @scheme[output-response] function
+ that outputs the content it is called with.
+}
+
+@defproc[(response? [v any/c])
+ boolean?]{
+ Checks if @scheme[v] is a valid response. A response is either:
+ @itemize[
+ @item{A @scheme[response/basic] structure.}
+ @item{A value matching the contract @scheme[(cons/c (or/c bytes? string?) (listof (or/c bytes? string?)))].}
+ @item{A value matching @scheme[xexpr?].}
+ ]
+}
+
+@defthing[TEXT/HTML-MIME-TYPE bytes?]{Equivalent to @scheme[#"text/html; charset=utf-8"].}
+
+@warning{If you include a Content-Length header in a response that is inaccurate, there WILL be an error in
+transmission that the server will not catch.}
+
+@; ------------------------------------------------------------
+@section[#:tag "web.ss"]{Web}
+@require[(for-label web-server/servlet/web)]
+
+@defmodule[web-server/servlet/web]{The
+@schememodname[web-server/servlet/web] library provides the primary
+functions of interest for the servlet developer.}
+
+@defproc[(send/back [response response?])
+ void?]{
+ Sends @scheme[response] to the client.
+}
+
+@defthing[current-servlet-continuation-expiration-handler parameter?]{
+ Holds the @scheme[expiration-handler?] to be used when a continuation
+ captured in this context is expired, then looked up.
+}
+
+@defproc[(send/suspend [make-response response-generator?]
+ [exp expiration-handler? (current-servlet-continuation-expiration-handler)])
+ request?]{
+ Captures the current continuation, stores it with @scheme[exp] as the expiration
+ handler, and binds it to a URL. @scheme[make-response] is called with this URL and
+ is expected to generate a @scheme[response?], which is sent to the client. If the
+ continuation URL is invoked, the captured continuation is invoked and the request is
+ returned from this call to @scheme[send/suspend].
+}
+
+@defproc[(continuation-url? [u url?])
+ (or/c false/c (list/c number? number? number?))]{
+ Checks if @scheme[u] is a URL that refers to a continuation, if so
+ returns the instance id, continuation id, and nonce.
+}
+
+@; XXX Move
+@defproc[(adjust-timeout! [t number?])
+ void?]{
+ Calls the servlet's manager's @scheme[adjust-timeout!] function.
+}
+
+@defproc[(clear-continuation-table!)
+ void?]{
+ Calls the servlet's manager's @scheme[clear-continuation-table!] function.
+}
+
+@defproc[(send/forward [make-response response-generator?]
+ [exp expiration-handler? (current-servlet-continuation-expiration-handler)])
+ request?]{
+ Calls @scheme[clear-continuation-table!], then @scheme[send/suspend].
+}
+
+@defproc[(send/finish [response response?])
+ void?]{
+ Calls @scheme[clear-continuation-table!], then @scheme[send/back].
+}
+
+@defproc[(send/suspend/dispatch [make-response (embed/url? . -> . response?)])
+ any/c]{
+ Calls @scheme[make-response] with a function that, when called with a procedure from
+ @scheme[request?] to @scheme[any/c] will generate a URL, that when invoked will call
+ the function with the @scheme[request?] object and return the result to the caller of
+ @scheme[send/suspend/dispatch].
+}
+
+@defproc[(redirect/get)
+ request?]{
+ Calls @scheme[send/suspend] with @scheme[redirect-to].
+}
+
+@defproc[(redirect/get/forget)
+ request?]{
+ Calls @scheme[send/forward] with @scheme[redirect-to].
+}
+
+@; XXX Remove
+@defproc[(embed-ids [ids (list/c number? number? number?)]
+ [u url?])
+ string?]{
+ Creates a @scheme[continuation-url?].
+}
+
+@; XXX Remove
+@defthing[current-url-transform parameter?]{
+ Holds a @scheme[url-transform?] function that is called by
+ @scheme[send/suspend] to transform the URLs it generates.
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "helpers.ss"]{Helpers}
+@require[(for-label web-server/servlet/helpers)]
+
+@defmodule[web-server/servlet/helpers]
+
+@filepath{servlet/helpers.ss} provides functions built on
+@filepath{servlet/web.ss} that are useful in many servlets.
+
+@; XXX Move into http/response.ss
+@defproc[(redirect-to [uri string?]
+ [perm/temp redirection-status? temporarily]
+ [#:headers headers (listof header?) (list)])
+ response?]{
+ Generates an HTTP response that redirects the browser to @scheme[uri],
+ while including the @scheme[headers] in the response.
+}
+
+@defproc[(redirection-status? [v any/c])
+ boolean?]{
+ Determines if @scheme[v] is one of the following values.
+}
+
+@defthing[permanently redirection-status?]{A @scheme[redirection-status?] for permanent redirections.}
+
+@defthing[temporarily redirection-status?]{A @scheme[redirection-status?] for temporary redirections.}
+
+@defthing[see-other redirection-status?]{A @scheme[redirection-status?] for "see-other" redirections.}
+
+@defproc[(with-errors-to-browser [send/finish-or-back (response? . -> . void?)]
+ [thunk (-> any)])
+ any]{
+ Calls @scheme[thunk] with an exception handler that generates an HTML error page
+ and calls @scheme[send/finish-or-back].
+}
+
+@; XXX Depreciate
+@; ------------------------------------------------------------
+@section[#:tag "servlet-url.ss"]{Servlet URLs}
+@require[(for-label web-server/servlet/servlet-url)]
+
+@defmodule[web-server/servlet/servlet-url]
+
+@filepath{servlet/servlet-url.ss} provides functions that might be useful to you.
+They may eventually provided by another module.
+
+@defproc[(request->servlet-url (req request?))
+ servlet-url?]{Generates a value to be passed to the next function.}
+
+@defproc[(servlet-url->url-string/no-continuation [su servlet-url?])
+ string?]{
+ Returns a URL string without the continuation information in the URL
+ that went into @scheme[su]
+}
+
+@; XXX Support Digest
+@; ------------------------------------------------------------
+@section[#:tag "basic-auth.ss"]{Basic Authentication}
+@require[(for-label web-server/servlet/basic-auth)]
+
+@defmodule[web-server/servlet/basic-auth]
+
+@filepath{servlet/basic-auth.ss} provides a function for helping with
+implementation of HTTP Basic Authentication.
+
+@defproc[(extract-user-pass [heads (listof header?)])
+ (or/c false/c (cons/c bytes? bytes?))]{
+ Returns a pair of the username and password from the authentication
+ header in @scheme[heads] if they are present, or @scheme[#f]
+}
+
+@; ------------------------------------------------------------
+@section[#:tag "web-cells.ss"]{Web Cells}
+@require[(for-label web-server/servlet/web-cells)]
+
+@defmodule[web-server/servlet/web-cells]{The
+@schememodname[web-server/servlet/web-cells] library provides the
+interface to web cells.}
+
+A web cell is a kind of state defined relative to the @defterm{frame tree}.
+The frame-tree is a mirror of the user's browsing session. Every time a
+continuation is invoked, a new frame (called the @defterm{current frame}) is
+created as a child of the current frame when the continuation was captured.
+
+You should use web cells if you want an effect to be encapsulated in all
+interactions linked from (in a transitive sense) the HTTP response being
+generated. For more information on their semantics, consult the paper
+@href-link["http://www.cs.brown.edu/~sk/Publications/Papers/Published/mk-int-safe-state-web/"
+"\"Interaction-Safe State for the Web\""].
+
+@; XXX Document with-frame and with-frame-after?
+
+@defproc[(web-cell? [v any/c])
+ boolean?]{
+ Determines if @scheme[v] is a web-cell.
+}
+
+@defproc[(make-web-cell [v any/c])
+ web-cell?]{
+ Creates a web-cell with a default value of @scheme[v].
+}
+
+@defproc[(web-cell-ref [wc web-cell?])
+ any/c]{
+ Looks up the value of @scheme[wc] found in the nearest
+ frame.
+}
+
+@defproc[(web-cell-shadow [wc web-cell?]
+ [v any/c])
+ void]{
+ Binds @scheme[wc] to @scheme[v] in the current frame, shadowing any
+ other bindings to @scheme[wc] in the current frame.
+}
+
+@include-section["servlet-env.scrbl"]
diff --git a/collects/web-server/scribblings/web-config-unit.scrbl b/collects/web-server/scribblings/web-config-unit.scrbl
new file mode 100644
index 0000000000..d69f043f81
--- /dev/null
+++ b/collects/web-server/scribblings/web-config-unit.scrbl
@@ -0,0 +1,68 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "web-config-unit.ss"]{Web Config Unit}
+@require[(for-label web-server/web-config-unit)]
+@require[(for-label web-server/web-config-sig)]
+
+The @web-server offers a unit-based approach to configuring the server.
+
+@section{Configuration Signature}
+
+@defmodule[web-server/web-config-sig]
+
+@defsignature[web-config^ ()]{
+
+@signature-desc{
+Provides contains the following identifiers.
+}
+
+@defthing[max-waiting integer?]{
+ Passed to @scheme[tcp-accept].
+}
+
+@defthing[virtual-hosts (listof (cons/c string? host-table?))]{
+ Contains the configuration of individual virtual hosts.
+}
+
+@defthing[scripts (box/c (cache-table? path? servlet?))]{
+ Contains initially loaded servlets.
+}
+
+@defthing[initial-connection-timeout integer?]{
+ Specifies the initial timeout given to a connection.
+}
+
+@defthing[port port-number?]{
+ Specifies the port to serve HTTP on.
+}
+
+@defthing[listen-ip string?]{
+ Passed to @scheme[tcp-accept].
+}
+
+@defthing[make-servlet-namespace make-servlet-namespace?]{
+ Passed to @scheme[servlets:make].
+}
+}
+
+@section{Configuration Units}
+
+@defmodule[web-server/web-config-unit]
+
+@defproc[(configuration-table->web-config\@ [path path?]
+ [#:port port (or/c false/c port-number?) #f]
+ [#:listen-ip listen-ip (or/c false/c string?) #f]
+ [#:make-servlet-namespace make-servlet-namespace make-servlet-namespace? (make-make-servlet-namespace)])
+ (unit? web-config^)]{
+ Reads the S-expression at @scheme[path] and calls @scheme[configuration-table-sexpr->web-config\@] appropriately.
+}
+
+@defproc[(configuration-table-sexpr->web-config\@ [sexpr list?]
+ [#:web-server-root web-server-root path? (directory-part default-configuration-table-path)]
+ [#:port port (or/c false/c port-number?) #f]
+ [#:listen-ip listen-ip (or/c false/c string?) #f]
+ [#:make-servlet-namespace make-servlet-namespace make-servlet-namespace? (make-make-servlet-namespace)])
+ (unit? web-config^)]{
+ Parses @scheme[sexpr] as a configuration-table and constructs a @scheme[web-config^] unit.
+}
diff --git a/collects/web-server/scribblings/web-server-unit.scrbl b/collects/web-server/scribblings/web-server-unit.scrbl
new file mode 100644
index 0000000000..11a2d7e322
--- /dev/null
+++ b/collects/web-server/scribblings/web-server-unit.scrbl
@@ -0,0 +1,52 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "web-server-unit.ss"]{Web Server Unit}
+@require[(for-label web-server/web-server-sig)]
+@require[(for-label web-server/web-server-unit)]
+
+The @web-server offers a unit-based approach to running the server.
+
+@section{Signature}
+
+@defmodule[web-server/web-server-sig]
+
+@defsignature[web-server^ ()]{
+
+ @defproc[(serve) (-> void)]{
+ Runs the server and returns a procedure that shuts down the server.
+ }
+
+ @defproc[(serve-ports [ip input-port?]
+ [op output-port?])
+ void]{
+ Serves a single connection represented by the ports @scheme[ip] and
+ @scheme[op].
+ }
+}
+
+@section{Unit}
+
+@defmodule[web-server/web-server-unit]
+
+@defthing[web-server\@ (unit/c (web-config^ tcp^)
+ (web-server^))]{
+
+Uses the @scheme[web-config^] to construct a @scheme[dispatcher?]
+function that sets up one virtual host dispatcher, for each virtual
+host in the @scheme[web-config^], that sequences the following
+operations:
+
+@itemize[
+ @item{Logs the incoming request with the given format to the given file}
+ @item{Performs HTTP Basic Authentication with the given password file}
+ @item{Allows the @scheme["/conf/refresh-passwords"] URL to refresh the password file.}
+ @item{Allows the @scheme["/conf/collect-garbage"] URL to call the garbage collector.}
+ @item{Allows the @scheme["/conf/refresh-servlets"] URL to refresh the servlets cache.}
+ @item{Execute servlets under the @scheme["/servlets/"] URL in the given servlet root directory.}
+ @item{Serves files under the @scheme["/"] URL in the given htdocs directory.}
+]
+
+Using this @scheme[dispatcher?], it loads a dispatching server that provides @scheme[serve]
+and @scheme[serve-ports] functions that operate as expected.
+}
diff --git a/collects/web-server/scribblings/web-server.scrbl b/collects/web-server/scribblings/web-server.scrbl
new file mode 100644
index 0000000000..748609e460
--- /dev/null
+++ b/collects/web-server/scribblings/web-server.scrbl
@@ -0,0 +1,38 @@
+#lang scribble/doc
+@require["web-server.ss"]
+
+@title[#:tag "web-server-ref"]{@bold{Web Server}: Reference Manual}
+@author{Jay McCarthy (jay@"@"plt-scheme.org)}
+
+The @web-server collection provides libraries that can be used to
+develop Web applications in Scheme.
+
+@table-of-contents[]
+
+@include-section["running.scrbl"]
+
+@include-section["servlet.scrbl"]
+@include-section["lang.scrbl"]
+
+@include-section["configuration.scrbl"]
+@include-section["dispatchers.scrbl"]
+@include-section["web-config-unit.scrbl"]
+@include-section["web-server-unit.scrbl"]
+@include-section["managers.scrbl"]
+
+@include-section["private.scrbl"]
+
+@include-section["faq.scrbl"]
+
+@; ------------------------------------------------------------
+@section[#:tag "ack"]{Acknowledgements}
+
+We thank Matthew Flatt for his superlative work on MzScheme.
+We thank the previous maintainers of the @web-server : Paul T. Graunke, Mike Burns, and Greg Pettyjohn
+Numerous people have
+provided invaluable feedback on the server, including Eli Barzilay, Ryan Culpepper, Robby
+Findler, Dan Licata, Matt Jadud, Jacob Matthews, Matthias Radestock, Andrey Skylar,
+Michael Sperber, Dave Tucker, Anton van Straaten, and Noel Welsh. We also thank the
+many other PLT Scheme users who have exercised the server and offered critiques.
+
+@index-section[]
diff --git a/collects/web-server/scribblings/web-server.ss b/collects/web-server/scribblings/web-server.ss
new file mode 100644
index 0000000000..249f60e65b
--- /dev/null
+++ b/collects/web-server/scribblings/web-server.ss
@@ -0,0 +1,31 @@
+#lang scheme/base
+(require (lib "manual.ss" "scribble")
+ (lib "eval.ss" "scribble")
+ (for-label scheme/base
+ scheme/contract
+ scheme/unit))
+
+(define web-server "Web Server")
+
+; XXX Format better
+(define (author x)
+ (elem (hspace 4)
+ (bold x)))
+
+; XXX Format better
+(define (warning . x)
+ (apply elem "Warning:" x))
+
+; XXX Actually display link
+(define (href-link url label)
+ (elem label " (" url ")"))
+
+(provide (all-from-out (lib "manual.ss" "scribble"))
+ (all-from-out (lib "eval.ss" "scribble"))
+ (for-label (all-from-out scheme/base
+ scheme/contract
+ scheme/unit))
+ web-server
+ author
+ warning
+ href-link)
\ No newline at end of file