1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +02:00

read-header returns EOF at end, update (web http) docs

* doc/ref/web.texi (HTTP): Add an example for declaring a header, and
  adapt to read-header change.

* module/web/http.scm (read-header): Return EOF for both values if there
  are no more headers, instead of #f.
  (read-headers): Adapt.
This commit is contained in:
Andy Wingo 2011-01-02 12:42:25 -05:00
parent 569269b4b2
commit 929ccf48fc
2 changed files with 43 additions and 16 deletions

View file

@ -352,11 +352,36 @@ A writer, which writes a value to the port given in the second argument.
@end table @end table
@end defun @end defun
@defun declare-header! sym name [#:multiple?] [#:parser] [#:validator] [#:writer] @defun declare-header! sym name [#:multiple?=@code{#f}] [#:parser] [#:validator] [#:writer]
Make a header declaration, as above, and register it by symbol and by Make a header declaration, as above, and register it by symbol and by
name. name. The @var{parser}, @var{validator}, and @var{writer} arguments are
all mandatory.
@end defun @end defun
For example, let's say you are running a web server behind some sort of
proxy, and your proxy adds an @code{X-Client-Address} header, indicating
the IPv4 address of the original client. You would like for the HTTP
request record to parse out this header to a Scheme value, instead of
leaving it as a string. You could register this header with Guile's
HTTP stack like this:
@example
(define (parse-ip str)
(inet-aton str)
(define (validate-ip ip)
(define (write-ip ip port)
(display (inet-ntoa ip) port))
(declare-header! 'x-client-address
"X-Client-Address"
#:parser (lambda (str)
(inet-aton str))
#:validator (lambda (ip)
(and (integer? ip) (exact? ip) (<= 0 ip 4294967295)))
#:writer (lambda (ip port)
(display (inet-ntoa ip) port)))
@end example
@defun lookup-header-decl name @defun lookup-header-decl name
Return the @var{header-decl} object registered for the given @var{name}. Return the @var{header-decl} object registered for the given @var{name}.
@ -365,27 +390,27 @@ a case-insensitive fashion.
@end defun @end defun
@defun valid-header? sym val @defun valid-header? sym val
Returns a true value iff @var{val} is a valid Scheme value for the Return a true value iff @var{val} is a valid Scheme value for the header
header with name @var{sym}. with name @var{sym}.
@end defun @end defun
Now that we have a generic interface for reading and writing headers, we Now that we have a generic interface for reading and writing headers, we
do just that. do just that.
@defun read-header port @defun read-header port
Reads one HTTP header from @var{port}. Returns two values: the header Read one HTTP header from @var{port}. Return 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.
Returns @var{#f} for both values if the end of the message body was Returns the end-of-file object for both values if the end of the message
reached (i.e., a blank line). body was reached (i.e., a blank line).
@end defun @end defun
@defun parse-header name val @defun parse-header name val
Parse @var{val}, a string, with the parser for the header named Parse @var{val}, a string, with the parser for the header named
@var{name}. @var{name}.
Returns two values, the header name and parsed value. If a parser was Return two values, the header name and parsed value. If a parser was
found, the header name will be returned as a symbol. If a parser was not found, the header name will be returned as a symbol. If a parser was not
found, both the header name and the value are returned as strings. found, both the header name and the value are returned as strings.
@end defun @end defun
@ -397,8 +422,8 @@ value is written using @var{display}.
@end defun @end defun
@defun read-headers port @defun read-headers port
Read an HTTP message from @var{port}, returning the headers as an Read the headers of an HTTP message from @var{port}, returning the
ordered alist. headers as an ordered alist.
@end defun @end defun
@defun write-headers headers port @defun write-headers headers port

View file

@ -130,17 +130,19 @@ port, and writes the value to the port."
(read-line* port)))) (read-line* port))))
val)) val))
(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 @var{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.
Returns @var{#f} for both values if the end of the message body was Returns the end-of-file object for both values if the end of the message
reached (i.e., a blank line)." body was reached (i.e., a blank line)."
(let ((line (read-line* port))) (let ((line (read-line* port)))
(if (or (string-null? line) (if (or (string-null? line)
(string=? line "\r")) (string=? line "\r"))
(values #f #f) (values *eof* *eof*)
(let ((delim (or (string-index line #\:) (let ((delim (or (string-index line #\:)
(bad-header '%read line)))) (bad-header '%read line))))
(parse-header (parse-header
@ -205,9 +207,9 @@ 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)
(if k (if (eof-object? k)
(lp (acons k v headers)) (reverse! headers)
(reverse! 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 @var{port}. Doesn't write the final