Replace $letrec with $rec
[bpt/guile.git] / doc / ref / web.texi
index ef2c7cd..2311b82 100644 (file)
@@ -1,6 +1,6 @@
 @c -*-texinfo-*-
 @c This is part of the GNU Guile Reference Manual.
-@c Copyright (C) 2010 Free Software Foundation, Inc.
+@c Copyright (C) 2010, 2011, 2012, 2013, 2015 Free Software Foundation, Inc.
 @c See the file guile.texi for copying conditions.
 
 @node Web
@@ -10,7 +10,7 @@
 @cindex HTTP
 
 It has always been possible to connect computers together and share
-information between them, but the rise of the World-Wide Web over the
+information between them, but the rise of the World Wide Web over the
 last couple of decades has made it much easier to do so.  The result is
 a richly connected network of computation, in which Guile forms a part.
 
@@ -37,8 +37,10 @@ back.
 * URIs::                        Universal Resource Identifiers.
 * HTTP::                        The Hyper-Text Transfer Protocol.
 * HTTP Headers::                How Guile represents specific header values.
+* Transfer Codings::            HTTP Transfer Codings.
 * Requests::                    HTTP requests.
 * Responses::                   HTTP responses.
+* Web Client::                  Accessing web resources over HTTP.
 * Web Server::                  Serving HTTP to the internet.
 * Web Examples::                How to use this thing.
 @end menu
@@ -59,8 +61,8 @@ valid dates.  Error handling for a number of basic cases, like invalid
 dates, occurs on the boundary in which we produce a SRFI 19 date record
 from other types, like strings.
 
-With regards to the web, data types are help in the two broad phases of
-HTTP messages: parsing and generation.
+With regards to the web, data types are helpful in the two broad phases
+of HTTP messages: parsing and generation.
 
 Consider a server, which has to parse a request, and produce a response.
 Guile will parse the request into an HTTP request object
@@ -125,8 +127,8 @@ basic idea is that HTML is either text, represented by a string, or an
 element, represented as a tagged list.  So @samp{foo} becomes
 @samp{"foo"}, and @samp{<b>foo</b>} becomes @samp{(b "foo")}.
 Attributes, if present, go in a tagged list headed by @samp{@@}, like
-@samp{(img (@@ (src "http://example.com/foo.png")))}.  @xref{sxml
-simple}, for more information.
+@samp{(img (@@ (src "http://example.com/foo.png")))}.  @xref{SXML}, for
+more information.
 
 The good thing about SXML is that HTML elements cannot be confused with
 text.  Let's make a new definition of @code{para}:
@@ -144,9 +146,9 @@ text.  Let's make a new definition of @code{para}:
 @end example
 
 So we see in the second example that HTML elements cannot be unwittingly
-introduced into the output.  However it is perfectly acceptable to pass
-SXML to @code{you-said}; in fact, that is the big advantage of SXML over
-everything-as-a-string.
+introduced into the output.  However it is now perfectly acceptable to
+pass SXML to @code{you-said}; in fact, that is the big advantage of SXML
+over everything-as-a-string.
 
 @example
 (sxml->xml (you-said (you-said "<Hi!>")))
@@ -175,23 +177,33 @@ URI := scheme ":" ["//" [userinfo "@@"] host [":" port]] path \
        [ "?" query ] [ "#" fragment ]
 @end example
 
