The (www server-utils answer) module provides a simple wrapper
around the formatting/accounting requirements of a standard HTTP
response. Additionally, the #:rechunk-content facility allows
some degree of performance tuning; a server may be able to achieve
better throughput with certain chunk sizes than with others.
The output from compose-response,
mouthpiece and string<-headers
is formatted according to their optional style argument.
By default, headers have the form:
NAME ": " VALUE CR LF
Additionally, for compose-response and
mouthpiece, the first line, preceding all the
headers, has the form:
"HTTP/" MAJOR "." MINOR SP NNN SP MSG
and a single CRLF pair separates the headers from the body.
(Actually, mouthpiece hardcodes the protocol version to ‘1.0’,
which is one reason why new code should use compose-response.)
See modlisp, for another way to format this information.
Keywords: style, protocol-versionReturn a command-delegating closure capable of writing a properly formatted HTTP 1.1 response with
Hostheader set to host. The actual status and header format is controlled by style, an opaque object. The actual protocol version is controlled by protocol-version, a pair of integers, such as(1 . 0)to indicate HTTP 1.0.The returned closure r accepts commands and args:
#:set-protocol-versionpair- Set the major and minor version protocol-version numbers.
#:set-reply-statusnumber message- Set the reply status. message is a short string.
#:add-headername value- name may be
#f,#t, a string, symbol or keyword. value is a string. If name is#for#t, value is taken to be a pre-formatted string, "A: B" or "A: B\r\n", respectively. If name is not a boolean, value may also be a tree of strings or a number.#:add-content [tree...]- tree may be a string, a nested list of strings, or a series of such. Subsequent calls to
#:add-contentappend their trees to the collected content tree thus far.#:add-formattedformat-string[args...]- format-string may be
#fto mean~S,#tto mean~A, or a normal format string. It is used to format args, and the result passed to#:add-content.#:add-direct-writerlen write- len is the number of bytes that procedure write will output to its arg, out-port (passed back), when called during
#:send-reply. This is to allow sendfile(2) and related hackery.#:entity-length- Return the total number of bytes in the content added thus far.
#:rechunk-contentchunk- chunk may be
#f, in which case a list of the string lengths collected thus far is returned;#twhich means to use the content length as the chunk size (effectively producing one chunk); or a number specifying the maximum size of a chunk. The return value is a list of the chunk sizes.It is an error to use
#:rechunk-contentwith a non-#fchunk in the presence of a previous#:add-direct-writer.#:inhibit-content!bool- Non-
#fbool arranges for#:send-reply(below) to compute content length and add the appropriate header, as usual, but no content is actually sent. This is useful, e.g., when answering aHEADrequest. If bool is#f,#:send-replyacts normally (i.e., sends both headers and content).#:send!sock[flags]- Send the properly formatted response to file-port sock. It is an error to invoke
#:send-replywithout having first set the reply status.Optional arg flags are the same as for
send-request. See http.
Return a command-delegating closure capable of writing a properly formatted HTTP 1.0 response to out-port. Optional arg status-box is a list whose car is set to the numeric status code given to a
#:set-reply-statuscommand. If status-box has length of two or more, its cadr is set to the content-length on#:send-reply. A content-length value of#fmeans there have been no calls to#:add-content. The commands and their args are:
#:reset-protocol!- Reset internal state, including reply status, headers and content. This is called automatically by
#:send-reply.#:set-reply-statusnumber message- Set the reply status. message is a short string.
#:set-reply-status:success- This is equivalent to
#:set-reply-status 200 "OK".#:add-headername value- name may be
#f,#t, a string, symbol or keyword. value is a string. If name is#for#t, value is taken to be a pre-formatted string, "A: B" or "A: B\r\n", respectively. If name is not a boolean, value may also be a tree of strings or a number.#:add-content [tree...]- tree may be a string, a nested list of strings, or a series of such. Subsequent calls to
#:add-contentappend their trees to the collected content tree thus far.#:add-formattedformat-string[args...]- format-string may be
#fto mean~S,#tto mean~A, or a normal format string. It is used to format args, and the result passed to#:add-content.#:add-direct-writerlen write- len is the number of bytes that procedure write will output to its arg, out-port (passed back), when called during
#:send-reply. This is to allow sendfile(2) and related hackery.#:content-length- Return the total number of bytes in the content added thus far.
#:rechunk-contentchunk- chunk may be
#f, in which case a list of the string lengths collected thus far is returned;#twhich means to use the content length as the chunk size (effectively producing one chunk); or a number specifying the maximum size of a chunk. The return value is a list of the chunk sizes.It is an error to use
#:rechunk-contentwith a non-#fchunk in the presence of a previous#:add-direct-writer.#:inhibit-content!bool- Non-
#fbool arranges for#:send-reply(below) to compute content length and add the appropriate header, as usual, but no content is actually sent. This is useful, e.g., when answering aHEADrequest. If bool is#f,#:send-replyacts normally (i.e., sends both headers and content).#:send-reply [close]- Send the properly formatted response to out-port, and reset all internal state (status reset, content discarded, etc). It is an error to invoke
#:send-replywithout having first set the reply status.Optional arg close means do a
shutdownon out-port using close — directly, if an integer, or called with no arguments, if a thunk — as the shutdownhowargument. (Note: If out-port is not a socket, this does nothing silently.) See Network Sockets and Communication.If close is specified, the closure forgets about out-port internally; it is an error to call other mouthpiece commands, subsequently.
Here is an example that uses most of the mouthpiece commands:
(use-modules (www server-utils filesystem) (scripts slurp))
(define SERVER-NAME "Guile-WWW-example-server")
(define SERVER-VERSION "1.0")
(define STATUS (list #f #f))
(define M (mouthpiece (open-output-file "fake") STATUS))
(define (transmit-file filename)
(M #:set-reply-status:success)
(M #:add-header 'Server (string-append SERVER-NAME "/"
SERVER-VERSION))
(M #:add-header 'Connection "close")
(M #:add-header 'Content-Type (filename->content-type
filename "text/plain"))
(M #:add-content (slurp filename))
(simple-format #t "rechunked: ~A~%"
(M #:rechunk-content (* 8 1024)))
;; We don't shutdown because this is a file port;
;; if it were a socket, we might specify 2 to
;; stop both reception and transmission.
(M #:send-reply))
(transmit-file "COPYING")
-| rechunked: (8192 8192 1605)
STATUS
⇒ (200 17989)
For higher performance, you can preformat parts of the response, using
CRLF, and some lower-level convenience procedures.
If preformatting is not possible (or desirable), you can still
declare a nested list of strings (aka tree) to have a
flat length, i.e., the size in bytes a tree would occupy
once flattened, thus enabling internal optimizations.
(The flat length of a string is its string-length.)
Return a new string made by using format string s on args. As in
simple-format(which this procedure uses),~Aexpands as withdisplay, while~Sexpands as withwrite.
Call proc for each recursively-visited leaf in tree, excluding empty lists. It is an error for tree to contain improper lists.
If tree is a string, return its
string-length. If tree already has aflat-length, return that. Otherwise, recursively compute, set, and return theflat-lengthof tree.
Return a new string made from flattening tree. Set the
flat-length(usingtree-flat-length!) of tree by side effect.
Return a string made from formatting name/value pairs in alist, according to the optional
styleargument. If unspecified or specified as#f, the default is to format headers like so:NAME #\: #\space VALUE #\cr #\lfEach name may be a string, symbol or keyword. Each value may be a string, number, symbol, or a tree.
Here is transmit-file from the above example, slightly modified to use
preformatted headers and fs:
(define CONSTANT-HEADERS
(string<-headers
`((#:Server . ,(fs "~A ~A" SERVER-NAME SERVER-VERSION))
(#:Connection . "close"))))
(define (transmit-file filename)
(M #:set-reply-status:success)
(M #:add-header #t CONSTANT-HEADERS)
(M #:add-header 'Content-Type (filename->content-type
filename "text/plain"))
(M #:add-content (slurp filename))
(display (fs "rechunked: ~A~%" (M #:rechunk-content (* 8 1024))))
(M #:send-reply))
Note that mouthpiece accepts trees for both #:add-header and
#:add-content commands. Thus, the following two fragments give the
same result, although the latter is both more elegant and more efficient:
;; Doing things "manually".
(walk-tree (lambda (string)
(M #:add-content string))
tree)
;; Letting the mouthpiece handle things.
(M #:add-content tree)