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:
parent
e8772a9ede
commit
06883ae000
7 changed files with 170 additions and 144 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
|
@ -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) "/"))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue