1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-23 20:05:32 +02:00

syncronize web module docstrings with manual

* doc/ref/web.texi: Fix spacing.  Update with a few missing function
  descriptions.

* module/web/client.scm:
* module/web/http.scm:
* module/web/request.scm:
* module/web/server.scm:
* module/web/uri.scm: Update docstrings from manual (reworked by Ludovic
  Courtès).
This commit is contained in:
Daniel Hartwig 2012-11-24 14:10:12 +08:00 committed by Ludovic Courtès
parent e8772a9ede
commit 06883ae000
7 changed files with 170 additions and 144 deletions

View file

@ -431,8 +431,8 @@ from @code{header-writer}.
@end deffn @end deffn
@deffn {Scheme Procedure} read-headers port @deffn {Scheme Procedure} read-headers port
Read the headers of an HTTP message from @var{port}, returning the Read the headers of an HTTP message from @var{port}, returning them
headers as an ordered alist. as an ordered alist.
@end deffn @end deffn
@deffn {Scheme Procedure} write-headers headers port @deffn {Scheme Procedure} write-headers headers port
@ -1368,6 +1368,7 @@ Return the given response header, or @var{default} if none was present.
the lower-level HTTP, request, and response modules. the lower-level HTTP, request, and response modules.
@deffn {Scheme Procedure} open-socket-for-uri uri @deffn {Scheme Procedure} open-socket-for-uri uri
Return an open input/output port for a connection to URI.
@end deffn @end deffn
@deffn {Scheme Procedure} http-get uri [#:port=(open-socket-for-uri uri)] [#:version='(1 . 1)] [#:keep-alive?=#f] [#:extra-headers='()] [#:decode-body?=#t] @deffn {Scheme Procedure} http-get uri [#:port=(open-socket-for-uri uri)] [#:version='(1 . 1)] [#:keep-alive?=#f] [#:extra-headers='()] [#:decode-body?=#t]
@ -1470,17 +1471,17 @@ the server socket.
A user may define a server implementation with the following form: A user may define a server implementation with the following form:
@deffn {Scheme Procedure} 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}, 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{read}, @var{write}, and @var{close}, and bind it to the symbol
@var{name} in the current module. @var{name} in the current module.
@end deffn @end deffn
@deffn {Scheme Procedure} lookup-server-impl impl @deffn {Scheme Procedure} lookup-server-impl impl
Look up a server implementation. If @var{impl} is a server Look up a server implementation. If @var{impl} is a server
implementation already, it is returned directly. If it is a symbol, the 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 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 Currently a server implementation is a somewhat opaque type, useful only
for passing to other procedures in this module, like @code{read-client}. for passing to other procedures in this module, like @code{read-client}.
@ -1494,7 +1495,7 @@ any access to the impl objects.
@deffn {Scheme Procedure} open-server impl open-params @deffn {Scheme Procedure} open-server impl open-params
Open a server for the given implementation. Return one value, the new Open a server for the given implementation. Return one value, the new
server object. The implementation's @code{open} procedure is applied to server object. The implementation's @code{open} procedure is applied to
@var{open-params}, which should be a list. @var{open-params}, which should be a list.
@end deffn @end deffn
@ -1502,7 +1503,7 @@ server object. The implementation's @code{open} procedure is applied to
Read a new client from @var{server}, by applying the implementation's Read a new client from @var{server}, by applying the implementation's
@code{read} procedure to the server. If successful, return three @code{read} procedure to the server. If successful, return three
values: an object corresponding to the client, a request object, and the values: an object corresponding to the client, a request object, and the
request body. If any exception occurs, return @code{#f} for all three request body. If any exception occurs, return @code{#f} for all three
values. values.
@end deffn @end deffn
@ -1513,9 +1514,9 @@ The response and response body are produced by calling the given
@var{handler} with @var{request} and @var{body} as arguments. @var{handler} with @var{request} and @var{body} as arguments.
The elements of @var{state} are also passed to @var{handler} as 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 @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 from the user, along with whatever state values the user is interested
in, allowing the user's handler to explicitly manage its state. in, allowing the user's handler to explicitly manage its state.
@end deffn @end deffn
@ -1526,20 +1527,20 @@ given request.
As a convenience to web handler authors, @var{response} may be given as 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 an alist of headers, in which case it is used to construct a default
response. Ensures that the response version corresponds to the request response. Ensures that the response version corresponds to the request
version. If @var{body} is a string, encodes the string to a bytevector, version. If @var{body} is a string, encodes the string to a bytevector,
in an encoding appropriate for @var{response}. Adds a in an encoding appropriate for @var{response}. Adds a
@code{content-length} and @code{content-type} header, as necessary. @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, 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 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. the procedure being called at any particular time.
@end deffn @end deffn
@deffn {Scheme Procedure} write-client impl server client response body @deffn {Scheme Procedure} write-client impl server client response body
Write an HTTP response and body to @var{client}. If the server and Write an HTTP response and body to @var{client}. If the server and
client support persistent connections, it is the implementation's client support persistent connections, it is the implementation's
responsibility to keep track of the client thereafter, presumably by responsibility to keep track of the client thereafter, presumably by
attaching it to the @var{server} argument somehow. attaching it to the @var{server} argument somehow.
@ -1572,7 +1573,7 @@ before sending back to the client.
Additional arguments to @var{handler} are taken from @var{state}. Additional arguments to @var{handler} are taken from @var{state}.
Additional return values are accumulated into a new @var{state}, which Additional return values are accumulated into a new @var{state}, which
will be used for subsequent requests. In this way a handler can will be used for subsequent requests. In this way a handler can
explicitly manage its state. explicitly manage its state.
@end deffn @end deffn

View file

@ -115,6 +115,15 @@
(define* (http-get uri #:key (port (open-socket-for-uri uri)) (define* (http-get uri #:key (port (open-socket-for-uri uri))
(version '(1 . 1)) (keep-alive? #f) (extra-headers '()) (version '(1 . 1)) (keep-alive? #f) (extra-headers '())
(decode-body? #t)) (decode-body? #t))
"Connect to the server corresponding to URI and ask for the
resource, using the GET method. If you already have a port open,
pass it as PORT. The port will be closed at the end of the
request unless KEEP-ALIVE? is true. Any extra headers in the
alist EXTRA-HEADERS will be added to the request.
If 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."
(let ((req (build-request uri #:version version (let ((req (build-request uri #:version version
#:headers (if keep-alive? #:headers (if keep-alive?
extra-headers extra-headers

View file

@ -77,7 +77,7 @@
(define (string->header name) (define (string->header name)
"Parse @var{name} to a symbolic header name." "Parse NAME to a symbolic header name."
(string->symbol (string-downcase name))) (string->symbol (string-downcase name)))
(define-record-type <header-decl> (define-record-type <header-decl>
@ -100,12 +100,7 @@
validator validator
writer writer
#:key multiple?) #:key multiple?)
"Define a parser, validator, and writer for the HTTP header, @var{name}. "Declare a parser, validator, and writer for a given header."
@var{parser} should be a procedure that takes a string and returns a
Scheme value. @var{validator} is a predicate for whether the given
Scheme value is valid for this header. @var{writer} takes a value and a
port, and writes the value to the port."
(if (and (string? name) parser validator writer) (if (and (string? name) parser validator writer)
(let ((decl (make-header-decl name parser validator writer multiple?))) (let ((decl (make-header-decl name parser validator writer multiple?)))
(hashq-set! *declared-headers* (string->header name) decl) (hashq-set! *declared-headers* (string->header name) decl)
@ -113,34 +108,40 @@ port, and writes the value to the port."
(error "bad header decl" name parser validator writer multiple?))) (error "bad header decl" name parser validator writer multiple?)))
(define (header->string sym) (define (header->string sym)
"Return the string form for the header named @var{sym}." "Return the string form for the header named SYM."
(let ((decl (lookup-header-decl sym))) (let ((decl (lookup-header-decl sym)))
(if decl (if decl
(header-decl-name decl) (header-decl-name decl)
(string-titlecase (symbol->string sym))))) (string-titlecase (symbol->string sym)))))
(define (known-header? sym) (define (known-header? sym)
"Return @code{#t} if there are parsers and writers registered for this "Return #t iff SYM is a known header, with associated
header, otherwise @code{#f}." parsers and serialization procedures."
(and (lookup-header-decl sym) #t)) (and (lookup-header-decl sym) #t))
(define (header-parser sym) (define (header-parser sym)
"Returns a procedure to parse values for the given header." "Return the value parser for headers named 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."
(let ((decl (lookup-header-decl sym))) (let ((decl (lookup-header-decl sym)))
(if decl (if decl
(header-decl-parser decl) (header-decl-parser decl)
(lambda (x) x)))) (lambda (x) x))))
(define (header-validator sym) (define (header-validator sym)
"Returns a procedure to validate values for the given header." "Return a predicate which returns #t if the given value is valid
for headers named SYM. The default validator for unknown headers
is string?."
(let ((decl (lookup-header-decl sym))) (let ((decl (lookup-header-decl sym)))
(if decl (if decl
(header-decl-validator decl) (header-decl-validator decl)
string?))) string?)))
(define (header-writer sym) (define (header-writer sym)
"Returns a procedure to write values for the given header to a given "Return a procedure that writes values for headers named SYM to a
port." port. The resulting procedure takes two arguments: a value and a port.
The default writer is display."
(let ((decl (lookup-header-decl sym))) (let ((decl (lookup-header-decl sym)))
(if decl (if decl
(header-decl-writer decl) (header-decl-writer decl)
@ -173,7 +174,7 @@ port."
(define *eof* (call-with-input-string "" read)) (define *eof* (call-with-input-string "" read))
(define (read-header port) (define (read-header port)
"Reads one HTTP header from @var{port}. Returns two values: the header "Reads one HTTP header from PORT. Returns two values: the header
name and the parsed Scheme value. May raise an exception if the header name and the parsed Scheme value. May raise an exception if the header
was known but the value was invalid. was known but the value was invalid.
@ -195,32 +196,28 @@ body was reached (i.e., a blank line)."
(string-trim-both line char-set:whitespace (1+ delim))))))))) (string-trim-both line char-set:whitespace (1+ delim)))))))))
(define (parse-header sym val) (define (parse-header sym val)
"Parse @var{val}, a string, with the parser registered for the header "Parse VAL, a string, with the parser registered for the header
named @var{sym}. named SYM. Returns the parsed value."
Returns the parsed value. If a parser was not found, the value is
returned as a string."
((header-parser sym) val)) ((header-parser sym) val))
(define (valid-header? sym val) (define (valid-header? sym val)
"Returns a true value iff @var{val} is a valid Scheme value for the "Returns a true value iff VAL is a valid Scheme value for the
header with name @var{sym}." header with name SYM."
(if (symbol? sym) (if (symbol? sym)
((header-validator sym) val) ((header-validator sym) val)
(error "header name not a symbol" sym))) (error "header name not a symbol" sym)))
(define (write-header sym val port) (define (write-header sym val port)
"Writes the given header name and value to @var{port}. If @var{sym} "Write the given header name and value to PORT, using the writer
is a known header, uses the specific writer registered for that header. from header-writer."
Otherwise the value is written using @code{display}."
(display (header->string sym) port) (display (header->string sym) port)
(display ": " port) (display ": " port)
((header-writer sym) val port) ((header-writer sym) val port)
(display "\r\n" port)) (display "\r\n" port))
(define (read-headers port) (define (read-headers port)
"Read an HTTP message from @var{port}, returning the headers as an "Read the headers of an HTTP message from PORT, returning them
ordered alist." as an ordered alist."
(let lp ((headers '())) (let lp ((headers '()))
(call-with-values (lambda () (read-header port)) (call-with-values (lambda () (read-header port))
(lambda (k v) (lambda (k v)
@ -229,8 +226,8 @@ ordered alist."
(lp (acons k v headers))))))) (lp (acons k v headers)))))))
(define (write-headers headers port) (define (write-headers headers port)
"Write the given header alist to @var{port}. Doesn't write the final "Write the given header alist to PORT. Doesn't write the final
\\r\\n, as the user might want to add another header." @samp{\\r\\n}, as the user might want to add another header."
(let lp ((headers headers)) (let lp ((headers headers))
(if (pair? headers) (if (pair? headers)
(begin (begin
@ -981,9 +978,9 @@ ordered alist."
(define *known-versions* '()) (define *known-versions* '())
(define* (parse-http-version str #:optional (start 0) (end (string-length str))) (define* (parse-http-version str #:optional (start 0) (end (string-length str)))
"Parse an HTTP version from @var{str}, returning it as a major-minor "Parse an HTTP version from STR, returning it as a major-minor
pair. For example, @code{HTTP/1.1} parses as the pair of integers, pair. For example, HTTP/1.1 parses as the pair of integers,
@code{(1 . 1)}." (1 . 1)."
(or (let lp ((known *known-versions*)) (or (let lp ((known *known-versions*))
(and (pair? known) (and (pair? known)
(if (string= str (caar known) start end) (if (string= str (caar known) start end)
@ -998,7 +995,7 @@ pair. For example, @code{HTTP/1.1} parses as the pair of integers,
(bad-header-component 'http-version (substring str start end)))))) (bad-header-component 'http-version (substring str start end))))))
(define (write-http-version val port) (define (write-http-version val port)
"Write the given major-minor version pair to @var{port}." "Write the given major-minor version pair to PORT."
(display "HTTP/" port) (display "HTTP/" port)
(display (car val) port) (display (car val) port)
(display #\. port) (display #\. port)
@ -1019,8 +1016,8 @@ pair. For example, @code{HTTP/1.1} parses as the pair of integers,
;; ourselves the trouble of that case, and disallow the CONNECT method. ;; ourselves the trouble of that case, and disallow the CONNECT method.
;; ;;
(define* (parse-http-method str #:optional (start 0) (end (string-length str))) (define* (parse-http-method str #:optional (start 0) (end (string-length str)))
"Parse an HTTP method from @var{str}. The result is an upper-case "Parse an HTTP method from STR. The result is an upper-case
symbol, like @code{GET}." symbol, like GET."
(cond (cond
((string= str "GET" start end) 'GET) ((string= str "GET" start end) 'GET)
((string= str "HEAD" start end) 'HEAD) ((string= str "HEAD" start end) 'HEAD)
@ -1052,7 +1049,7 @@ not have to have a scheme or host name. The result is a URI object."
(bad-request "Invalid URI: ~a" (substring str start end)))))) (bad-request "Invalid URI: ~a" (substring str start end))))))
(define (read-request-line port) (define (read-request-line port)
"Read the first line of an HTTP request from @var{port}, returning "Read the first line of an HTTP request from PORT, returning
three values: the method, the URI, and the version." three values: the method, the URI, and the version."
(let* ((line (read-line* port)) (let* ((line (read-line* port))
(d0 (string-index line char-set:whitespace)) ; "delimiter zero" (d0 (string-index line char-set:whitespace)) ; "delimiter zero"
@ -1093,7 +1090,7 @@ three values: the method, the URI, and the version."
(display (uri-query uri) port)))) (display (uri-query uri) port))))
(define (write-request-line method uri version port) (define (write-request-line method uri version port)
"Write the first line of an HTTP request to @var{port}." "Write the first line of an HTTP request to PORT."
(display method port) (display method port)
(display #\space port) (display #\space port)
(let ((path (uri-path uri)) (let ((path (uri-path uri))
@ -1113,7 +1110,7 @@ three values: the method, the URI, and the version."
(display "\r\n" port)) (display "\r\n" port))
(define (read-response-line port) (define (read-response-line port)
"Read the first line of an HTTP response from @var{port}, returning "Read the first line of an HTTP response from PORT, returning
three values: the HTTP version, the response code, and the \"reason three values: the HTTP version, the response code, and the \"reason
phrase\"." phrase\"."
(let* ((line (read-line* port)) (let* ((line (read-line* port))
@ -1128,7 +1125,7 @@ phrase\"."
(bad-response "Bad Response-Line: ~s" line)))) (bad-response "Bad Response-Line: ~s" line))))
(define (write-response-line version code reason-phrase port) (define (write-response-line version code reason-phrase port)
"Write the first line of an HTTP response to @var{port}." "Write the first line of an HTTP response to PORT."
(write-http-version version port) (write-http-version version port)
(display #\space port) (display #\space port)
(display code port) (display code port)
@ -1833,10 +1830,10 @@ treated specially, and is just returned as a plain string."
(define* (make-chunked-input-port port #:key (keep-alive? #f)) (define* (make-chunked-input-port port #:key (keep-alive? #f))
"Returns a new port which translates HTTP chunked transfer encoded "Returns a new port which translates HTTP chunked transfer encoded
data from @var{port} into a non-encoded format. Returns eof when it has data from PORT into a non-encoded format. Returns eof when it has
read the final chunk from @var{port}. This does not necessarily mean read the final chunk from PORT. This does not necessarily mean
that there is no more data on @var{port}. When the returned port is that there is no more data on PORT. When the returned port is
closed it will also close @var{port}, unless the KEEP-ALIVE? is true." closed it will also close PORT, unless the KEEP-ALIVE? is true."
(define (next-chunk) (define (next-chunk)
(read-chunk port)) (read-chunk port))
(define finished? #f) (define finished? #f)
@ -1872,11 +1869,11 @@ closed it will also close @var{port}, unless the KEEP-ALIVE? is true."
(define* (make-chunked-output-port port #:key (keep-alive? #f)) (define* (make-chunked-output-port port #:key (keep-alive? #f))
"Returns a new port which translates non-encoded data into a HTTP "Returns a new port which translates non-encoded data into a HTTP
chunked transfer encoded data and writes this to @var{port}. Data chunked transfer encoded data and writes this to PORT. Data
written to this port is buffered until the port is flushed, at which written to this port is buffered until the port is flushed, at which
point it is all sent as one chunk. Take care to close the port when point it is all sent as one chunk. Take care to close the port when
done, as it will output the remaining data, and encode the final zero done, as it will output the remaining data, and encode the final zero
chunk. When the port is closed it will also close @var{port}, unless chunk. When the port is closed it will also close PORT, unless
KEEP-ALIVE? is true." KEEP-ALIVE? is true."
(define (q-for-each f q) (define (q-for-each f q)
(while (not (q-empty? q)) (while (not (q-empty? q))

View file

@ -160,7 +160,7 @@
(define* (build-request uri #:key (method 'GET) (version '(1 . 1)) (define* (build-request uri #:key (method 'GET) (version '(1 . 1))
(headers '()) port (meta '()) (headers '()) port (meta '())
(validate-headers? #t)) (validate-headers? #t))
"Construct an HTTP request object. If @var{validate-headers?} is true, "Construct an HTTP request object. If VALIDATE-HEADERS? is true,
the headers are each run through their respective validators." the headers are each run through their respective validators."
(let ((needs-host? (and (equal? version '(1 . 1)) (let ((needs-host? (and (equal? version '(1 . 1))
(not (assq-ref headers 'host))))) (not (assq-ref headers 'host)))))
@ -189,13 +189,17 @@ the headers are each run through their respective validators."
meta port))) meta port)))
(define* (read-request port #:optional (meta '())) (define* (read-request port #:optional (meta '()))
"Read an HTTP request from @var{port}, optionally attaching the given "Read an HTTP request from PORT, optionally attaching the given
metadata, @var{meta}. metadata, META.
As a side effect, sets the encoding on @var{port} to As a side effect, sets the encoding on PORT to
ISO-8859-1 (latin-1), so that reading one character reads one byte. See 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 the discussion of character sets in \"HTTP Requests\" in the manual, for
more information." more information.
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."
(set-port-encoding! port "ISO-8859-1") (set-port-encoding! port "ISO-8859-1")
(call-with-values (lambda () (read-request-line port)) (call-with-values (lambda () (read-request-line port))
(lambda (method uri version) (lambda (method uri version)
@ -203,10 +207,10 @@ more information."
;; FIXME: really return a new request? ;; FIXME: really return a new request?
(define (write-request r port) (define (write-request r port)
"Write the given HTTP request to @var{port}. "Write the given HTTP request to PORT.
Returns a new request, whose @code{request-port} will continue writing Return a new request, whose request-port will continue writing
on @var{port}, perhaps using some transfer encoding." on PORT, perhaps using some transfer encoding."
(write-request-line (request-method r) (request-uri r) (write-request-line (request-method r) (request-uri r)
(request-version r) port) (request-version r) port)
(write-headers (request-headers r) port) (write-headers (request-headers r) port)
@ -217,8 +221,8 @@ on @var{port}, perhaps using some transfer encoding."
(request-headers r) (request-meta r) port))) (request-headers r) (request-meta r) port)))
(define (read-request-body r) (define (read-request-body r)
"Reads the request body from @var{r}, as a bytevector. Returns "Reads the request body from R, as a bytevector. Return #f
@code{#f} if there was no request body." if there was no request body."
(let ((nbytes (request-content-length r))) (let ((nbytes (request-content-length r)))
(and nbytes (and nbytes
(let ((bv (get-bytevector-n (request-port r) nbytes))) (let ((bv (get-bytevector-n (request-port r) nbytes)))
@ -228,8 +232,8 @@ on @var{port}, perhaps using some transfer encoding."
(bytevector-length bv) nbytes)))))) (bytevector-length bv) nbytes))))))
(define (write-request-body r bv) (define (write-request-body r bv)
"Write @var{bv}, a bytevector, to the port corresponding to the HTTP "Write BV, a bytevector, to the port corresponding to the HTTP
request @var{r}." request R."
(put-bytevector (request-port r) bv)) (put-bytevector (request-port r) bv))
(define-syntax define-request-accessor (define-syntax define-request-accessor
@ -297,6 +301,8 @@ request @var{r}."
;; Misc accessors ;; Misc accessors
(define* (request-absolute-uri r #:optional default-host default-port) (define* (request-absolute-uri r #:optional default-host default-port)
"A helper routine to determine the absolute URI of a request, using the
host header and the default host and port."
(let ((uri (request-uri r))) (let ((uri (request-uri r)))
(if (uri-host uri) (if (uri-host uri)
uri uri

View file

@ -107,7 +107,7 @@
(define* (build-response #:key (version '(1 . 1)) (code 200) reason-phrase (define* (build-response #:key (version '(1 . 1)) (code 200) reason-phrase
(headers '()) port (validate-headers? #t)) (headers '()) port (validate-headers? #t))
"Construct an HTTP response object. If @var{validate-headers?} is true, "Construct an HTTP response object. If VALIDATE-HEADERS? is true,
the headers are each run through their respective validators." the headers are each run through their respective validators."
(cond (cond
((not (and (pair? version) ((not (and (pair? version)
@ -170,15 +170,15 @@ the headers are each run through their respective validators."
"(Unknown)")) "(Unknown)"))
(define (response-reason-phrase response) (define (response-reason-phrase response)
"Return the reason phrase given in @var{response}, or the standard "Return the reason phrase given in RESPONSE, or the standard
reason phrase for the response's code." reason phrase for the response's code."
(or (%response-reason-phrase response) (or (%response-reason-phrase response)
(code->reason-phrase (response-code response)))) (code->reason-phrase (response-code response))))
(define (read-response port) (define (read-response port)
"Read an HTTP response from @var{port}. "Read an HTTP response from PORT.
As a side effect, sets the encoding on @var{port} to As a side effect, sets the encoding on PORT to
ISO-8859-1 (latin-1), so that reading one character reads one byte. See 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, the discussion of character sets in \"HTTP Responses\" in the manual,
for more information." for more information."
@ -202,10 +202,10 @@ the version field."
#:port (response-port response))) #:port (response-port response)))
(define (write-response r port) (define (write-response r port)
"Write the given HTTP response to @var{port}. "Write the given HTTP response to PORT.
Returns a new response, whose @code{response-port} will continue writing Returns a new response, whose response-port will continue writing
on @var{port}, perhaps using some transfer encoding." on PORT, perhaps using some transfer encoding."
(write-response-line (response-version r) (response-code r) (write-response-line (response-version r) (response-code r)
(response-reason-phrase r) port) (response-reason-phrase r) port)
(write-headers (response-headers r) port) (write-headers (response-headers r) port)
@ -216,7 +216,7 @@ on @var{port}, perhaps using some transfer encoding."
(response-reason-phrase r) (response-headers r) port))) (response-reason-phrase r) (response-headers r) port)))
(define (response-must-not-include-body? r) (define (response-must-not-include-body? r)
"Returns @code{#t} if the response @var{r} is not permitted to have a body. "Returns #t if the response R is not permitted to have a body.
This is true for some response types, like those with code 304." This is true for some response types, like those with code 304."
;; RFC 2616, section 4.3. ;; RFC 2616, section 4.3.
@ -225,8 +225,8 @@ This is true for some response types, like those with code 304."
(= (response-code r) 304))) (= (response-code r) 304)))
(define (read-response-body r) (define (read-response-body r)
"Reads the response body from @var{r}, as a bytevector. Returns "Reads the response body from R, as a bytevector. Returns
@code{#f} if there was no response body." #f if there was no response body."
(if (member '(chunked) (response-transfer-encoding r)) (if (member '(chunked) (response-transfer-encoding r))
(let ((chunk-port (make-chunked-input-port (response-port r) (let ((chunk-port (make-chunked-input-port (response-port r)
#:keep-alive? #t))) #:keep-alive? #t)))
@ -240,8 +240,8 @@ This is true for some response types, like those with code 304."
(bytevector-length bv) nbytes))))))) (bytevector-length bv) nbytes)))))))
(define (write-response-body r bv) (define (write-response-body r bv)
"Write @var{bv}, a bytevector, to the port corresponding to the HTTP "Write BV, a bytevector, to the port corresponding to the HTTP
response @var{r}." response R."
(put-bytevector (response-port r) bv)) (put-bytevector (response-port r) bv))
(define-syntax define-response-accessor (define-syntax define-response-accessor

View file

@ -123,14 +123,14 @@
(make-server-impl 'name open read write close))) (make-server-impl 'name open read write close)))
(define (lookup-server-impl impl) (define (lookup-server-impl impl)
"Look up a server implementation. If @var{impl} is a server "Look up a server implementation. If IMPL is a server
implementation already, it is returned directly. If it is a symbol, the 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 binding named IMPL in the (web server 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 Currently a server implementation is a somewhat opaque type, useful only
for passing to other procedures in this module, like for passing to other procedures in this module, like
@code{read-client}." read-client."
(cond (cond
((server-impl? impl) impl) ((server-impl? impl) impl)
((symbol? impl) ((symbol? impl)
@ -143,17 +143,17 @@ for passing to other procedures in this module, like
;; -> server ;; -> server
(define (open-server impl open-params) (define (open-server impl open-params)
"Open a server for the given implementation. Returns one value, the "Open a server for the given implementation. Return one value, the
new server object. The implementation's @code{open} procedure is new server object. The implementation's open procedure is
applied to @var{open-params}, which should be a list." applied to OPEN-PARAMS, which should be a list."
(apply (server-impl-open impl) open-params)) (apply (server-impl-open impl) open-params))
;; -> (client request body | #f #f #f) ;; -> (client request body | #f #f #f)
(define (read-client impl server) (define (read-client impl server)
"Read a new client from @var{server}, by applying the implementation's "Read a new client from SERVER, by applying the implementation's
@code{read} procedure to the server. If successful, returns three read procedure to the server. If successful, return three
values: an object corresponding to the client, a request object, and the 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 #f for all three
values." values."
(call-with-error-handling (call-with-error-handling
(lambda () (lambda ()
@ -213,14 +213,14 @@ values."
"\"Sanitize\" the given response and body, making them appropriate for "\"Sanitize\" the given response and body, making them appropriate for
the given request. the given request.
As a convenience to web handler authors, @var{response} may be given as As a convenience to web handler authors, RESPONSE may be given as
an alist of headers, in which case it is used to construct a default an alist of headers, in which case it is used to construct a default
response. Ensures that the response version corresponds to the request response. Ensures that the response version corresponds to the request
version. If @var{body} is a string, encodes the string to a bytevector, version. If BODY is a string, encodes the string to a bytevector,
in an encoding appropriate for @var{response}. Adds a in an encoding appropriate for RESPONSE. Adds a
@code{content-length} and @code{content-type} header, as necessary. content-length and content-type header, as necessary.
If @var{body} is a procedure, it is called with a port as an argument, If 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 instead use a compressing, chunk-encoded port, and call this procedure
later, in the write-client procedure. Authors are advised not to rely later, in the write-client procedure. Authors are advised not to rely
@ -290,11 +290,11 @@ on the procedure being called at any particular time."
"Handle a given request, returning the response and body. "Handle a given request, returning the response and body.
The response and response body are produced by calling the given The response and response body are produced by calling the given
@var{handler} with @var{request} and @var{body} as arguments. HANDLER with REQUEST and BODY as arguments.
The elements of @var{state} are also passed to @var{handler} as The elements of STATE are also passed to 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 STATE, collected from the 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 from the user, along with whatever state values the user is interested
in, allowing the user's handler to explicitly manage its state." in, allowing the user's handler to explicitly manage its state."
@ -318,10 +318,10 @@ in, allowing the user's handler to explicitly manage its state."
;; -> unspecified values ;; -> unspecified values
(define (write-client impl server client response body) (define (write-client impl server client response body)
"Write an HTTP response and body to @var{client}. If the server and "Write an HTTP response and body to CLIENT. If the server and
client support persistent connections, it is the implementation's client support persistent connections, it is the implementation's
responsibility to keep track of the client thereafter, presumably by responsibility to keep track of the client thereafter, presumably by
attaching it to the @var{server} argument somehow." attaching it to the SERVER argument somehow."
(call-with-error-handling (call-with-error-handling
(lambda () (lambda ()
((server-impl-write impl) server client response body)) ((server-impl-write impl) server client response body))
@ -332,7 +332,7 @@ attaching it to the @var{server} argument somehow."
;; -> unspecified values ;; -> unspecified values
(define (close-server impl server) (define (close-server impl server)
"Release resources allocated by a previous invocation of "Release resources allocated by a previous invocation of
@code{open-server}." open-server."
((server-impl-close impl) server)) ((server-impl-close impl) server))
(define call-with-sigint (define call-with-sigint
@ -363,8 +363,8 @@ attaching it to the @var{server} argument somehow."
;; -> new-state ;; -> new-state
(define (serve-one-client handler impl server state) (define (serve-one-client handler impl server state)
"Read one request from @var{server}, call @var{handler} on the request "Read one request from SERVER, call 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." produced by the handler procedure."
(debug-elapsed 'serve-again) (debug-elapsed 'serve-again)
(call-with-values (call-with-values
@ -387,7 +387,7 @@ produced by the handler procedure."
. state) . state)
"Run Guile's built-in web server. "Run Guile's built-in web server.
@var{handler} should be a procedure that takes two or more arguments, HANDLER should be a procedure that takes two or more arguments,
the HTTP request and request body, and returns two or more values, the the HTTP request and request body, and returns two or more values, the
response and response body. response and response body.
@ -400,16 +400,16 @@ For example, here is a simple \"Hello, World!\" server:
(run-server handler) (run-server handler)
@end example @end example
The response and body will be run through @code{sanitize-response} The response and body will be run through sanitize-response
before sending back to the client. before sending back to the client.
Additional arguments to @var{handler} are taken from Additional arguments to HANDLER are taken from
@var{state}. Additional return values are accumulated into a new STATE. Additional return values are accumulated into a new
@var{state}, which will be used for subsequent requests. In this way a STATE, which will be used for subsequent requests. In this way a
handler can explicitly manage its state. handler can explicitly manage its state.
The default server implementation is @code{http}, which accepts The default server implementation is http, which accepts
@var{open-params} like @code{(#:port 8081)}, among others. See \"Web OPEN-PARAMS like (#:port 8081), among others. See \"Web
Server\" in the manual, for more information." Server\" in the manual, for more information."
(let* ((impl (lookup-server-impl impl)) (let* ((impl (lookup-server-impl impl))
(server (open-server impl open-params))) (server (open-server impl open-params)))

View file

@ -79,8 +79,10 @@
(define* (build-uri scheme #:key userinfo host port (path "") query fragment (define* (build-uri scheme #:key userinfo host port (path "") query fragment
(validate? #t)) (validate? #t))
"Construct a URI object. If @var{validate?} is true, also run some "Construct a URI object. SCHEME should be a symbol, and the rest
consistency checks to make sure that the constructed URI is valid." of the fields are either strings or #f. If VALIDATE? is
true, also run some consistency checks to make sure that the constructed
URI is valid."
(if validate? (if validate?
(validate-uri scheme userinfo host port path query fragment)) (validate-uri scheme userinfo host port path query fragment))
(make-uri scheme userinfo host port path query fragment)) (make-uri scheme userinfo host port path query fragment))
@ -168,7 +170,7 @@ consistency checks to make sure that the constructed URI is valid."
(make-regexp uri-pat)) (make-regexp uri-pat))
(define (string->uri string) (define (string->uri string)
"Parse @var{string} into a URI object. Returns @code{#f} if the string "Parse STRING into a URI object. Return #f if the string
could not be parsed." could not be parsed."
(% (let ((m (regexp-exec uri-regexp string))) (% (let ((m (regexp-exec uri-regexp string)))
(if (not m) (abort)) (if (not m) (abort))
@ -191,10 +193,7 @@ could not be parsed."
(define *default-ports* (make-hash-table)) (define *default-ports* (make-hash-table))
(define (declare-default-port! scheme port) (define (declare-default-port! scheme port)
"Declare a default port for the given URI scheme. "Declare a default port for the given URI scheme."
Default ports are for printing URI objects: a default port is not
printed."
(hashq-set! *default-ports* scheme port)) (hashq-set! *default-ports* scheme port))
(define (default-port? scheme port) (define (default-port? scheme port)
@ -205,7 +204,9 @@ printed."
(declare-default-port! 'https 443) (declare-default-port! 'https 443)
(define (uri->string uri) (define (uri->string uri)
"Serialize @var{uri} to a string." "Serialize 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."
(let* ((scheme-str (string-append (let* ((scheme-str (string-append
(symbol->string (uri-scheme uri)) ":")) (symbol->string (uri-scheme uri)) ":"))
(userinfo (uri-userinfo uri)) (userinfo (uri-userinfo uri))
@ -285,26 +286,32 @@ printed."
;; characters in other character sets. ;; characters in other character sets.
;; ;;
;; Return a new string made from uri-decoding @var{str}. Specifically, ;; Return a new string made from uri-decoding STR. Specifically,
;; turn @code{+} into space, and hex-encoded @code{%XX} strings into ;; turn + into space, and hex-encoded %XX strings into
;; their eight-bit characters. ;; their eight-bit characters.
;; ;;
(define hex-chars (define hex-chars
(string->char-set "0123456789abcdefABCDEF")) (string->char-set "0123456789abcdefABCDEF"))
(define* (uri-decode str #:key (encoding "utf-8")) (define* (uri-decode str #:key (encoding "utf-8"))
"Percent-decode the given @var{str}, according to @var{encoding}. "Percent-decode the given STR, according to ENCODING,
which should be the name of a character encoding.
Note that this function should not generally be applied to a full URI 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 string. For paths, use split-and-decode-uri-path instead. For query
strings, split the query on @code{&} and @code{=} boundaries, and decode strings, split the query on & and = boundaries, and decode
the components separately. the components separately.
Note that percent-encoded strings encode @emph{bytes}, not characters. Note also that percent-encoded strings encode @emph{bytes}, not
There is no guarantee that a given byte sequence is a valid string characters. There is no guarantee that a given byte sequence is a valid
encoding. Therefore this routine may signal an error if the decoded string encoding. Therefore this routine may signal an error if the
bytes are not valid for the given encoding. Pass @code{#f} for decoded bytes are not valid for the given encoding. Pass #f for
@var{encoding} if you want decoded bytes as a bytevector directly." ENCODING if you want decoded bytes as a bytevector directly.
@xref{Ports, set-port-encoding!}, for more information on
character encodings.
Returns a string of the decoded characters, or a bytevector if
ENCODING was #f."
(let* ((len (string-length str)) (let* ((len (string-length str))
(bv (bv
(call-with-output-bytevector* (call-with-output-bytevector*
@ -353,16 +360,19 @@ bytes are not valid for the given encoding. Pass @code{#f} for
(char-set-union ascii-alnum-chars (char-set-union ascii-alnum-chars
(string->char-set "-._~"))) (string->char-set "-._~")))
;; Return a new string made from uri-encoding @var{str}, unconditionally ;; Return a new string made from uri-encoding STR, unconditionally
;; transforming any characters not in @var{unescaped-chars}. ;; transforming any characters not in UNESCAPED-CHARS.
;; ;;
(define* (uri-encode str #:key (encoding "utf-8") (define* (uri-encode str #:key (encoding "utf-8")
(unescaped-chars unreserved-chars)) (unescaped-chars unreserved-chars))
"Percent-encode any character not in the character set, @var{unescaped-chars}. "Percent-encode any character not in the character set,
UNESCAPED-CHARS.
Percent-encoding first writes out the given character to a bytevector The default character set includes alphanumerics from ASCII, as well as
within the given @var{encoding}, then encodes each byte as the special characters @samp{-}, @samp{.}, @samp{_}, and @samp{~}. Any
@code{%@var{HH}}, where @var{HH} is the hexadecimal representation of other character will be percent-encoded, by writing out the character to
a bytevector within the given ENCODING, then encoding each byte as
%HH, where HH is the hexadecimal representation of
the byte." the byte."
(define (needs-escaped? ch) (define (needs-escaped? ch)
(not (char-set-contains? unescaped-chars ch))) (not (char-set-contains? unescaped-chars ch)))
@ -387,15 +397,18 @@ the byte."
str)) str))
(define (split-and-decode-uri-path path) (define (split-and-decode-uri-path path)
"Split @var{path} into its components, and decode each "Split PATH into its components, and decode each component,
component, removing empty components. removing empty components.
For example, @code{\"/foo/bar/\"} decodes to the two-element list, For example, \"/foo/bar%20baz/\" decodes to the two-element list,
@code{(\"foo\" \"bar\")}." (\"foo\" \"bar baz\")."
(filter (lambda (x) (not (string-null? x))) (filter (lambda (x) (not (string-null? x)))
(map uri-decode (string-split path #\/)))) (map uri-decode (string-split path #\/))))
(define (encode-and-join-uri-path parts) (define (encode-and-join-uri-path parts)
"URI-encode each element of @var{parts}, which should be a list of "URI-encode each element of PARTS, which should be a list of
strings, and join the parts together with @code{/} as a delimiter." strings, and join the parts together with / as a delimiter.
For example, the list (\"scrambled eggs\" \"biscuits&gravy\")
encodes as \"scrambled%20eggs/biscuits%26gravy\"."
(string-join (map uri-encode parts) "/")) (string-join (map uri-encode parts) "/"))