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
@deffn {Scheme Procedure} read-headers port
Read the headers of an HTTP message from @var{port}, returning the
headers as an ordered alist.
Read the headers of an HTTP message from @var{port}, returning them
as an ordered alist.
@end deffn
@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.
@deffn {Scheme Procedure} open-socket-for-uri uri
Return an open input/output port for a connection to URI.
@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]
@ -1470,7 +1471,7 @@ the server socket.
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},
@var{read}, @var{write}, and @var{close}, and bind it to the symbol
@var{name} in the current module.

View file

@ -115,6 +115,15 @@
(define* (http-get uri #:key (port (open-socket-for-uri uri))
(version '(1 . 1)) (keep-alive? #f) (extra-headers '())
(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
#:headers (if keep-alive?
extra-headers

View file

@ -77,7 +77,7 @@
(define (string->header name)
"Parse @var{name} to a symbolic header name."
"Parse NAME to a symbolic header name."
(string->symbol (string-downcase name)))
(define-record-type <header-decl>
@ -100,12 +100,7 @@
validator
writer
#:key multiple?)
"Define a parser, validator, and writer for the HTTP header, @var{name}.
@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."
"Declare a parser, validator, and writer for a given header."
(if (and (string? name) parser validator writer)
(let ((decl (make-header-decl name parser validator writer multiple?)))
(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?)))
(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)))
(if decl
(header-decl-name decl)
(string-titlecase (symbol->string sym)))))
(define (known-header? sym)
"Return @code{#t} if there are parsers and writers registered for this
header, otherwise @code{#f}."
"Return #t iff SYM is a known header, with associated
parsers and serialization procedures."
(and (lookup-header-decl sym) #t))
(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)))
(if decl
(header-decl-parser decl)
(lambda (x) x))))
(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)))
(if decl
(header-decl-validator decl)
string?)))
(define (header-writer sym)
"Returns a procedure to write values for the given header to a given
port."
"Return a procedure that writes values for headers named SYM to a
port. The resulting procedure takes two arguments: a value and a port.
The default writer is display."
(let ((decl (lookup-header-decl sym)))
(if decl
(header-decl-writer decl)
@ -173,7 +174,7 @@ port."
(define *eof* (call-with-input-string "" read))
(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
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)))))))))
(define (parse-header sym val)
"Parse @var{val}, a string, with the parser registered for the header
named @var{sym}.
Returns the parsed value. If a parser was not found, the value is
returned as a string."
"Parse VAL, a string, with the parser registered for the header
named SYM. Returns the parsed value."
((header-parser sym) val))
(define (valid-header? sym val)
"Returns a true value iff @var{val} is a valid Scheme value for the
header with name @var{sym}."
"Returns a true value iff VAL is a valid Scheme value for the
header with name SYM."
(if (symbol? sym)
((header-validator sym) val)
(error "header name not a symbol" sym)))
(define (write-header sym val port)
"Writes the given header name and value to @var{port}. If @var{sym}
is a known header, uses the specific writer registered for that header.
Otherwise the value is written using @code{display}."
"Write the given header name and value to PORT, using the writer
from header-writer."
(display (header->string sym) port)
(display ": " port)
((header-writer sym) val port)
(display "\r\n" port))
(define (read-headers port)
"Read an HTTP message from @var{port}, returning the headers as an
ordered alist."
"Read the headers of an HTTP message from PORT, returning them
as an ordered alist."
(let lp ((headers '()))
(call-with-values (lambda () (read-header port))
(lambda (k v)
@ -229,8 +226,8 @@ ordered alist."
(lp (acons k v headers)))))))
(define (write-headers headers port)
"Write the given header alist to @var{port}. Doesn't write the final
\\r\\n, as the user might want to add another header."
"Write the given header alist to PORT. Doesn't write the final
@samp{\\r\\n}, as the user might want to add another header."
(let lp ((headers headers))
(if (pair? headers)
(begin
@ -981,9 +978,9 @@ ordered alist."
(define *known-versions* '())
(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
pair. For example, @code{HTTP/1.1} parses as the pair of integers,
@code{(1 . 1)}."
"Parse an HTTP version from STR, returning it as a major-minor
pair. For example, HTTP/1.1 parses as the pair of integers,
(1 . 1)."
(or (let lp ((known *known-versions*))
(and (pair? known)
(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))))))
(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 (car val) 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.
;;
(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
symbol, like @code{GET}."
"Parse an HTTP method from STR. The result is an upper-case
symbol, like GET."
(cond
((string= str "GET" start end) 'GET)
((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))))))
(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."
(let* ((line (read-line* port))
(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))))
(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 #\space port)
(let ((path (uri-path uri))
@ -1113,7 +1110,7 @@ three values: the method, the URI, and the version."
(display "\r\n" 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
phrase\"."
(let* ((line (read-line* port))
@ -1128,7 +1125,7 @@ phrase\"."
(bad-response "Bad Response-Line: ~s" line))))
(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)
(display #\space 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))
"Returns a new port which translates HTTP chunked transfer encoded
data from @var{port} into a non-encoded format. Returns eof when it has
read the final chunk from @var{port}. This does not necessarily mean
that there is no more data on @var{port}. When the returned port is
closed it will also close @var{port}, unless the KEEP-ALIVE? is true."
data from PORT into a non-encoded format. Returns eof when it has
read the final chunk from PORT. This does not necessarily mean
that there is no more data on PORT. When the returned port is
closed it will also close PORT, unless the KEEP-ALIVE? is true."
(define (next-chunk)
(read-chunk port))
(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))
"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
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
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."
(define (q-for-each f q)
(while (not (q-empty? q))

View file

@ -160,7 +160,7 @@
(define* (build-request uri #:key (method 'GET) (version '(1 . 1))
(headers '()) port (meta '())
(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."
(let ((needs-host? (and (equal? version '(1 . 1))
(not (assq-ref headers 'host)))))
@ -189,13 +189,17 @@ the headers are each run through their respective validators."
meta port)))
(define* (read-request port #:optional (meta '()))
"Read an HTTP request from @var{port}, optionally attaching the given
metadata, @var{meta}.
"Read an HTTP request from PORT, optionally attaching the given
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
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")
(call-with-values (lambda () (read-request-line port))
(lambda (method uri version)
@ -203,10 +207,10 @@ more information."
;; FIXME: really return a new request?
(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
on @var{port}, perhaps using some transfer encoding."
Return a new request, whose request-port will continue writing
on PORT, perhaps using some transfer encoding."
(write-request-line (request-method r) (request-uri r)
(request-version 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)))
(define (read-request-body r)
"Reads the request body from @var{r}, as a bytevector. Returns
@code{#f} if there was no request body."
"Reads the request body from R, as a bytevector. Return #f
if there was no request body."
(let ((nbytes (request-content-length r)))
(and 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))))))
(define (write-request-body r bv)
"Write @var{bv}, a bytevector, to the port corresponding to the HTTP
request @var{r}."
"Write BV, a bytevector, to the port corresponding to the HTTP
request R."
(put-bytevector (request-port r) bv))
(define-syntax define-request-accessor
@ -297,6 +301,8 @@ request @var{r}."
;; Misc accessors
(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)))
(if (uri-host uri)
uri

View file

@ -107,7 +107,7 @@
(define* (build-response #:key (version '(1 . 1)) (code 200) reason-phrase
(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."
(cond
((not (and (pair? version)
@ -170,15 +170,15 @@ the headers are each run through their respective validators."
"(Unknown)"))
(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."
(or (%response-reason-phrase response)
(code->reason-phrase (response-code response))))
(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
the discussion of character sets in \"HTTP Responses\" in the manual,
for more information."
@ -202,10 +202,10 @@ the version field."
#:port (response-port response)))
(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
on @var{port}, perhaps using some transfer encoding."
Returns a new response, whose response-port will continue writing
on PORT, perhaps using some transfer encoding."
(write-response-line (response-version r) (response-code r)
(response-reason-phrase 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)))
(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."
;; 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)))
(define (read-response-body r)
"Reads the response body from @var{r}, as a bytevector. Returns
@code{#f} if there was no response body."
"Reads the response body from R, as a bytevector. Returns
#f if there was no response body."
(if (member '(chunked) (response-transfer-encoding r))
(let ((chunk-port (make-chunked-input-port (response-port r)
#:keep-alive? #t)))
@ -240,8 +240,8 @@ This is true for some response types, like those with code 304."
(bytevector-length bv) nbytes)))))))
(define (write-response-body r bv)
"Write @var{bv}, a bytevector, to the port corresponding to the HTTP
response @var{r}."
"Write BV, a bytevector, to the port corresponding to the HTTP
response R."
(put-bytevector (response-port r) bv))
(define-syntax define-response-accessor

View file

@ -123,14 +123,14 @@
(make-server-impl 'name open read write close)))
(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
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.
Currently a server implementation is a somewhat opaque type, useful only
for passing to other procedures in this module, like
@code{read-client}."
read-client."
(cond
((server-impl? impl) impl)
((symbol? impl)
@ -143,17 +143,17 @@ for passing to other procedures in this module, like
;; -> server
(define (open-server impl open-params)
"Open a server for the given implementation. Returns one value, the
new server object. The implementation's @code{open} procedure is
applied to @var{open-params}, which should be a list."
"Open a server for the given implementation. Return one value, the
new server object. The implementation's open procedure is
applied to OPEN-PARAMS, which should be a list."
(apply (server-impl-open impl) open-params))
;; -> (client request body | #f #f #f)
(define (read-client impl server)
"Read a new client from @var{server}, by applying the implementation's
@code{read} procedure to the server. If successful, returns three
"Read a new client from SERVER, by applying the implementation's
read procedure to the server. If successful, return three
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."
(call-with-error-handling
(lambda ()
@ -213,14 +213,14 @@ values."
"\"Sanitize\" the given response and body, making them appropriate for
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
response. Ensures that the response version corresponds to the request
version. If @var{body} is a string, encodes the string to a bytevector,
in an encoding appropriate for @var{response}. Adds a
@code{content-length} and @code{content-type} header, as necessary.
version. If BODY is a string, encodes the string to a bytevector,
in an encoding appropriate for RESPONSE. Adds a
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
instead use a compressing, chunk-encoded port, and call this procedure
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.
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
@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
from the user, along with whatever state values the user is interested
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
(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
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
(lambda ()
((server-impl-write impl) server client response body))
@ -332,7 +332,7 @@ attaching it to the @var{server} argument somehow."
;; -> unspecified values
(define (close-server impl server)
"Release resources allocated by a previous invocation of
@code{open-server}."
open-server."
((server-impl-close impl) server))
(define call-with-sigint
@ -363,8 +363,8 @@ attaching it to the @var{server} argument somehow."
;; -> new-state
(define (serve-one-client handler impl server state)
"Read one request from @var{server}, call @var{handler} on the request
and body, and write the response to the client. Returns the new state
"Read one request from SERVER, call HANDLER on the request
and body, and write the response to the client. Return the new state
produced by the handler procedure."
(debug-elapsed 'serve-again)
(call-with-values
@ -387,7 +387,7 @@ produced by the handler procedure."
. state)
"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
response and response body.
@ -400,16 +400,16 @@ For example, here is a simple \"Hello, World!\" server:
(run-server handler)
@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.
Additional arguments to @var{handler} are taken from
@var{state}. Additional return values are accumulated into a new
@var{state}, which will be used for subsequent requests. In this way a
Additional arguments to HANDLER are taken from
STATE. Additional return values are accumulated into a new
STATE, which will be used for subsequent requests. In this way a
handler can explicitly manage its state.
The default server implementation is @code{http}, which accepts
@var{open-params} like @code{(#:port 8081)}, among others. See \"Web
The default server implementation is http, which accepts
OPEN-PARAMS like (#:port 8081), among others. See \"Web
Server\" in the manual, for more information."
(let* ((impl (lookup-server-impl impl))
(server (open-server impl open-params)))

View file

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