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-status
command. 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#f
means 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-status
number message- Set the reply status. message is a short string.
#:set-reply-status:success
- This is equivalent to
#:set-reply-status 200 "OK"
.#:add-header
name value- name may be
#f
,#t
, a string, symbol or keyword. value is a string. If name is#f
or#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-content
append their trees to the collected content tree thus far.#:add-formatted
format-string[
args...]
- format-string may be
#f
to mean~S
,#t
to mean~A
, or a normal format string. It is used to format args, and the result passed to#:add-content
.#:add-direct-writer
len 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-content
chunk- chunk may be
#f
, in which case a list of the string lengths collected thus far is returned;#t
which 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-content
with a non-#f
chunk in the presence of a previous#:add-direct-writer
.#:inhibit-content!
bool- Non-
#f
bool 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 aHEAD
request. If bool is#f
,#:send-reply
acts 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-reply
without having first set the reply status.Optional arg close means do a
shutdown
on out-port using close — directly, if an integer, or called with no arguments, if a thunk — as the shutdownhow
argument. (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),~A
expands as withdisplay
, while~S
expands 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-length
of 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
style
argument. 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<-headers
instead.
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)