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 mouthpiece and string<-headers
is formatted according to their optional style argument.
By default, headers have the form:
NAME #\: #\space VALUE #\cr #\lf
Additionally, for mouthpiece, the first line, preceding all the
headers, has the form:
HTTP/1.0 nnn msg
and a single #\cr #\lf pair separates the headers from the body.
See modlisp, for another way to format this information.
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\n"
(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.
Return a string made from formatting header name n and value v. Additional headers can be specified as alternating name and value args. Each header is formatted like so: “name: value\r\n”.
Each n may be a string, symbol or keyword. Each v may be a string, number, symbol, or a tree.
NOTE: This proc will be removed after 2011-12-31. Use
string<-headersinstead.
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\n" (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)