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
Host
header 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-version
pair- Set the major and minor version protocol-version numbers.
#:set-reply-status
number message- Set the reply status. message is a short string.
#: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.#:entity-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!
sock[
flags]
- Send the properly formatted response to file-port sock. It is an error to invoke
#:send-reply
without 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-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~%" (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.
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)