-So, all URIs have a scheme and a path. Some URIs have a host, and some
-of those have ports and userinfo. Any URI might have a query part or a
-fragment.
+For example, in the URI, @indicateurl{http://www.gnu.org/help/}, the
+scheme is @code{http}, the host is @code{www.gnu.org}, the path is
+@code{/help/}, and there is no userinfo, port, query, or fragment.  All
+URIs have a scheme and a path (though the path might be empty).  Some
+URIs have a host, and some of those have ports and userinfo.  Any URI
+might have a query part or a fragment.
+
+There is also a ``URI-reference'' data type, which is the same as a URI
+but where the scheme is optional.  In this case, the scheme is taken to
+be relative to some other related URI.  A common use of URI references
+is when you want to be vague regarding the choice of HTTP or HTTPS --
+serving a web page referring to @code{/foo.css} will use HTTPS if loaded
+over HTTPS, or HTTP otherwise.
 
 Userinfo is something of an abstraction, as some legacy URI schemes
-allowed userinfo of the form @code{@var{username}:@var{passwd}}.
-Passwords don't belong in URIs, so the RFC does not want to condone
-this, but neither can it say that what is before the @code{@@} sign is
-just a username, so the RFC punts on the issue and calls it
+allowed userinfo of the form @code{@var{username}:@var{passwd}}.  But
+since passwords do not belong in URIs, the RFC does not want to condone
+this practice, so it calls anything before the @code{@@} sign
 @dfn{userinfo}.
 
-Also, strictly speaking, a URI with a fragment is a @dfn{URI
-reference}.  A fragment is typically not serialized when sending a URI
-over the wire; that is, it is not part of the identifier of a resource.
-It only identifies a part of a given resource.  But it's useful to have
-a field for it in the URI record itself, so we hope you will forgive the
-inconsistency.
+Properly speaking, a fragment is not part of a URI.  For example, when a
+web browser follows a link to @indicateurl{http://example.com/#foo}, it
+sends a request for @indicateurl{http://example.com/}, then looks in the
+resulting page for the fragment identified @code{foo} reference.  A
+fragment identifies a part of a resource, not the resource itself.  But
+it is useful to have a fragment field in the URI record itself, so we
+hope you will forgive the inconsistency.
 
 @example
 (use-modules (web uri))
@@ -201,74 +213,110 @@ The following procedures can be found in the @code{(web uri)}
 module. Load it into your Guile, using a form like the above, to have
 access to them.
 
-@defun build-uri scheme [#:userinfo] [#:host] [#:port] [#:path] [#:query] [#:fragment] [#:validate?]
-Construct a URI object. If @var{validate?} is true, also run some
-consistency checks to make sure that the constructed URI is valid.
-@end defun
-
-@defun uri? x
-@defunx uri-scheme uri
-@defunx uri-userinfo uri
-@defunx uri-host uri
-@defunx uri-port uri
-@defunx uri-path uri
-@defunx uri-query uri
-@defunx uri-fragment uri
-A predicate and field accessors for the URI record type.
-@end defun
-
-@defun declare-default-port! scheme port
-Declare a default port for the given URI scheme.
+@deffn {Scheme Procedure} build-uri scheme @
+       [#:userinfo=@code{#f}] [#:host=@code{#f}] [#:port=@code{#f}] @
+       [#:path=@code{""}] [#:query=@code{#f}] [#:fragment=@code{#f}] @
+       [#:validate?=@code{#t}]
+Construct a URI object.  @var{scheme} should be a symbol, @var{port}
+either a positive, exact integer or @code{#f}, and the rest of the
+fields are either strings or @code{#f}.  If @var{validate?} is true,
+also run some consistency checks to make sure that the constructed URI
+is valid.
+@end deffn
+
+@deffn {Scheme Procedure} build-uri-reference [#:scheme=@code{#f}]@
+       [#:userinfo=@code{#f}] [#:host=@code{#f}] [#:port=@code{#f}] @
+       [#:path=@code{""}] [#:query=@code{#f}] [#:fragment=@code{#f}] @
+       [#:validate?=@code{#t}]
+Like @code{build-uri}, but with an optional scheme.
+@end deffn
+
+In Guile, both URI and URI reference data types are represented in the
+same way, as URI objects.
+
+@deffn {Scheme Procedure} uri? obj
+@deffnx {Scheme Procedure} uri-scheme uri
+@deffnx {Scheme Procedure} uri-userinfo uri
+@deffnx {Scheme Procedure} uri-host uri
+@deffnx {Scheme Procedure} uri-port uri
+@deffnx {Scheme Procedure} uri-path uri
+@deffnx {Scheme Procedure} uri-query uri
+@deffnx {Scheme Procedure} uri-fragment uri
+A predicate and field accessors for the URI record type.  The URI scheme
+will be a symbol, or @code{#f} if the object is a URI reference but not
+a URI.  The port will be either a positive, exact integer or @code{#f},
+and the rest of the fields will be either strings or @code{#f} if not
+present.
+@end deffn
+
+@deffn {Scheme Procedure} string->uri string
+Parse @var{string} into a URI object.  Return @code{#f} if the string
+could not be parsed.
+@end deffn
 
-Default ports are for printing URI objects: a default port is not
-printed.
-@end defun
+@deffn {Scheme Procedure} string->uri-reference string
+Parse @var{string} into a URI object, while not requiring a scheme.
+Return @code{#f} if the string could not be parsed.
+@end deffn
 
-@defun parse-uri string
-Parse @var{string} into a URI object. Returns @code{#f} if the string
-could not be parsed.
-@end defun
+@deffn {Scheme Procedure} uri->string uri
+Serialize @var{uri} to a string.  If the URI has a port that is the
+default port for its scheme, the port is not included in the
+serialization.
+@end deffn
 
-@defun unparse-uri uri
-Serialize @var{uri} to a string.
-@end defun
+@deffn {Scheme Procedure} declare-default-port! scheme port
+Declare a default port for the given URI scheme.
+@end deffn
 
-@defun uri-decode str [#:charset]
-Percent-decode the given @var{str}, according to @var{charset}.
+@deffn {Scheme Procedure} uri-decode str [#:encoding=@code{"utf-8"}]
+Percent-decode the given @var{str}, according to @var{encoding}, which
+should be the name of a character encoding.
 
 Note that this function should not generally be applied to a full URI
-string. For paths, use split-and-decode-uri-path instead. For query
-strings, split the query on @code{&} and @code{=} boundaries, and decode
-the components separately.
-
-Note that percent-encoded strings encode @emph{bytes}, not characters.
-There is no guarantee that a given byte sequence is a valid string
-encoding. Therefore this routine may signal an error if the decoded
-bytes are not valid for the given encoding. Pass @code{#f} for
-@var{charset} if you want decoded bytes as a bytevector directly.
-@end defun
-
-@defun uri-encode str [#:charset] [#:unescaped-chars]
-Percent-encode any character not in @var{unescaped-chars}.
-
-Percent-encoding first writes out the given character to a bytevector
-within the given @var{charset}, then encodes each byte as
+string. For paths, use @code{split-and-decode-uri-path} instead. For
+query strings, split the query on @code{&} and @code{=} boundaries, and
+decode the components separately.
+
+Note also that percent-encoded strings encode @emph{bytes}, not
+characters.  There is no guarantee that a given byte sequence is a valid
+string encoding. Therefore this routine may signal an error if the
+decoded bytes are not valid for the given encoding. Pass @code{#f} for
+@var{encoding} if you want decoded bytes as a bytevector directly.
+@xref{Ports, @code{set-port-encoding!}}, for more information on
+character encodings.
+
+Returns a string of the decoded characters, or a bytevector if
+@var{encoding} was @code{#f}.
+@end deffn
+
+@deffn {Scheme Procedure} uri-encode str [#:encoding=@code{"utf-8"}] [#:unescaped-chars]
+Percent-encode any character not in the character set,
+@var{unescaped-chars}.
+
+The default character set includes alphanumerics from ASCII, as well as
+the special characters @samp{-}, @samp{.}, @samp{_}, and @samp{~}.  Any
+other character will be percent-encoded, by writing out the character to
+a bytevector within the given @var{encoding}, then encoding each byte as
 @code{%@var{HH}}, where @var{HH} is the hexadecimal representation of
 the byte.
-@end defun
+@end deffn
 
-@defun split-and-decode-uri-path path
+@deffn {Scheme Procedure} split-and-decode-uri-path path
 Split @var{path} into its components, and decode each component,
 removing empty components.
 
-For example, @code{"/foo/bar/"} decodes to the two-element list,
-@code{("foo" "bar")}.
-@end defun
+For example, @code{"/foo/bar%20baz/"} decodes to the two-element list,
+@code{("foo" "bar baz")}.
+@end deffn
 
-@defun encode-and-join-uri-path parts
+@deffn {Scheme Procedure} encode-and-join-uri-path parts
 URI-encode each element of @var{parts}, which should be a list of
 strings, and join the parts together with @code{/} as a delimiter.
-@end defun
+
+For example, the list @code{("scrambled eggs" "biscuits&gravy")} encodes
+as @code{"scrambled%20eggs/biscuits%26gravy"}.
+@end deffn
 
 @node HTTP
 @subsection The Hyper-Text Transfer Protocol
@@ -280,8 +328,8 @@ types by providing a number of low-level parsers and unparsers for
 elements of the HTTP protocol.
 
 If you are want to skip the low-level details for now and move on to web
-pages, @pxref{Web Server}.  Otherwise, load the HTTP module, and read
-on.
+pages, @pxref{Web Client}, and @pxref{Web Server}.  Otherwise, load the
+HTTP module, and read on.
 
 @example
 (use-modules (web http))
@@ -296,372 +344,778 @@ Guile tries to follow RFCs fairly strictly---the road to perdition being
 paved with compatibility hacks---though some allowances are made for
 not-too-divergent texts.
 
-The first bit is to define a registry of parsers, validators, and
-unparsers, keyed by header name.  That is the function of the
-@code{<header-decl>} object.
-
-@defun make-header-decl sym name multiple? parser validator writer
-@defunx header-decl? x
-@defunx header-decl-sym decl
-@defunx header-decl-name decl
-@defunx header-decl-multiple? decl
-@defunx header-decl-parser decl
-@defunx header-decl-validator decl
-@defunx header-decl-writer decl.
-A constructor, predicate, and field accessors for the
-@code{<header-decl>} type. The fields are as follows:
+Header names are represented as lower-case symbols.
 
-@table @code
-@item sym
-The symbol name for this header field, always in lower-case.  For
-example, @code{"Content-Length"} has a symbolic name of
-@code{content-length}.
-@item name
-The string name of the header, in its preferred capitalization.
-@item multiple?
-@code{#t} iff this header may appear multiple times in a message.
-@item parser
-A procedure which takes a string and returns a parsed value.
-@item validator
-A predicate, returning @code{#t} iff the value is valid for this header.
-@item writer
-A writer, which writes a value to the port given in the second argument.
-@end table
-@end defun
+@deffn {Scheme Procedure} string->header name
+Parse @var{name} to a symbolic header name.
+@end deffn
+
+@deffn {Scheme Procedure} header->string sym
+Return the string form for the header named @var{sym}.
+@end deffn
+
+For example:
+
+@example
+(string->header "Content-Length")
+@result{} content-length
+(header->string 'content-length)
+@result{} "Content-Length"
+
+(string->header "FOO")
+@result{} foo
+(header->string 'foo)
+@result{} "Foo"
+@end example
 
-@defun declare-header! sym name [#:multiple?] [#:parser] [#:validator] [#:writer]
-Make a header declaration, as above, and register it by symbol and by
-name.
-@end defun
+Guile keeps a registry of known headers, their string names, and some
+parsing and serialization procedures.  If a header is unknown, its
+string name is simply its symbol name in title-case.
+
+@deffn {Scheme Procedure} known-header? sym
+Return @code{#t} if @var{sym} is a known header, with associated
+parsers and serialization procedures, or @code{#f} otherwise.
+@end deffn
+
+@deffn {Scheme Procedure} header-parser sym
+Return the value parser for headers named @var{sym}.  The result is a
+procedure that takes one argument, a string, and returns the parsed
+value.  If the header isn't known to Guile, a default parser is returned
+that passes through the string unchanged.
+@end deffn
+
+@deffn {Scheme Procedure} header-validator sym
+Return a predicate which returns @code{#t} if the given value is valid
+for headers named @var{sym}.  The default validator for unknown headers
+is @code{string?}.
+@end deffn
+
+@deffn {Scheme Procedure} header-writer sym
+Return a procedure that writes values for headers named @var{sym} to a
+port.  The resulting procedure takes two arguments: a value and a port.
+The default writer is @code{display}.
+@end deffn
+
+For more on the set of headers that Guile knows about out of the box,
+@pxref{HTTP Headers}.  To add your own, use the @code{declare-header!}
+procedure:
+
+@deffn {Scheme Procedure} declare-header! name parser validator writer @
+       [#:multiple?=@code{#f}]
+Declare a parser, validator, and writer for a given header.
+@end deffn
+
+For example, let's say you are running a web server behind some sort of
+proxy, and your proxy adds an @code{X-Client-Address} header, indicating
+the IPv4 address of the original client.  You would like for the HTTP
+request record to parse out this header to a Scheme value, instead of
+leaving it as a string.  You could register this header with Guile's
+HTTP stack like this:
 
-@defun lookup-header-decl name
-Return the @var{header-decl} object registered for the given @var{name}.
+@example
+(declare-header! "X-Client-Address"
+  (lambda (str)
+    (inet-aton str))
+  (lambda (ip)
+    (and (integer? ip) (exact? ip) (<= 0 ip #xffffffff)))
+  (lambda (ip port)
+    (display (inet-ntoa ip) port)))
+@end example
 
-@var{name} may be a symbol or a string. Strings are mapped to headers in
-a case-insensitive fashion.
-@end defun
+@deffn {Scheme Procedure} declare-opaque-header! name
+A specialised version of @code{declare-header!} for the case in which
+you want a header's value to be returned/written ``as-is''.
+@end deffn
 
-@defun valid-header? sym val
-Returns a true value iff @var{val} is a valid Scheme value for the
-header with name @var{sym}.
-@end defun
+@deffn {Scheme Procedure} valid-header? sym val
+Return a true value if @var{val} is a valid Scheme value for the header
+with name @var{sym}, or @code{#f} otherwise.
+@end deffn
 
 Now that we have a generic interface for reading and writing headers, we
 do just that.
 
-@defun read-header port
-Reads one HTTP header from @var{port}. Returns two values: the header
+@deffn {Scheme Procedure} read-header port
+Read one HTTP header from @var{port}. Return two values: the header
 name and the parsed Scheme value. May raise an exception if the header
 was known but the value was invalid.
 
-Returns @var{#f} for both values if the end of the message body was
-reached (i.e., a blank line).
-@end defun
+Returns the end-of-file object for both values if the end of the message
+body was reached (i.e., a blank line).
+@end deffn
 
-@defun parse-header name val
+@deffn {Scheme Procedure} parse-header name val
 Parse @var{val}, a string, with the parser for the header named
-@var{name}.
+@var{name}.  Returns the parsed value.
+@end deffn
 
-Returns two values, the header name and parsed value. If a parser was
-found, the header name will be returned as a symbol. If a parser was not
-found, both the header name and the value are returned as strings.
-@end defun
+@deffn {Scheme Procedure} write-header name val port
+Write the given header name and value to @var{port}, using the writer
+from @code{header-writer}.
+@end deffn
 
-@defun write-header name val port
-Writes the given header name and value to @var{port}. If @var{name} is a
-symbol, looks up a declared header and uses that writer. Otherwise the
-value is written using @var{display}.
-@end defun
+@deffn {Scheme Procedure} read-headers port
+Read the headers of an HTTP message from @var{port}, returning them
+as an ordered alist.
+@end deffn
 
-@defun read-headers port
-Read an HTTP message from @var{port}, returning the headers as an
-ordered alist.
-@end defun
-
-@defun write-headers headers port
+@deffn {Scheme Procedure} write-headers headers port
 Write the given header alist to @var{port}. Doesn't write the final
-\r\n, as the user might want to add another header.
-@end defun
+@samp{\r\n}, as the user might want to add another header.
+@end deffn
 
 The @code{(web http)} module also has some utility procedures to read
 and write request and response lines.
 
-@defun parse-http-method str [start] [end]
+@deffn {Scheme Procedure} parse-http-method str [start] [end]
 Parse an HTTP method from @var{str}. The result is an upper-case symbol,
 like @code{GET}.
-@end defun
+@end deffn
 
-@defun parse-http-version str [start] [end]
-Parse an HTTP version from @var{str}, returning it as a major-minor
+@deffn {Scheme Procedure} parse-http-version str [start] [end]
+Parse an HTTP version from @var{str}, returning it as a major--minor
 pair. For example, @code{HTTP/1.1} parses as the pair of integers,
 @code{(1 . 1)}.
-@end defun
+@end deffn
 
-@defun parse-request-uri str [start] [end]
+@deffn {Scheme Procedure} parse-request-uri str [start] [end]
 Parse a URI from an HTTP request line. Note that URIs in requests do not
 have to have a scheme or host name. The result is a URI object.
-@end defun
+@end deffn
 
-@defun read-request-line port
+@deffn {Scheme Procedure} read-request-line port
 Read the first line of an HTTP request from @var{port}, returning three
 values: the method, the URI, and the version.
-@end defun
+@end deffn
 
-@defun write-request-line method uri version port
+@deffn {Scheme Procedure} write-request-line method uri version port
 Write the first line of an HTTP request to @var{port}.
-@end defun
+@end deffn
 
-@defun read-response-line port
+@deffn {Scheme Procedure} read-response-line port
 Read the first line of an HTTP response from @var{port}, returning three
-values: the HTTP version, the response code, and the "reason phrase".
-@end defun
+values: the HTTP version, the response code, and the ``reason phrase''.
+@end deffn
 
-@defun write-response-line version code reason-phrase port
+@deffn {Scheme Procedure} write-response-line version code reason-phrase port
 Write the first line of an HTTP response to @var{port}.
-@end defun
+@end deffn
 
 
 @node HTTP Headers
 @subsection HTTP Headers
 
-The @code{(web http)} module defines parsers and unparsers for all
-headers defined in the HTTP/1.1 standard.  This section describes the
-parsed format of the various headers.
-
-We cannot describe the function of all of these headers, however, in
-sufficient detail.  The interested reader would do well to download a
-copy of RFC 2616 and have it on hand.
-
-To begin with, we should make a few definitions:
-
-@table @dfn
-@item key-value list
-A key-value list is a list of values.  Each value may be a string,
-a symbol, or a pair.  Known keys are parsed to symbols; otherwise keys
-are left as strings.  Keys with values are parsed to pairs, the car of
-which is the symbol or string key, and the cdr is the parsed value.
-Parsed values for known keys have key-dependent formats.  Parsed values
-for unknown keys are strings.
-
-@item param list
-A param list is a list of key-value lists.  When serialized to a string,
-items in the inner lists are separated by semicolons.  Again, known keys
-are parsed to symbols.
-
-@item quality
-A number of headers have quality values in them, which are decimal
-fractions between zero and one indicating a preference for various kinds
-of responses, which the server may choose to heed.  Given that only
-three digits are allowed in the fractional part, Guile parses quality
-values to integers between 0 and 1000 instead of inexact numbers between
-0.0 and 1.0.
-
-@item quality list
-A list of pairs, the car of which is a quality value.
-
-@item entity tag
-A pair, the car of which is an opaque string, and the cdr of which is
-true iff the entity tag is a ``strong'' entity tag.
-@end table
+In addition to defining the infrastructure to parse headers, the
+@code{(web http)} module defines specific parsers and unparsers for all
+headers defined in the HTTP/1.1 standard.
+
+For example, if you receive a header named @samp{Accept-Language} with a
+value @samp{en, es;q=0.8}, Guile parses it as a quality list (defined
+below):
+
+@example
+(parse-header 'accept-language "en, es;q=0.8")
+@result{} ((1000 . "en") (800 . "es"))
+@end example
+
+The format of the value for @samp{Accept-Language} headers is defined
+below, along with all other headers defined in the HTTP standard.  (If
+the header were unknown, the value would have been returned as a
+string.)
+
+For brevity, the header definitions below are given in the form,
+@var{Type} @code{@var{name}}, indicating that values for the header
+@code{@var{name}} will be of the given @var{Type}.  Since Guile
+internally treats header names in lower case, in this document we give
+types title-cased names.  A short description of the each header's
+purpose and an example follow.
+
+For full details on the meanings of all of these headers, see the HTTP
+1.1 standard, RFC 2616.
+
+@subsubsection HTTP Header Types
+
+Here we define the types that are used below, when defining headers.
 
+@deftp {HTTP Header Type} Date
+A SRFI-19 date.
+@end deftp
+
+@deftp {HTTP Header Type} KVList
+A list whose elements are keys or key-value pairs.  Keys are parsed to
+symbols.  Values are strings by default.  Non-string values are the
+exception, and are mentioned explicitly below, as appropriate.
+@end deftp
+
+@deftp {HTTP Header Type} SList
+A list of strings.
+@end deftp
+
+@deftp {HTTP Header Type} Quality
+An exact integer between 0 and 1000.  Qualities are used to express
+preference, given multiple options.  An option with a quality of 870,
+for example, is preferred over an option with quality 500.
+
+(Qualities are written out over the wire as numbers between 0.0 and
+1.0, but since the standard only allows three digits after the decimal,
+it's equivalent to integers between 0 and 1000, so that's what Guile
+uses.)
+@end deftp
+
+@deftp {HTTP Header Type} QList
+A quality list: a list of pairs, the car of which is a quality, and the
+cdr a string.  Used to express a list of options, along with their
+qualities.
+@end deftp
+
+@deftp {HTTP Header Type} ETag
+An entity tag, represented as a pair.  The car of the pair is an opaque
+string, and the cdr is @code{#t} if the entity tag is a ``strong'' entity
+tag, and @code{#f} otherwise.
+@end deftp
 
 @subsubsection General Headers
 
-@table @code
-@item cache-control
-A key-value list of cache-control directives. Known keys are
-@code{max-age}, @code{max-stale}, @code{min-fresh},
-@code{must-revalidate}, @code{no-cache}, @code{no-store},
-@code{no-transform}, @code{only-if-cached}, @code{private},
-@code{proxy-revalidate}, @code{public}, and @code{s-maxage}.
+General HTTP headers may be present in any HTTP message.
+
+@deftypevr {HTTP Header} KVList cache-control
+A key-value list of cache-control directives.  See RFC 2616, for more
+details.
 
 If present, parameters to @code{max-age}, @code{max-stale},
 @code{min-fresh}, and @code{s-maxage} are all parsed as non-negative
 integers.
 
 If present, parameters to @code{private} and @code{no-cache} are parsed
-as lists of header names, represented as symbols if they are known
-headers or strings otherwise.
+as lists of header names, as symbols.
 
-@item connection
-A list of connection tokens.  A connection token is a string.
+@example
+(parse-header 'cache-control "no-cache,no-store"
+@result{} (no-cache no-store)
+(parse-header 'cache-control "no-cache=\"Authorization,Date\",no-store"
+@result{} ((no-cache . (authorization date)) no-store)
+(parse-header 'cache-control "no-cache=\"Authorization,Date\",max-age=10"
+@result{} ((no-cache . (authorization date)) (max-age . 10))
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} List connection
+A list of header names that apply only to this HTTP connection, as
+symbols.  Additionally, the symbol @samp{close} may be present, to
+indicate that the server should close the connection after responding to
+the request.
+@example
+(parse-header 'connection "close")
+@result{} (close)
+@end example
+@end deftypevr
 
-@item date
-A SRFI-19 date record.
+@deftypevr {HTTP Header} Date date
+The date that a given HTTP message was originated.
+@example
+(parse-header 'date "Tue, 15 Nov 1994 08:12:31 GMT")
+@result{} #<date ...>
+@end example
+@end deftypevr
 
-@item pragma
-A key-value list of pragma directives.  @code{no-cache} is the only
-known key.
+@deftypevr {HTTP Header} KVList pragma
+A key-value list of implementation-specific directives.
+@example
+(parse-header 'pragma "no-cache, broccoli=tasty")
+@result{} (no-cache (broccoli . "tasty"))
+@end example
+@end deftypevr
 
-@item trailer
-A list of header names.  Known header names are parsed to symbols,
-otherwise they are left as strings.
+@deftypevr {HTTP Header} List trailer
+A list of header names which will appear after the message body, instead
+of with the message headers.
+@example
+(parse-header 'trailer "ETag")
+@result{} (etag)
+@end example
+@end deftypevr
 
-@item transfer-encoding
-A param list of transfer codings.  @code{chunked} is the only known key.
+@deftypevr {HTTP Header} List transfer-encoding
+A list of transfer codings, expressed as key-value lists.  The only
+transfer coding defined by the specification is @code{chunked}.
+@example
+(parse-header 'transfer-encoding "chunked")
+@result{} ((chunked))
+@end example
+@end deftypevr
 
-@item upgrade
-A list of strings.
+@deftypevr {HTTP Header} List upgrade
+A list of strings, indicating additional protocols that a server could use
+in response to a request.
+@example
+(parse-header 'upgrade "WebSocket")
+@result{} ("WebSocket")
+@end example
+@end deftypevr
 
-@item via
-A list of strings.  There may be multiple @code{via} headers in ne
-message.
+FIXME: parse out more fully?
+@deftypevr {HTTP Header} List via
+A list of strings, indicating the protocol versions and hosts of
+intermediate servers and proxies.  There may be multiple @code{via}
+headers in one message.
+@example
+(parse-header 'via "1.0 venus, 1.1 mars")
+@result{} ("1.0 venus" "1.1 mars")
+@end example
+@end deftypevr
 
-@item warning
-A list of warnings.  Each warning is a itself a list of four elements: a
-code, as an exact integer between 0 and 1000, a host as a string, the
-warning text as a string, and either @code{#f} or a SRFI-19 date.
+@deftypevr {HTTP Header} List warning
+A list of warnings given by a server or intermediate proxy.  Each
+warning is a itself a list of four elements: a code, as an exact integer
+between 0 and 1000, a host as a string, the warning text as a string,
+and either @code{#f} or a SRFI-19 date.
 
 There may be multiple @code{warning} headers in one message.
-@end table
+@example
+(parse-header 'warning "123 foo \"core breach imminent\"")
+@result{} ((123 "foo" "core-breach imminent" #f))
+@end example
+@end deftypevr
 
 
 @subsubsection Entity Headers
 
-@table @code
-@item allow
-A list of methods, as strings.  Methods are parsed as strings instead of
-@code{parse-http-method} so as to allow for new methods.
+Entity headers may be present in any HTTP message, and refer to the
+resource referenced in the HTTP request or response.
 
-@item content-encoding
-A list of content codings, as strings.
+@deftypevr {HTTP Header} List allow
+A list of allowed methods on a given resource, as symbols.
+@example
+(parse-header 'allow "GET, HEAD")
+@result{} (GET HEAD)
+@end example
+@end deftypevr
 
-@item content-language
-A list of language tags, as strings.
+@deftypevr {HTTP Header} List content-encoding
+A list of content codings, as symbols.
+@example
+(parse-header 'content-encoding "gzip")
+@result{} (gzip)
+@end example
+@end deftypevr
 
-@item content-length
-An exact, non-negative integer.
+@deftypevr {HTTP Header} List content-language
+The languages that a resource is in, as strings.
+@example
+(parse-header 'content-language "en")
+@result{} ("en")
+@end example
+@end deftypevr
 
-@item content-location
-A URI record.
+@deftypevr {HTTP Header} UInt content-length
+The number of bytes in a resource, as an exact, non-negative integer.
+@example
+(parse-header 'content-length "300")
+@result{} 300
+@end example
+@end deftypevr
 
-@item content-md5
-A string.
+@deftypevr {HTTP Header} URI content-location
+The canonical URI for a resource, in the case that it is also accessible
+from a different URI.
+@example
+(parse-header 'content-location "http://example.com/foo")
+@result{} #<<uri> ...>
+@end example
+@end deftypevr
 
-@item content-range
-A list of three elements: the symbol @code{bytes}, either the symbol
-@code{*} or a pair of integers, indicating the byte rage, and either
-@code{*} or an integer, for the instance length.
+@deftypevr {HTTP Header} String content-md5
+The MD5 digest of a resource.
+@example
+(parse-header 'content-md5 "ffaea1a79810785575e29e2bd45e2fa5")
+@result{} "ffaea1a79810785575e29e2bd45e2fa5"
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} List content-range
+A range specification, as a list of three elements: the symbol
+@code{bytes}, either the symbol @code{*} or a pair of integers,
+indicating the byte rage, and either @code{*} or an integer, for the
+instance length.  Used to indicate that a response only includes part of
+a resource.
+@example
+(parse-header 'content-range "bytes 10-20/*")
+@result{} (bytes (10 . 20) *)
+@end example
+@end deftypevr
 
-@item content-type
-A pair, the car of which is the media type as a string, and the cdr is
-an alist of parameters, with strings as keys and values.
+@deftypevr {HTTP Header} List content-type
+The MIME type of a resource, as a symbol, along with any parameters.
+@example
+(parse-header 'content-length "text/plain")
+@result{} (text/plain)
+(parse-header 'content-length "text/plain;charset=utf-8")
+@result{} (text/plain (charset . "utf-8"))
+@end example
+Note that the @code{charset} parameter is something is a misnomer, and
+the HTTP specification admits this.  It specifies the @emph{encoding} of
+the characters, not the character set.
+@end deftypevr
+
+@deftypevr {HTTP Header} Date expires
+The date/time after which the resource given in a response is considered
+stale.
+@example
+(parse-header 'expires "Tue, 15 Nov 1994 08:12:31 GMT")
+@result{} #<date ...>
+@end example
+@end deftypevr
 
-For example, @code{"text/plain"} parses as @code{("text/plain")}, and
-@code{"text/plain;charset=utf-8"} parses as @code{("text/plain"
-("charset" . "utf-8"))}.
+@deftypevr {HTTP Header} Date last-modified
+The date/time on which the resource given in a response was last
+modified.
+@example
+(parse-header 'expires "Tue, 15 Nov 1994 08:12:31 GMT")
+@result{} #<date ...>
+@end example
+@end deftypevr
 
-@item expires
-A SRFI-19 date.
 
-@item last-modified
-A SRFI-19 date.
+@subsubsection Request Headers
 
-@end table
+Request headers may only appear in an HTTP request, not in a response.
 
+@deftypevr {HTTP Header} List accept
+A list of preferred media types for a response.  Each element of the
+list is itself a list, in the same format as @code{content-type}.  
+@example
+(parse-header 'accept "text/html,text/plain;charset=utf-8")
+@result{} ((text/html) (text/plain (charset . "utf-8")))
+@end example
+Preference is expressed with quality values:
+@example
+(parse-header 'accept "text/html;q=0.8,text/plain;q=0.6")
+@result{} ((text/html (q . 800)) (text/plain (q . 600)))
+@end example
+@end deftypevr
 
-@subsubsection Request Headers
+@deftypevr {HTTP Header} QList accept-charset
+A quality list of acceptable charsets.  Note again that what HTTP calls
+a ``charset'' is what Guile calls a ``character encoding''.
+@example
+(parse-header 'accept-charset "iso-8859-5, unicode-1-1;q=0.8")
+@result{} ((1000 . "iso-8859-5") (800 . "unicode-1-1"))
+@end example
+@end deftypevr
 
-@table @code
-@item accept
-A param list.  Each element in the list indicates one media-range
-with accept-params.  They only known key is @code{q}, whose value is
-parsed as a quality value.
+@deftypevr {HTTP Header} QList accept-encoding
+A quality list of acceptable content codings.
+@example
+(parse-header 'accept-encoding "gzip,identity=0.8")
+@result{} ((1000 . "gzip") (800 . "identity"))
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} QList accept-language
+A quality list of acceptable languages.
+@example
+(parse-header 'accept-language "cn,en=0.75")
+@result{} ((1000 . "cn") (750 . "en"))
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} Pair authorization
+Authorization credentials.  The car of the pair indicates the
+authentication scheme, like @code{basic}.  For basic authentication, the
+cdr of the pair will be the base64-encoded @samp{@var{user}:@var{pass}}
+string.  For other authentication schemes, like @code{digest}, the cdr
+will be a key-value list of credentials.
+@example
+(parse-header 'authorization "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
+@result{} (basic . "QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} List expect
+A list of expectations that a client has of a server.  The expectations
+are key-value lists.
+@example
+(parse-header 'expect "100-continue")
+@result{} ((100-continue))
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} String from
+The email address of a user making an HTTP request.
+@example
+(parse-header 'from "bob@@example.com")
+@result{} "bob@@example.com"
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} Pair host
+The host for the resource being requested, as a hostname-port pair.  If
+no port is given, the port is @code{#f}.
+@example
+(parse-header 'host "gnu.org:80")
+@result{} ("gnu.org" . 80)
+(parse-header 'host "gnu.org")
+@result{} ("gnu.org" . #f)
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} *|List if-match
+A set of etags, indicating that the request should proceed if and only
+if the etag of the resource is in that set.  Either the symbol @code{*},
+indicating any etag, or a list of entity tags.
+@example
+(parse-header 'if-match "*")
+@result{} *
+(parse-header 'if-match "asdfadf")
+@result{} (("asdfadf" . #t))
+(parse-header 'if-match W/"asdfadf")
+@result{} (("asdfadf" . #f))
+@end example
+@end deftypevr
 
-@item accept-charset
-A quality-list of charsets, as strings.
+@deftypevr {HTTP Header} Date if-modified-since
+Indicates that a response should proceed if and only if the resource has
+been modified since the given date.
+@example
+(parse-header 'if-modified-since "Tue, 15 Nov 1994 08:12:31 GMT")
+@result{} #<date ...>
+@end example
+@end deftypevr
 
-@item accept-encoding
-A quality-list of content codings, as strings.
+@deftypevr {HTTP Header} *|List if-none-match
+A set of etags, indicating that the request should proceed if and only
+if the etag of the resource is not in the set.  Either the symbol
+@code{*}, indicating any etag, or a list of entity tags.
+@example
+(parse-header 'if-none-match "*")
+@result{} *
+@end example
+@end deftypevr
 
-@item accept-language
-A quality-list of languages, as strings.
+@deftypevr {HTTP Header} ETag|Date if-range
+Indicates that the range request should proceed if and only if the
+resource matches a modification date or an etag.  Either an entity tag,
+or a SRFI-19 date.
+@example
+(parse-header 'if-range "\"original-etag\"")
+@result{} ("original-etag" . #t)
+@end example
+@end deftypevr
 
-@item authorization
-A string.
+@deftypevr {HTTP Header} Date if-unmodified-since
+Indicates that a response should proceed if and only if the resource has
+not been modified since the given date.
+@example
+(parse-header 'if-not-modified-since "Tue, 15 Nov 1994 08:12:31 GMT")
+@result{} #<date ...>
+@end example
+@end deftypevr
 
-@item expect
-A param list of expectations.  The only known key is
-@code{100-continue}.
+@deftypevr {HTTP Header} UInt max-forwards
+The maximum number of proxy or gateway hops that a request should be
+subject to.
+@example
+(parse-header 'max-forwards "10")
+@result{} 10
+@end example
+@end deftypevr
 
-@item from
-A string.
+@deftypevr {HTTP Header} Pair proxy-authorization
+Authorization credentials for a proxy connection.  See the documentation
+for @code{authorization} above for more information on the format.
+@example
+(parse-header 'proxy-authorization "Digest foo=bar,baz=qux"
+@result{} (digest (foo . "bar") (baz . "qux"))
+@end example
+@end deftypevr
+
+@deftypevr {HTTP Header} Pair range
+A range request, indicating that the client wants only part of a
+resource.  The car of the pair is the symbol @code{bytes}, and the cdr
+is a list of pairs. Each element of the cdr indicates a range; the car
+is the first byte position and the cdr is the last byte position, as
+integers, or @code{#f} if not given.
+@example
+(parse-header 'range "bytes=10-30,50-")
+@result{} (bytes (10 . 30) (50 . #f))
+@end example
+@end deftypevr
 
-@item host
-A pair of the host, as a string, and the port, as an integer. If no port
-is given, port is @code{#f}.
+@deftypevr {HTTP Header} URI referer
+The URI of the resource that referred the user to this resource.  The
+name of the header is a misspelling, but we are stuck with it.
+@example
+(parse-header 'referer "http://www.gnu.org/")
+@result{} #<uri ...>
+@end example
+@end deftypevr
 
-@item if-match
-Either the symbol @code{*}, or a list of entity tags (see above).
+@deftypevr {HTTP Header} List te
+A list of transfer codings, expressed as key-value lists.  A common
+transfer coding is @code{trailers}.
+@example
+(parse-header 'te "trailers")
+@result{} ((trailers))
+@end example
+@end deftypevr
 
-@item if-modified-since
-A SRFI-19 date.
+@deftypevr {HTTP Header} String user-agent
+A string indicating the user agent making the request.  The
+specification defines a structured format for this header, but it is
+widely disregarded, so Guile does not attempt to parse strictly.
+@example
+(parse-header 'user-agent "Mozilla/5.0")
+@result{} "Mozilla/5.0"
+@end example
+@end deftypevr
 
-@item if-none-match
-Either the symbol @code{*}, or a list of entity tags (see above).
 
-@item if-range
-Either an entity tag, or a SRFI-19 date.
+@subsubsection Response Headers
 
-@item if-unmodified-since
-A SRFI-19 date.
+@deftypevr {HTTP Header} List accept-ranges
+A list of range units that the server supports, as symbols.
+@example
+(parse-header 'accept-ranges "bytes")
+@result{} (bytes)
+@end example
+@end deftypevr
 
-@item max-forwards
-An exact non-negative integer.
+@deftypevr {HTTP Header} UInt age
+The age of a cached response, in seconds.
+@example
+(parse-header 'age "3600")
+@result{} 3600
+@end example
+@end deftypevr
 
-@item proxy-authorization
-A string.
+@deftypevr {HTTP Header} ETag etag
+The entity-tag of the resource.
+@example
+(parse-header 'etag "\"foo\"")
+@result{} ("foo" . #t)
+@end example
+@end deftypevr
 
-@item range
-A pair whose car is the symbol @code{bytes}, and whose cdr is a list of
-pairs. Each element of the cdr indicates a range; the car is the first
-byte position and the cdr is the last byte position, as integers, or
-@code{#f} if not given.
+@deftypevr {HTTP Header} URI-reference location
+A URI reference on which a request may be completed.  Used in
+combination with a redirecting status code to perform client-side
+redirection.
+@example
+(parse-header 'location "http://example.com/other")
+@result{} #<uri ...>
+@end example
+@end deftypevr
 
-@item referer
-A URI.
+@deftypevr {HTTP Header} List proxy-authenticate
+A list of challenges to a proxy, indicating the need for authentication.
+@example
+(parse-header 'proxy-authenticate "Basic realm=\"foo\"")
+@result{} ((basic (realm . "foo")))
+@end example
+@end deftypevr
 
-@item te
-A param list of transfer-codings.  The only known key is
-@code{trailers}.
+@deftypevr {HTTP Header} UInt|Date retry-after
+Used in combination with a server-busy status code, like 503, to
+indicate that a client should retry later.  Either a number of seconds,
+or a date.
+@example
+(parse-header 'retry-after "60")
+@result{} 60
+@end example
+@end deftypevr
 
-@item user-agent
-A string.
-@end table
+@deftypevr {HTTP Header} String server
+A string identifying the server.
+@example
+(parse-header 'server "My first web server")
+@result{} "My first web server"
+@end example
+@end deftypevr
 
+@deftypevr {HTTP Header} *|List vary
+A set of request headers that were used in computing this response.
+Used to indicate that server-side content negotiation was performed, for
+example in response to the @code{accept-language} header.  Can also be
+the symbol @code{*}, indicating that all headers were considered.
+@example
+(parse-header 'vary "Accept-Language, Accept")
+@result{} (accept-language accept)
+@end example
+@end deftypevr
 
-@subsubsection Response Headers
+@deftypevr {HTTP Header} List www-authenticate
+A list of challenges to a user, indicating the need for authentication.
+@example
+(parse-header 'www-authenticate "Basic realm=\"foo\"")
+@result{} ((basic (realm . "foo")))
+@end example
+@end deftypevr
 
-@table @code
-@item accept-ranges
-A list of strings.
+@node Transfer Codings
+@subsection Transfer Codings
 
-@item age
-An exact, non-negative integer.
+HTTP 1.1 allows for various transfer codings to be applied to message
+bodies. These include various types of compression, and HTTP chunked
+encoding. Currently, only chunked encoding is supported by guile.
 
-@item etag
-An entity tag.
+Chunked coding is an optional coding that may be applied to message
+bodies, to allow messages whose length is not known beforehand to be
+returned. Such messages can be split into chunks, terminated by a final
+zero length chunk.
 
-@item location
-A URI.
+In order to make dealing with encodings more simple, guile provides
+procedures to create ports that ``wrap'' existing ports, applying
+transformations transparently under the hood.
 
-@item proxy-authenticate
-A string.
+These procedures are in the @code{(web http)} module.
 
-@item retry-after
-Either an exact, non-negative integer, or a SRFI-19 date.
+@example
+(use-modules (web http))
+@end example
 
-@item server
-A string.
+@deffn {Scheme Procedure} make-chunked-input-port port [#:keep-alive?=#f]
+Returns a new port, that transparently reads and decodes chunk-encoded
+data from @var{port}. If no more chunk-encoded data is available, it
+returns the end-of-file object. When the port is closed, @var{port} will
+also be closed, unless @var{keep-alive?} is true.
+@end deffn
 
-@item vary
-Either the symbol @code{*}, or a list of headers, with known headers
-parsed to symbols.
+@example
+(use-modules (ice-9 rdelim))
+
+(define s "5\r\nFirst\r\nA\r\n line\n Sec\r\n8\r\nond line\r\n0\r\n")
+(define p (make-chunked-input-port (open-input-string s)))
+(read-line s)
+@result{} "First line"
+(read-line s)
+@result{} "Second line"
+@end example
 
-@item www-authenticate
-A string.
-@end table
+@deffn {Scheme Procedure} make-chunked-output-port port [#:keep-alive?=#f]
+Returns a new port, which transparently encodes data as chunk-encoded
+before writing it to @var{port}. Whenever a write occurs on this port,
+it buffers it, until the port is flushed, at which point it writes a
+chunk containing all the data written so far. When the port is closed,
+the data remaining is written to @var{port}, as is the terminating zero
+chunk. It also causes @var{port} to be closed, unless @var{keep-alive?}
+is true.
 
+Note. Forcing a chunked output port when there is no data is buffered
+does not write a zero chunk, as this would cause the data to be
+interpreted incorrectly by the client.
+@end deffn
+
+@example
+(call-with-output-string
+  (lambda (out)
+    (define out* (make-chunked-output-port out #:keep-alive? #t))
+    (display "first chunk" out*)
+    (force-output out*)
+    (force-output out*) ; note this does not write a zero chunk
+    (display "second chunk" out*)
+    (close-port out*)))
+@result{} "b\r\nfirst chunk\r\nc\r\nsecond chunk\r\n0\r\n"
+@end example
 
 @node Requests
 @subsection HTTP Requests
@@ -670,23 +1124,43 @@ A string.
 (use-modules (web request))
 @end example
 
-The request module contains a data type for HTTP requests.  Note that
-the body is not part of the request, but the port is.  Once you have
-read a request, you may read the body separately, and likewise for
-writing requests.
+The request module contains a data type for HTTP requests.  
 
-@defun build-request [#:method] [#:uri] [#:version] [#:headers] [#:port] [#:meta] [#:validate-headers?]
-Construct an HTTP request object. If @var{validate-headers?} is true,
-the headers are each run through their respective validators.
-@end defun
-
-@defun request? 
-@defunx request-method 
-@defunx request-uri 
-@defunx request-version 
-@defunx request-headers 
-@defunx request-meta 
-@defunx request-port 
+@subsubsection An Important Note on Character Sets
+
+HTTP requests consist of two parts: the request proper, consisting of a
+request line and a set of headers, and (optionally) a body.  The body
+might have a binary content-type, and even in the textual case its
+length is specified in bytes, not characters.
+
+Therefore, HTTP is a fundamentally binary protocol.  However the request
+line and headers are specified to be in a subset of ASCII, so they can
+be treated as text, provided that the port's encoding is set to an
+ASCII-compatible one-byte-per-character encoding.  ISO-8859-1 (latin-1)
+is just such an encoding, and happens to be very efficient for Guile.
+
+So what Guile does when reading requests from the wire, or writing them
+out, is to set the port's encoding to latin-1, and treating the request
+headers as text.
+
+The request body is another issue.  For binary data, the data is
+probably in a bytevector, so we use the R6RS binary output procedures to
+write out the binary payload.  Textual data usually has to be written
+out to some character encoding, usually UTF-8, and then the resulting
+bytevector is written out to the port.
+
+In summary, Guile reads and writes HTTP over latin-1 sockets, without
+any loss of generality.
+
+@subsubsection Request API
+
+@deffn {Scheme Procedure} request? obj 
+@deffnx {Scheme Procedure} request-method request
+@deffnx {Scheme Procedure} request-uri request
+@deffnx {Scheme Procedure} request-version request
+@deffnx {Scheme Procedure} request-headers request
+@deffnx {Scheme Procedure} request-meta request
+@deffnx {Scheme Procedure} request-port request
 A predicate and field accessors for the request type.  The fields are as
 follows:
 @table @code
@@ -705,100 +1179,97 @@ Communication}).
 @item port
 The port on which to read or write a request body, if any.
 @end table
-@end defun
+@end deffn
 
-@defun read-request port [meta]
+@deffn {Scheme Procedure} read-request port [meta='()]
 Read an HTTP request from @var{port}, optionally attaching the given
 metadata, @var{meta}.
 
 As a side effect, sets the encoding on @var{port} to ISO-8859-1
 (latin-1), so that reading one character reads one byte. See the
-discussion of character sets in "HTTP Requests" in the manual, for more
-information.
-@end defun
-
-@defun write-request r port
-Write the given HTTP request to @var{port}.
+discussion of character sets above, for more information.
 
-Returns a new request, whose @code{request-port} will continue writing
-on @var{port}, perhaps using some transfer encoding.
-@end defun
+Note that the body is not part of the request.  Once you have read a
+request, you may read the body separately, and likewise for writing
+requests.
+@end deffn
 
-@defun read-request-body/latin-1 r
-Reads the request body from @var{r}, as a string.
+@deffn {Scheme Procedure} build-request uri [#:method='GET] @
+       [#:version='(1 . 1)] [#:headers='()] [#:port=#f] [#:meta='()] @
+       [#:validate-headers?=#t]
+Construct an HTTP request object. If @var{validate-headers?} is true,
+the headers are each run through their respective validators.
+@end deffn
 
-Assumes that the request port has ISO-8859-1 encoding, so that the
-number of characters to read is the same as the
-@code{request-content-length}. Returns @code{#f} if there was no request
-body.
-@end defun
+@deffn {Scheme Procedure} write-request r port
+Write the given HTTP request to @var{port}.
 
-@defun write-request-body/latin-1 r body
-Write @var{body}, a string encodable in ISO-8859-1, to the port
-corresponding to the HTTP request @var{r}.
-@end defun
+Return a new request, whose @code{request-port} will continue writing
+on @var{port}, perhaps using some transfer encoding.
+@end deffn
 
-@defun read-request-body/bytevector r
-Reads the request body from @var{r}, as a bytevector. Returns @code{#f}
+@deffn {Scheme Procedure} read-request-body r
+Reads the request body from @var{r}, as a bytevector.  Return @code{#f}
 if there was no request body.
-@end defun
+@end deffn
 
-@defun write-request-body/bytevector r bv
-Write @var{body}, a bytevector, to the port corresponding to the HTTP
+@deffn {Scheme Procedure} write-request-body r bv
+Write @var{bv}, a bytevector, to the port corresponding to the HTTP
 request @var{r}.
-@end defun
+@end deffn
 
 The various headers that are typically associated with HTTP requests may
 be accessed with these dedicated accessors.  @xref{HTTP Headers}, for
 more information on the format of parsed headers.
 
-@defun request-accept request [default='()]
-@defunx request-accept-charset request [default='()]
-@defunx request-accept-encoding request [default='()]
-@defunx request-accept-language request [default='()]
-@defunx request-allow request [default='()]
-@defunx request-authorization request [default=#f]
-@defunx request-cache-control request [default='()]
-@defunx request-connection request [default='()]
-@defunx request-content-encoding request [default='()]
-@defunx request-content-language request [default='()]
-@defunx request-content-length request [default=#f]
-@defunx request-content-location request [default=#f]
-@defunx request-content-md5 request [default=#f]
-@defunx request-content-range request [default=#f]
-@defunx request-content-type request [default=#f]
-@defunx request-date request [default=#f]
-@defunx request-expect request [default='()]
-@defunx request-expires request [default=#f]
-@defunx request-from request [default=#f]
-@defunx request-host request [default=#f]
-@defunx request-if-match request [default=#f]
-@defunx request-if-modified-since request [default=#f]
-@defunx request-if-none-match request [default=#f]
-@defunx request-if-range request [default=#f]
-@defunx request-if-unmodified-since request [default=#f]
-@defunx request-last-modified request [default=#f]
-@defunx request-max-forwards request [default=#f]
-@defunx request-pragma request [default='()]
-@defunx request-proxy-authorization request [default=#f]
-@defunx request-range request [default=#f]
-@defunx request-referer request [default=#f]
-@defunx request-te request [default=#f]
-@defunx request-trailer request [default='()]
-@defunx request-transfer-encoding request [default='()]
-@defunx request-upgrade request [default='()]
-@defunx request-user-agent request [default=#f]
-@defunx request-via request [default='()]
-@defunx request-warning request [default='()]
+@deffn {Scheme Procedure} request-accept request [default='()]
+@deffnx {Scheme Procedure} request-accept-charset request [default='()]
+@deffnx {Scheme Procedure} request-accept-encoding request [default='()]
+@deffnx {Scheme Procedure} request-accept-language request [default='()]
+@deffnx {Scheme Procedure} request-allow request [default='()]
+@deffnx {Scheme Procedure} request-authorization request [default=#f]
+@deffnx {Scheme Procedure} request-cache-control request [default='()]
+@deffnx {Scheme Procedure} request-connection request [default='()]
+@deffnx {Scheme Procedure} request-content-encoding request [default='()]
+@deffnx {Scheme Procedure} request-content-language request [default='()]
+@deffnx {Scheme Procedure} request-content-length request [default=#f]
+@deffnx {Scheme Procedure} request-content-location request [default=#f]
+@deffnx {Scheme Procedure} request-content-md5 request [default=#f]
+@deffnx {Scheme Procedure} request-content-range request [default=#f]
+@deffnx {Scheme Procedure} request-content-type request [default=#f]
+@deffnx {Scheme Procedure} request-date request [default=#f]
+@deffnx {Scheme Procedure} request-expect request [default='()]
+@deffnx {Scheme Procedure} request-expires request [default=#f]
+@deffnx {Scheme Procedure} request-from request [default=#f]
+@deffnx {Scheme Procedure} request-host request [default=#f]
+@deffnx {Scheme Procedure} request-if-match request [default=#f]
+@deffnx {Scheme Procedure} request-if-modified-since request [default=#f]
+@deffnx {Scheme Procedure} request-if-none-match request [default=#f]
+@deffnx {Scheme Procedure} request-if-range request [default=#f]
+@deffnx {Scheme Procedure} request-if-unmodified-since request [default=#f]
+@deffnx {Scheme Procedure} request-last-modified request [default=#f]
+@deffnx {Scheme Procedure} request-max-forwards request [default=#f]
+@deffnx {Scheme Procedure} request-pragma request [default='()]
+@deffnx {Scheme Procedure} request-proxy-authorization request [default=#f]
+@deffnx {Scheme Procedure} request-range request [default=#f]
+@deffnx {Scheme Procedure} request-referer request [default=#f]
+@deffnx {Scheme Procedure} request-te request [default=#f]
+@deffnx {Scheme Procedure} request-trailer request [default='()]
+@deffnx {Scheme Procedure} request-transfer-encoding request [default='()]
+@deffnx {Scheme Procedure} request-upgrade request [default='()]
+@deffnx {Scheme Procedure} request-user-agent request [default=#f]
+@deffnx {Scheme Procedure} request-via request [default='()]
+@deffnx {Scheme Procedure} request-warning request [default='()]
 Return the given request header, or @var{default} if none was present.
-@end defun
+@end deffn
 
-@defun request-absolute-uri r [default-host] [default-port]
+@deffn {Scheme Procedure} request-absolute-uri r [default-host=#f] @
+       [default-port=#f] [default-scheme=#f]
 A helper routine to determine the absolute URI of a request, using the
-@code{host} header and the default host and port.
-@end defun
-
-
+@code{host} header and the default scheme, host and port.  If there is
+no default scheme and the URI is not itself absolute, an error is
+signalled.
+@end deffn
 
 @node Responses
 @subsection HTTP Responses
@@ -810,12 +1281,12 @@ A helper routine to determine the absolute URI of a request, using the
 As with requests (@pxref{Requests}), Guile offers a data type for HTTP
 responses.  Again, the body is represented separately from the request.
 
-@defun response? 
-@defunx response-version 
-@defunx response-code 
-@defunx response-reason-phrase response
-@defunx response-headers 
-@defunx response-port 
+@deffn {Scheme Procedure} response? obj
+@deffnx {Scheme Procedure} response-version response
+@deffnx {Scheme Procedure} response-code response
+@deffnx {Scheme Procedure} response-reason-phrase response
+@deffnx {Scheme Procedure} response-headers response
+@deffnx {Scheme Procedure} response-port response
 A predicate and field accessors for the response type.  The fields are as
 follows:
 @table @code
@@ -831,30 +1302,23 @@ The response headers, as an alist of parsed values.
 @item port
 The port on which to read or write a response body, if any.
 @end table
-@end defun
+@end deffn
 
-@defun read-response port
-Read an HTTP response from @var{port}, optionally attaching the given
-metadata, @var{meta}.
+@deffn {Scheme Procedure} read-response port
+Read an HTTP response from @var{port}.
 
 As a side effect, sets the encoding on @var{port} to ISO-8859-1
 (latin-1), so that reading one character reads one byte. See the
-discussion of character sets in "HTTP Responses" in the manual, for more
-information.
-@end defun
+discussion of character sets in @ref{Responses}, for more information.
+@end deffn
 
-@defun build-response [#:version] [#:code] [#:reason-phrase] [#:headers] [#:port]
+@deffn {Scheme Procedure} build-response [#:version='(1 . 1)] [#:code=200] [#:reason-phrase=#f] [#:headers='()] [#:port=#f] [#:validate-headers?=#t]
 Construct an HTTP response object. If @var{validate-headers?} is true,
 the headers are each run through their respective validators.
-@end defun
-
-@defun extend-response r k v . additional
-Extend an HTTP response by setting additional HTTP headers @var{k},
-@var{v}. Returns a new HTTP response.
-@end defun
+@end deffn
 
-@defun adapt-response-version response version
-Adapt the given response to a different HTTP version. Returns a new HTTP
+@deffn {Scheme Procedure} adapt-response-version response version
+Adapt the given response to a different HTTP version.  Return a new HTTP
 response.
 
 The idea is that many applications might just build a response for the
@@ -862,74 +1326,176 @@ default HTTP version, and this method could handle a number of
 programmatic transformations to respond to older HTTP versions (0.9 and
 1.0). But currently this function is a bit heavy-handed, just updating
 the version field.
-@end defun
+@end deffn
 
-@defun write-response r port
+@deffn {Scheme Procedure} write-response r port
 Write the given HTTP response to @var{port}.
 
-Returns a new response, whose @code{response-port} will continue writing
+Return a new response, whose @code{response-port} will continue writing
 on @var{port}, perhaps using some transfer encoding.
-@end defun
+@end deffn
 
-@defun read-response-body/latin-1 r
-Reads the response body from @var{r}, as a string.
+@deffn {Scheme Procedure} response-must-not-include-body? r
+Some responses, like those with status code 304, are specified as never
+having bodies.  This predicate returns @code{#t} for those responses.
 
-Assumes that the response port has ISO-8859-1 encoding, so that the
-number of characters to read is the same as the
-@code{response-content-length}. Returns @code{#f} if there was no
-response body.
-@end defun
+Note also, though, that responses to @code{HEAD} requests must also not
+have a body.
+@end deffn
 
-@defun write-response-body/latin-1 r body
-Write @var{body}, a string encodable in ISO-8859-1, to the port
-corresponding to the HTTP response @var{r}.
-@end defun
+@deffn {Scheme Procedure} response-body-port r [#:decode?=#t] [#:keep-alive?=#t]
+Return an input port from which the body of @var{r} can be read.  The encoding
+of the returned port is set according to @var{r}'s @code{content-type} header,
+when it's textual, except if @var{decode?} is @code{#f}.  Return @code{#f}
+when no body is available.
 
-@defun read-response-body/bytevector r
-Reads the response body from @var{r}, as a bytevector. Returns @code{#f}
+When @var{keep-alive?} is @code{#f}, closing the returned port also closes
+@var{r}'s response port.
+@end deffn
+
+@deffn {Scheme Procedure} read-response-body r
+Read the response body from @var{r}, as a bytevector.  Returns @code{#f}
 if there was no response body.
-@end defun
+@end deffn
 
-@defun write-response-body/bytevector r bv
-Write @var{body}, a bytevector, to the port corresponding to the HTTP
+@deffn {Scheme Procedure} write-response-body r bv
+Write @var{bv}, a bytevector, to the port corresponding to the HTTP
 response @var{r}.
-@end defun
+@end deffn
 
 As with requests, the various headers that are typically associated with
 HTTP responses may be accessed with these dedicated accessors.
 @xref{HTTP Headers}, for more information on the format of parsed
 headers.
 
-@defun response-accept-ranges response [default=#f]
-@defunx response-age response [default='()]
-@defunx response-allow response [default='()]
-@defunx response-cache-control response [default='()]
-@defunx response-connection response [default='()]
-@defunx response-content-encoding response [default='()]
-@defunx response-content-language response [default='()]
-@defunx response-content-length response [default=#f]
-@defunx response-content-location response [default=#f]
-@defunx response-content-md5 response [default=#f]
-@defunx response-content-range response [default=#f]
-@defunx response-content-type response [default=#f]
-@defunx response-date response [default=#f]
-@defunx response-etag response [default=#f]
-@defunx response-expires response [default=#f]
-@defunx response-last-modified response [default=#f]
-@defunx response-location response [default=#f]
-@defunx response-pragma response [default='()]
-@defunx response-proxy-authenticate response [default=#f]
-@defunx response-retry-after response [default=#f]
-@defunx response-server response [default=#f]
-@defunx response-trailer response [default='()]
-@defunx response-transfer-encoding response [default='()]
-@defunx response-upgrade response [default='()]
-@defunx response-vary response [default='()]
-@defunx response-via response [default='()]
-@defunx response-warning response [default='()]
-@defunx response-www-authenticate response [default=#f]
-Return the given request header, or @var{default} if none was present.
-@end defun
+@deffn {Scheme Procedure} response-accept-ranges response [default=#f]
+@deffnx {Scheme Procedure} response-age response [default='()]
+@deffnx {Scheme Procedure} response-allow response [default='()]
+@deffnx {Scheme Procedure} response-cache-control response [default='()]
+@deffnx {Scheme Procedure} response-connection response [default='()]
+@deffnx {Scheme Procedure} response-content-encoding response [default='()]
+@deffnx {Scheme Procedure} response-content-language response [default='()]
+@deffnx {Scheme Procedure} response-content-length response [default=#f]
+@deffnx {Scheme Procedure} response-content-location response [default=#f]
+@deffnx {Scheme Procedure} response-content-md5 response [default=#f]
+@deffnx {Scheme Procedure} response-content-range response [default=#f]
+@deffnx {Scheme Procedure} response-content-type response [default=#f]
+@deffnx {Scheme Procedure} response-date response [default=#f]
+@deffnx {Scheme Procedure} response-etag response [default=#f]
+@deffnx {Scheme Procedure} response-expires response [default=#f]
+@deffnx {Scheme Procedure} response-last-modified response [default=#f]
+@deffnx {Scheme Procedure} response-location response [default=#f]
+@deffnx {Scheme Procedure} response-pragma response [default='()]
+@deffnx {Scheme Procedure} response-proxy-authenticate response [default=#f]
+@deffnx {Scheme Procedure} response-retry-after response [default=#f]
+@deffnx {Scheme Procedure} response-server response [default=#f]
+@deffnx {Scheme Procedure} response-trailer response [default='()]
+@deffnx {Scheme Procedure} response-transfer-encoding response [default='()]
+@deffnx {Scheme Procedure} response-upgrade response [default='()]
+@deffnx {Scheme Procedure} response-vary response [default='()]
+@deffnx {Scheme Procedure} response-via response [default='()]
+@deffnx {Scheme Procedure} response-warning response [default='()]
+@deffnx {Scheme Procedure} response-www-authenticate response [default=#f]
+Return the given response header, or @var{default} if none was present.
+@end deffn
+
+@deffn {Scheme Procedure} text-content-type? @var{type}
+Return @code{#t} if @var{type}, a symbol as returned by
+@code{response-content-type}, represents a textual type such as
+@code{text/plain}.
+@end deffn
+
+
+@node Web Client
+@subsection Web Client
+
+@code{(web client)} provides a simple, synchronous HTTP client, built on
+the lower-level HTTP, request, and response modules.
+
+@example
+(use-modules (web client))
+@end example
+
+@deffn {Scheme Procedure} open-socket-for-uri uri
+Return an open input/output port for a connection to URI.
+@end deffn
+
+@deffn {Scheme Procedure} http-get uri arg...
+@deffnx {Scheme Procedure} http-head uri arg...
+@deffnx {Scheme Procedure} http-post uri arg...
+@deffnx {Scheme Procedure} http-put uri arg...
+@deffnx {Scheme Procedure} http-delete uri arg...
+@deffnx {Scheme Procedure} http-trace uri arg...
+@deffnx {Scheme Procedure} http-options uri arg...
+
+Connect to the server corresponding to @var{uri} and make a request over
+HTTP, using the appropriate method (@code{GET}, @code{HEAD}, etc.).
+
+All of these procedures have the same prototype: a URI followed by an
+optional sequence of keyword arguments.  These keyword arguments allow
+you to modify the requests in various ways, for example attaching a body
+to the request, or setting specific headers.  The following table lists
+the keyword arguments and their default values.
+
+@table @code
+@item #:body #f
+@item #:port (open-socket-for-uri @var{uri})]
+@item #:version '(1 . 1)
+@item #:keep-alive? #f
+@item #:headers '()
+@item #:decode-body? #t
+@item #:streaming? #f
+@end table
+
+If you already have a port open, pass it as @var{port}.  Otherwise, a
+connection will be opened to the server corresponding to @var{uri}.  Any
+extra headers in the alist @var{headers} will be added to the request.
+
+If @var{body} is not @code{#f}, a message body will also be sent with
+the HTTP request.  If @var{body} is a string, it is encoded according to
+the content-type in @var{headers}, defaulting to UTF-8.  Otherwise
+@var{body} should be a bytevector, or @code{#f} for no body.  Although a
+message body may be sent with any request, usually only @code{POST} and
+@code{PUT} requests have bodies.
+
+If @var{decode-body?} is true, as is the default, the body of the
+response will be decoded to string, if it is a textual content-type.
+Otherwise it will be returned as a bytevector.
+
+However, if @var{streaming?} is true, instead of eagerly reading the
+response body from the server, this function only reads off the headers.
+The response body will be returned as a port on which the data may be
+read.
+
+Unless @var{keep-alive?} is true, the port will be closed after the full
+response body has been read.
+
+Returns two values: the response read from the server, and the response
+body as a string, bytevector, #f value, or as a port (if
+@var{streaming?} is true).
+@end deffn
+
+@code{http-get} is useful for making one-off requests to web sites.  If
+you are writing a web spider or some other client that needs to handle a
+number of requests in parallel, it's better to build an event-driven URL
+fetcher, similar in structure to the web server (@pxref{Web Server}).
+
+Another option, good but not as performant, would be to use threads,
+possibly via par-map or futures.
+
+@deffn {Scheme Parameter} current-http-proxy
+Either @code{#f} or a non-empty string containing the URL of the HTTP
+proxy server to be used by the procedures in the @code{(web client)}
+module, including @code{open-socket-for-uri}.  Its initial value is
+based on the @env{http_proxy} environment variable.
+
+@example
+(current-http-proxy) @result{} "http://localhost:8123/"
+(parameterize ((current-http-proxy #f))
+  (http-get "http://example.com/"))  ; temporarily bypass proxy
+(current-http-proxy) @result{} "http://localhost:8123/"
+@end example
+@end deffn
 
 
 @node Web Server
@@ -960,8 +1526,8 @@ The life cycle of a server goes as follows:
 
 @enumerate
 @item
-The @code{open} hook is called, to open the server. @code{open} takes 0 or
-more arguments, depending on the backend, and returns an opaque
+The @code{open} hook is called, to open the server. @code{open} takes
+zero or more arguments, depending on the backend, and returns an opaque
 server socket object, or signals an error.
 
 @item
@@ -976,13 +1542,20 @@ If the read failed, the @code{read} hook may return #f for the client
 socket, request, and body.
 
 @item
-A user-provided handler procedure is called, with the request
-and body as its arguments.  The handler should return two
-values: the response, as a @code{<response>} record from @code{(web
-response)}, and the response body as a string, bytevector, or
-@code{#f} if not present.  We also allow the reponse to be simply an
-alist of headers, in which case a default response object is
-constructed with those headers.
+A user-provided handler procedure is called, with the request and body
+as its arguments.  The handler should return two values: the response,
+as a @code{<response>} record from @code{(web response)}, and the
+response body as bytevector, or @code{#f} if not present.
+
+The respose and response body are run through @code{sanitize-response},
+documented below.  This allows the handler writer to take some
+convenient shortcuts: for example, instead of a @code{<response>}, the
+handler can simply return an alist of headers, in which case a default
+response object is constructed with those headers.  Instead of a
+bytevector for the body, the handler can return a string, which will be
+serialized into an appropriate encoding; or it can return a procedure,
+which will be called on a port to write out the data.  See the
+@code{sanitize-response} documentation, for more.
 
 @item
 The @code{write} hook is called with three arguments: the client
@@ -1000,21 +1573,21 @@ the server socket.
 
 A user may define a server implementation with the following form:
 
-@defun define-server-impl name open read write close
+@deffn {Scheme Syntax} define-server-impl name open read write close
 Make a @code{<server-impl>} object with the hooks @var{open},
 @var{read}, @var{write}, and @var{close}, and bind it to the symbol
 @var{name} in the current module.
-@end defun
+@end deffn
 
-@defun lookup-server-impl impl
-Look up a server implementation. If @var{impl} is a server
-implementation already, it is returned directly. If it is a symbol, the
+@deffn {Scheme Procedure} lookup-server-impl impl
+Look up a server implementation.  If @var{impl} is a server
+implementation already, it is returned directly.  If it is a symbol, the
 binding named @var{impl} in the @code{(web server @var{impl})} module is
-looked up. Otherwise an error is signaled.
+looked up.  Otherwise an error is signaled.
 
 Currently a server implementation is a somewhat opaque type, useful only
 for passing to other procedures in this module, like @code{read-client}.
-@end defun
+@end deffn
 
 The @code{(web server)} module defines a number of routines that use
 @code{<server-impl>} objects to implement parts of a web server.  Given
@@ -1022,101 +1595,118 @@ that we don't expose the accessors for the various fields of a
 @code{<server-impl>}, indeed these routines are the only procedures with
 any access to the impl objects.
 
-@defun open-server impl open-params
-Open a server for the given implementation. Returns one value, the new
-server object. The implementation's @code{open} procedure is applied to
+@deffn {Scheme Procedure} open-server impl open-params
+Open a server for the given implementation.  Return one value, the new
+server object.  The implementation's @code{open} procedure is applied to
 @var{open-params}, which should be a list.
-@end defun
+@end deffn
 
-@defun read-client impl server
+@deffn {Scheme Procedure} read-client impl server
 Read a new client from @var{server}, by applying the implementation's
-@code{read} procedure to the server. If successful, returns three
+@code{read} procedure to the server.  If successful, return three
 values: an object corresponding to the client, a request object, and the
-request body. If any exception occurs, returns @code{#f} for all three
+request body.  If any exception occurs, return @code{#f} for all three
 values.
-@end defun
+@end deffn
 
-@defun handle-request handler request body state
+@deffn {Scheme Procedure} handle-request handler request body state
 Handle a given request, returning the response and body.
 
 The response and response body are produced by calling the given
 @var{handler} with @var{request} and @var{body} as arguments.
 
 The elements of @var{state} are also passed to @var{handler} as
-arguments, and may be returned as additional values. The new
+arguments, and may be returned as additional values.  The new
 @var{state}, collected from the @var{handler}'s return values, is then
-returned as a list. The idea is that a server loop receives a handler
+returned as a list.  The idea is that a server loop receives a handler
 from the user, along with whatever state values the user is interested
 in, allowing the user's handler to explicitly manage its state.
-@end defun
+@end deffn
 
-@defun sanitize-response request response body
-"Sanitize" the given response and body, making them appropriate for the
-given request.
+@deffn {Scheme Procedure} sanitize-response request response body
+``Sanitize'' the given response and body, making them appropriate for
+the given request.
 
 As a convenience to web handler authors, @var{response} may be given as
 an alist of headers, in which case it is used to construct a default
-response. Ensures that the response version corresponds to the request
-version. If @var{body} is a string, encodes the string to a bytevector,
-in an encoding appropriate for @var{response}. Adds a
+response.  Ensures that the response version corresponds to the request
+version.  If @var{body} is a string, encodes the string to a bytevector,
+in an encoding appropriate for @var{response}.  Adds a
 @code{content-length} and @code{content-type} header, as necessary.
 
 If @var{body} is a procedure, it is called with a port as an argument,
-and the output collected as a bytevector. In the future we might try to
+and the output collected as a bytevector.  In the future we might try to
 instead use a compressing, chunk-encoded port, and call this procedure
-later, in the write-client procedure. Authors are advised not to rely on
+later, in the write-client procedure.  Authors are advised not to rely on
 the procedure being called at any particular time.
-@end defun
+@end deffn
 
-@defun write-client impl server client response body
-Write an HTTP response and body to @var{client}. If the server and
+@deffn {Scheme Procedure} write-client impl server client response body
+Write an HTTP response and body to @var{client}.  If the server and
 client support persistent connections, it is the implementation's
 responsibility to keep track of the client thereafter, presumably by
 attaching it to the @var{server} argument somehow.
-@end defun
+@end deffn
 
-@defun close-server impl server
+@deffn {Scheme Procedure} close-server impl server
 Release resources allocated by a previous invocation of
 @code{open-server}.
-@end defun
+@end deffn
 
 Given the procedures above, it is a small matter to make a web server:
 
-@defun serve-one-client handler impl server state
+@deffn {Scheme Procedure} serve-one-client handler impl server state
 Read one request from @var{server}, call @var{handler} on the request
-and body, and write the response to the client. Returns the new state
+and body, and write the response to the client.  Return the new state
 produced by the handler procedure.
-@end defun
+@end deffn
 
-@defun run-server handler [impl] [open-params] . state
+@deffn {Scheme Procedure} run-server handler @
+                          [impl='http] [open-params='()] @
+                          arg @dots{}
 Run Guile's built-in web server.
 
 @var{handler} should be a procedure that takes two or more arguments,
 the HTTP request and request body, and returns two or more values, the
 response and response body.
 
-For example, here is a simple "Hello, World!" server:
-
-@example 
- (define (handler request body)
-   (values '((content-type . ("text/plain")))
-           "Hello, World!"))
- (run-server handler)
-@end example
+For examples, skip ahead to the next section, @ref{Web Examples}.
 
 The response and body will be run through @code{sanitize-response}
 before sending back to the client.
 
-Additional arguments to @var{handler} are taken from @var{state}.
-Additional return values are accumulated into a new @var{state}, which
-will be used for subsequent requests. In this way a handler can
-explicitly manage its state.
-
-The default server implementation is @code{http}, which accepts
-@var{open-params} like @code{(#:port 8081)}, among others. See "Web
-Server" in the manual, for more information.
-@end defun
+Additional arguments to @var{handler} are taken from @var{arg}
+@enddots{}.  These arguments comprise a @dfn{state}.  Additional return
+values are accumulated into a new state, which will be used for
+subsequent requests.  In this way a handler can explicitly manage its
+state.
+@end deffn
+
+The default web server implementation is @code{http}, which binds to a
+socket, listening for request on that port.
+
+@deffn {HTTP Implementation} http [#:host=#f] @
+                             [#:family=AF_INET] @
+                             [#:addr=INADDR_LOOPBACK] @
+                             [#:port 8080] [#:socket]
+The default HTTP implementation.  We document it as a function with
+keyword arguments, because that is precisely the way that it is -- all
+of the @var{open-params} to @code{run-server} get passed to the
+implementation's open function.
 
+@example
+;; The defaults: localhost:8080
+(run-server handler)
+;; Same thing
+(run-server handler 'http '())
+;; On a different port
+(run-server handler 'http '(#:port 8081))
+;; IPv6
+(run-server handler 'http '(#:family AF_INET6 #:port 8081))
+;; Custom socket
+(run-server handler 'http `(#:socket ,(sudo-make-me-a-socket)))
+@end example
+@end deffn
 
 @node Web Examples
 @subsection Web Examples
@@ -1143,7 +1733,7 @@ is our payload:
 
 @example
 (define (hello-world-handler request request-body)
-  (values '((content-type . ("text/plain")))
+  (values '((content-type . (text/plain)))
           "Hello World!"))
 @end example
 
@@ -1179,7 +1769,7 @@ the request, response, and URI modules, and do just that.
 (define (hello-hacker-handler request body)
   (if (equal? (request-path-components request)
               '("hacker"))
-      (values '((content-type . ("text/plain")))
+      (values '((content-type . (text/plain)))
               "Hello hacker!")
       (not-found request)))
 
@@ -1215,7 +1805,7 @@ probably know, we'll want to return a 404 response.
 (define (not-found request)
   (values (build-response #:code 404)
           (string-append "Resource not found: "
-                         (unparse-uri (request-uri request)))))
+                         (uri->string (request-uri request)))))
 
 ;; Now paste this to let the web server keep going:
 ,continue
@@ -1231,7 +1821,7 @@ message body is long enough.)
 The web handler interface is a common baseline that all kinds of Guile
 web applications can use.  You will usually want to build something on
 top of it, however, especially when producing HTML.  Here is a simple
-example that builds up HTML output using SXML (@pxref{sxml simple}).
+example that builds up HTML output using SXML (@pxref{SXML}).
 
 First, load up the modules:
 
@@ -1267,8 +1857,8 @@ strings. Now we define a little response helper:
                   (status 200)
                   (title "Hello hello!")
                   (doctype "<!DOCTYPE html>\n")
-                  (content-type-params '(("charset" . "utf-8")))
-                  (content-type "text/html")
+                  (content-type-params '((charset . "utf-8")))
+                  (content-type 'text/html)
                   (extra-headers '())
                   (sxml (and body (templatize title body))))
   (values (build-response
@@ -1287,9 +1877,9 @@ Here we see the power of keyword arguments with default initializers. By
 the time the arguments are fully parsed, the @code{sxml} local variable
 will hold the templated SXML, ready for sending out to the client.
 
-Instead of returning the body as a string, here we give a procedure,
-which will be called by the web server to write out the response to the
-client.
+Also, instead of returning the body as a string, @code{respond} gives a
+procedure, which will be called by the web server to write out the
+response to the client.
 
 Now, a simple example using this responder, which lays out the incoming
 headers in an HTML table.