2010-10-24 12:36:17 +02:00
|
|
|
|
;;; HTTP messages
|
|
|
|
|
|
|
2014-01-18 21:08:52 +01:00
|
|
|
|
;; Copyright (C) 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; This library is free software; you can redistribute it and/or
|
|
|
|
|
|
;; modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
|
;; License as published by the Free Software Foundation; either
|
|
|
|
|
|
;; version 3 of the License, or (at your option) any later version.
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; This library is distributed in the hope that it will be useful,
|
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
|
;; Lesser General Public License for more details.
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
|
;; License along with this library; if not, write to the Free Software
|
|
|
|
|
|
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
|
|
;; 02110-1301 USA
|
|
|
|
|
|
|
2010-12-06 13:52:56 +01:00
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; This module has a number of routines to parse textual
|
|
|
|
|
|
;;; representations of HTTP data into native Scheme data structures.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; It tries to follow RFCs fairly strictly---the road to perdition
|
|
|
|
|
|
;;; being paved with compatibility hacks---though some allowances are
|
|
|
|
|
|
;;; made for not-too-divergent texts (like a quality of .2 which should
|
|
|
|
|
|
;;; be 0.2, etc).
|
|
|
|
|
|
;;;
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
|
|
(define-module (web http)
|
|
|
|
|
|
#:use-module ((srfi srfi-1) #:select (append-map! map!))
|
|
|
|
|
|
#:use-module (srfi srfi-9)
|
|
|
|
|
|
#:use-module (srfi srfi-19)
|
|
|
|
|
|
#:use-module (ice-9 rdelim)
|
2012-05-08 00:06:01 +01:00
|
|
|
|
#:use-module (ice-9 q)
|
|
|
|
|
|
#:use-module (ice-9 binary-ports)
|
|
|
|
|
|
#:use-module (rnrs bytevectors)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
#:use-module (web uri)
|
2011-01-08 11:40:20 -08:00
|
|
|
|
#:export (string->header
|
|
|
|
|
|
header->string
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
declare-header!
|
2012-05-08 00:18:59 +01:00
|
|
|
|
declare-opaque-header!
|
2011-01-08 11:40:20 -08:00
|
|
|
|
known-header?
|
|
|
|
|
|
header-parser
|
|
|
|
|
|
header-validator
|
|
|
|
|
|
header-writer
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
read-header
|
|
|
|
|
|
parse-header
|
|
|
|
|
|
valid-header?
|
|
|
|
|
|
write-header
|
|
|
|
|
|
|
|
|
|
|
|
read-headers
|
|
|
|
|
|
write-headers
|
|
|
|
|
|
|
2010-11-12 12:10:44 +01:00
|
|
|
|
parse-http-method
|
|
|
|
|
|
parse-http-version
|
|
|
|
|
|
parse-request-uri
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
read-request-line
|
|
|
|
|
|
write-request-line
|
|
|
|
|
|
read-response-line
|
2012-05-08 00:06:01 +01:00
|
|
|
|
write-response-line
|
|
|
|
|
|
|
|
|
|
|
|
make-chunked-input-port
|
2013-06-07 00:47:33 -04:00
|
|
|
|
make-chunked-output-port
|
|
|
|
|
|
|
|
|
|
|
|
http-proxy-port?
|
|
|
|
|
|
set-http-proxy-port?!))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(define (string->header name)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Parse NAME to a symbolic header name."
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(string->symbol (string-downcase name)))
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(define-record-type <header-decl>
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(make-header-decl name parser validator writer multiple?)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
header-decl?
|
|
|
|
|
|
(name header-decl-name)
|
|
|
|
|
|
(parser header-decl-parser)
|
|
|
|
|
|
(validator header-decl-validator)
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(writer header-decl-writer)
|
|
|
|
|
|
(multiple? header-decl-multiple?))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; sym -> header
|
|
|
|
|
|
(define *declared-headers* (make-hash-table))
|
|
|
|
|
|
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(define (lookup-header-decl sym)
|
|
|
|
|
|
(hashq-ref *declared-headers* sym))
|
|
|
|
|
|
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(define* (declare-header! name
|
2010-10-24 12:36:17 +02:00
|
|
|
|
parser
|
|
|
|
|
|
validator
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
writer
|
|
|
|
|
|
#:key multiple?)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Declare a parser, validator, and writer for a given header."
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(if (and (string? name) parser validator writer)
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(let ((decl (make-header-decl name parser validator writer multiple?)))
|
|
|
|
|
|
(hashq-set! *declared-headers* (string->header name) decl)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
decl)
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(error "bad header decl" name parser validator writer multiple?)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (header->string sym)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Return the string form for the header named SYM."
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(let ((decl (lookup-header-decl sym)))
|
|
|
|
|
|
(if decl
|
|
|
|
|
|
(header-decl-name decl)
|
|
|
|
|
|
(string-titlecase (symbol->string sym)))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (known-header? sym)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Return ‘#t’ iff SYM is a known header, with associated
|
|
|
|
|
|
parsers and serialization procedures."
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(and (lookup-header-decl sym) #t))
|
|
|
|
|
|
|
|
|
|
|
|
(define (header-parser sym)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"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."
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(let ((decl (lookup-header-decl sym)))
|
|
|
|
|
|
(if decl
|
|
|
|
|
|
(header-decl-parser decl)
|
|
|
|
|
|
(lambda (x) x))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (header-validator sym)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Return a predicate which returns ‘#t’ if the given value is valid
|
|
|
|
|
|
for headers named SYM. The default validator for unknown headers
|
|
|
|
|
|
is ‘string?’."
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(let ((decl (lookup-header-decl sym)))
|
|
|
|
|
|
(if decl
|
|
|
|
|
|
(header-decl-validator decl)
|
|
|
|
|
|
string?)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (header-writer sym)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"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’."
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(let ((decl (lookup-header-decl sym)))
|
|
|
|
|
|
(if decl
|
|
|
|
|
|
(header-decl-writer decl)
|
|
|
|
|
|
display)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (read-line* port)
|
|
|
|
|
|
(let* ((pair (%read-line port))
|
|
|
|
|
|
(line (car pair))
|
|
|
|
|
|
(delim (cdr pair)))
|
|
|
|
|
|
(if (and (string? line) (char? delim))
|
|
|
|
|
|
(let ((orig-len (string-length line)))
|
|
|
|
|
|
(let lp ((len orig-len))
|
|
|
|
|
|
(if (and (> len 0)
|
|
|
|
|
|
(char-whitespace? (string-ref line (1- len))))
|
|
|
|
|
|
(lp (1- len))
|
|
|
|
|
|
(if (= len orig-len)
|
|
|
|
|
|
line
|
|
|
|
|
|
(substring line 0 len)))))
|
|
|
|
|
|
(bad-header '%read line))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (read-continuation-line port val)
|
|
|
|
|
|
(if (or (eqv? (peek-char port) #\space)
|
|
|
|
|
|
(eqv? (peek-char port) #\tab))
|
|
|
|
|
|
(read-continuation-line port
|
|
|
|
|
|
(string-append val
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(read-line* port))))
|
|
|
|
|
|
val))
|
|
|
|
|
|
|
2011-01-02 12:42:25 -05:00
|
|
|
|
(define *eof* (call-with-input-string "" read))
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(define (read-header port)
|
2013-03-16 17:53:53 +08:00
|
|
|
|
"Read one HTTP header from PORT. Return two values: the header
|
2010-12-16 12:02:53 +01:00
|
|
|
|
name and the parsed Scheme value. May raise an exception if the header
|
|
|
|
|
|
was known but the value was invalid.
|
|
|
|
|
|
|
2011-01-02 12:42:25 -05:00
|
|
|
|
Returns the end-of-file object for both values if the end of the message
|
|
|
|
|
|
body was reached (i.e., a blank line)."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(let ((line (read-line* port)))
|
|
|
|
|
|
(if (or (string-null? line)
|
|
|
|
|
|
(string=? line "\r"))
|
2011-01-02 12:42:25 -05:00
|
|
|
|
(values *eof* *eof*)
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(let* ((delim (or (string-index line #\:)
|
|
|
|
|
|
(bad-header '%read line)))
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(sym (string->header (substring line 0 delim))))
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(values
|
|
|
|
|
|
sym
|
|
|
|
|
|
(parse-header
|
|
|
|
|
|
sym
|
|
|
|
|
|
(read-continuation-line
|
|
|
|
|
|
port
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(string-trim-both line char-set:whitespace (1+ delim)))))))))
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
|
|
|
|
|
|
(define (parse-header sym val)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Parse VAL, a string, with the parser registered for the header
|
|
|
|
|
|
named SYM. Returns the parsed value."
|
2011-01-08 11:40:20 -08:00
|
|
|
|
((header-parser sym) val))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (valid-header? sym val)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Returns a true value iff VAL is a valid Scheme value for the
|
|
|
|
|
|
header with name SYM."
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(if (symbol? sym)
|
2011-01-08 11:40:20 -08:00
|
|
|
|
((header-validator sym) val)
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(error "header name not a symbol" sym)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-header sym val port)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Write the given header name and value to PORT, using the writer
|
|
|
|
|
|
from ‘header-writer’."
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(display (header->string sym) port)
|
|
|
|
|
|
(display ": " port)
|
|
|
|
|
|
((header-writer sym) val port)
|
|
|
|
|
|
(display "\r\n" port))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (read-headers port)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Read the headers of an HTTP message from PORT, returning them
|
|
|
|
|
|
as an ordered alist."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(let lp ((headers '()))
|
|
|
|
|
|
(call-with-values (lambda () (read-header port))
|
|
|
|
|
|
(lambda (k v)
|
2011-01-02 12:42:25 -05:00
|
|
|
|
(if (eof-object? k)
|
|
|
|
|
|
(reverse! headers)
|
|
|
|
|
|
(lp (acons k v headers)))))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (write-headers headers port)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Write the given header alist to PORT. Doesn't write the final
|
2013-03-16 17:53:53 +08:00
|
|
|
|
‘\\r\\n’, as the user might want to add another header."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(let lp ((headers headers))
|
|
|
|
|
|
(if (pair? headers)
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(write-header (caar headers) (cdar headers) port)
|
|
|
|
|
|
(lp (cdr headers))))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Utilities
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
(define (bad-header sym val)
|
|
|
|
|
|
(throw 'bad-header sym val))
|
|
|
|
|
|
(define (bad-header-component sym val)
|
2012-03-11 10:24:08 +01:00
|
|
|
|
(throw 'bad-header-component sym val))
|
|
|
|
|
|
|
|
|
|
|
|
(define (bad-header-printer port key args default-printer)
|
|
|
|
|
|
(apply (case-lambda
|
|
|
|
|
|
((sym val)
|
|
|
|
|
|
(format port "Bad ~a header: ~a\n" (header->string sym) val))
|
|
|
|
|
|
(_ (default-printer)))
|
|
|
|
|
|
args))
|
|
|
|
|
|
(define (bad-header-component-printer port key args default-printer)
|
|
|
|
|
|
(apply (case-lambda
|
|
|
|
|
|
((sym val)
|
|
|
|
|
|
(format port "Bad ~a header component: ~a\n" sym val))
|
|
|
|
|
|
(_ (default-printer)))
|
|
|
|
|
|
args))
|
|
|
|
|
|
(set-exception-printer! 'bad-header bad-header-printer)
|
|
|
|
|
|
(set-exception-printer! 'bad-header-component bad-header-component-printer)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (parse-opaque-string str)
|
|
|
|
|
|
str)
|
|
|
|
|
|
(define (validate-opaque-string val)
|
|
|
|
|
|
(string? val))
|
|
|
|
|
|
(define (write-opaque-string val port)
|
|
|
|
|
|
(display val port))
|
|
|
|
|
|
|
2010-11-13 18:17:28 +01:00
|
|
|
|
(define separators-without-slash
|
|
|
|
|
|
(string->char-set "[^][()<>@,;:\\\"?= \t]"))
|
|
|
|
|
|
(define (validate-media-type str)
|
|
|
|
|
|
(let ((idx (string-index str #\/)))
|
|
|
|
|
|
(and idx (= idx (string-rindex str #\/))
|
|
|
|
|
|
(not (string-index str separators-without-slash)))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(define (parse-media-type str)
|
2010-11-13 18:17:28 +01:00
|
|
|
|
(if (validate-media-type str)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(string->symbol str)
|
2010-11-13 18:17:28 +01:00
|
|
|
|
(bad-header-component 'media-type str)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define* (skip-whitespace str #:optional (start 0) (end (string-length str)))
|
|
|
|
|
|
(let lp ((i start))
|
|
|
|
|
|
(if (and (< i end) (char-whitespace? (string-ref str i)))
|
|
|
|
|
|
(lp (1+ i))
|
|
|
|
|
|
i)))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (trim-whitespace str #:optional (start 0) (end (string-length str)))
|
|
|
|
|
|
(let lp ((i end))
|
|
|
|
|
|
(if (and (< start i) (char-whitespace? (string-ref str (1- i))))
|
|
|
|
|
|
(lp (1- i))
|
|
|
|
|
|
i)))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (split-and-trim str #:optional (delim #\,)
|
|
|
|
|
|
(start 0) (end (string-length str)))
|
|
|
|
|
|
(let lp ((i start))
|
|
|
|
|
|
(if (< i end)
|
|
|
|
|
|
(let* ((idx (string-index str delim i end))
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(tok (string-trim-both str char-set:whitespace i (or idx end))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(cons tok (split-and-trim str delim (if idx (1+ idx) end) end)))
|
|
|
|
|
|
'())))
|
|
|
|
|
|
|
2010-12-16 18:12:08 +01:00
|
|
|
|
(define (list-of-strings? val)
|
|
|
|
|
|
(list-of? val string?))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-list-of-strings val port)
|
|
|
|
|
|
(write-list val port display ", "))
|
|
|
|
|
|
|
|
|
|
|
|
(define (split-header-names str)
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(map string->header (split-and-trim str)))
|
2010-12-16 18:12:08 +01:00
|
|
|
|
|
|
|
|
|
|
(define (list-of-header-names? val)
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(list-of? val symbol?))
|
2010-12-16 18:12:08 +01:00
|
|
|
|
|
|
|
|
|
|
(define (write-header-list val port)
|
|
|
|
|
|
(write-list val port
|
|
|
|
|
|
(lambda (x port)
|
2011-01-08 11:40:20 -08:00
|
|
|
|
(display (header->string x) port))
|
2010-12-16 18:12:08 +01:00
|
|
|
|
", "))
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(define (collect-escaped-string from start len escapes)
|
|
|
|
|
|
(let ((to (make-string len)))
|
|
|
|
|
|
(let lp ((start start) (i 0) (escapes escapes))
|
|
|
|
|
|
(if (null? escapes)
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(substring-move! from start (+ start (- len i)) to i)
|
|
|
|
|
|
to)
|
|
|
|
|
|
(let* ((e (car escapes))
|
|
|
|
|
|
(next-start (+ start (- e i) 2)))
|
|
|
|
|
|
(substring-move! from start (- next-start 2) to i)
|
|
|
|
|
|
(string-set! to e (string-ref from (- next-start 1)))
|
|
|
|
|
|
(lp next-start (1+ e) (cdr escapes)))))))
|
|
|
|
|
|
|
|
|
|
|
|
;; in incremental mode, returns two values: the string, and the index at
|
|
|
|
|
|
;; which the string ended
|
|
|
|
|
|
(define* (parse-qstring str #:optional
|
|
|
|
|
|
(start 0) (end (trim-whitespace str start))
|
|
|
|
|
|
#:key incremental?)
|
|
|
|
|
|
(if (and (< start end) (eqv? (string-ref str start) #\"))
|
|
|
|
|
|
(let lp ((i (1+ start)) (qi 0) (escapes '()))
|
|
|
|
|
|
(if (< i end)
|
|
|
|
|
|
(case (string-ref str i)
|
|
|
|
|
|
((#\\)
|
|
|
|
|
|
(lp (+ i 2) (1+ qi) (cons qi escapes)))
|
|
|
|
|
|
((#\")
|
|
|
|
|
|
(let ((out (collect-escaped-string str (1+ start) qi escapes)))
|
|
|
|
|
|
(if incremental?
|
|
|
|
|
|
(values out (1+ i))
|
|
|
|
|
|
(if (= (1+ i) end)
|
|
|
|
|
|
out
|
|
|
|
|
|
(bad-header-component 'qstring str)))))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(lp (1+ i) (1+ qi) escapes)))
|
|
|
|
|
|
(bad-header-component 'qstring str)))
|
|
|
|
|
|
(bad-header-component 'qstring str)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-list l port write-item delim)
|
|
|
|
|
|
(if (pair? l)
|
|
|
|
|
|
(let lp ((l l))
|
|
|
|
|
|
(write-item (car l) port)
|
|
|
|
|
|
(if (pair? (cdr l))
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display delim port)
|
|
|
|
|
|
(lp (cdr l)))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-qstring str port)
|
|
|
|
|
|
(display #\" port)
|
|
|
|
|
|
(if (string-index str #\")
|
|
|
|
|
|
;; optimize me
|
|
|
|
|
|
(write-list (string-split str #\") port display "\\\"")
|
|
|
|
|
|
(display str port))
|
|
|
|
|
|
(display #\" port))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (parse-quality str #:optional (start 0) (end (string-length str)))
|
|
|
|
|
|
(define (char->decimal c)
|
|
|
|
|
|
(let ((i (- (char->integer c) (char->integer #\0))))
|
|
|
|
|
|
(if (and (<= 0 i) (< i 10))
|
|
|
|
|
|
i
|
|
|
|
|
|
(bad-header-component 'quality str))))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((not (< start end))
|
|
|
|
|
|
(bad-header-component 'quality str))
|
|
|
|
|
|
((eqv? (string-ref str start) #\1)
|
|
|
|
|
|
(if (or (string= str "1" start end)
|
|
|
|
|
|
(string= str "1." start end)
|
|
|
|
|
|
(string= str "1.0" start end)
|
|
|
|
|
|
(string= str "1.00" start end)
|
|
|
|
|
|
(string= str "1.000" start end))
|
|
|
|
|
|
1000
|
|
|
|
|
|
(bad-header-component 'quality str)))
|
|
|
|
|
|
((eqv? (string-ref str start) #\0)
|
|
|
|
|
|
(if (or (string= str "0" start end)
|
|
|
|
|
|
(string= str "0." start end))
|
|
|
|
|
|
0
|
|
|
|
|
|
(if (< 2 (- end start) 6)
|
|
|
|
|
|
(let lp ((place 1) (i (+ start 4)) (q 0))
|
|
|
|
|
|
(if (= i (1+ start))
|
|
|
|
|
|
(if (eqv? (string-ref str (1+ start)) #\.)
|
|
|
|
|
|
q
|
|
|
|
|
|
(bad-header-component 'quality str))
|
|
|
|
|
|
(lp (* 10 place) (1- i)
|
|
|
|
|
|
(if (< i end)
|
|
|
|
|
|
(+ q (* place (char->decimal (string-ref str i))))
|
|
|
|
|
|
q))))
|
|
|
|
|
|
(bad-header-component 'quality str))))
|
2010-12-06 13:52:56 +01:00
|
|
|
|
;; Allow the nonstandard .2 instead of 0.2.
|
|
|
|
|
|
((and (eqv? (string-ref str start) #\.)
|
|
|
|
|
|
(< 1 (- end start) 5))
|
|
|
|
|
|
(let lp ((place 1) (i (+ start 3)) (q 0))
|
|
|
|
|
|
(if (= i start)
|
|
|
|
|
|
q
|
|
|
|
|
|
(lp (* 10 place) (1- i)
|
|
|
|
|
|
(if (< i end)
|
|
|
|
|
|
(+ q (* place (char->decimal (string-ref str i))))
|
|
|
|
|
|
q)))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(else
|
|
|
|
|
|
(bad-header-component 'quality str))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (valid-quality? q)
|
2010-11-23 16:02:21 +01:00
|
|
|
|
(and (non-negative-integer? q) (<= q 1000)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (write-quality q port)
|
|
|
|
|
|
(define (digit->char d)
|
|
|
|
|
|
(integer->char (+ (char->integer #\0) d)))
|
|
|
|
|
|
(display (digit->char (modulo (quotient q 1000) 10)) port)
|
|
|
|
|
|
(display #\. port)
|
|
|
|
|
|
(display (digit->char (modulo (quotient q 100) 10)) port)
|
|
|
|
|
|
(display (digit->char (modulo (quotient q 10) 10)) port)
|
|
|
|
|
|
(display (digit->char (modulo q 10)) port))
|
|
|
|
|
|
|
|
|
|
|
|
(define (list-of? val pred)
|
|
|
|
|
|
(or (null? val)
|
|
|
|
|
|
(and (pair? val)
|
|
|
|
|
|
(pred (car val))
|
|
|
|
|
|
(list-of? (cdr val) pred))))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (parse-quality-list str)
|
|
|
|
|
|
(map (lambda (part)
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((string-rindex part #\;)
|
|
|
|
|
|
=> (lambda (idx)
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(let ((qpart (string-trim-both part char-set:whitespace (1+ idx))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(if (string-prefix? "q=" qpart)
|
|
|
|
|
|
(cons (parse-quality qpart 2)
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(string-trim-both part char-set:whitespace 0 idx))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(bad-header-component 'quality qpart)))))
|
|
|
|
|
|
(else
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(cons 1000 (string-trim-both part char-set:whitespace)))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(string-split str #\,)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (validate-quality-list l)
|
|
|
|
|
|
(list-of? l
|
|
|
|
|
|
(lambda (elt)
|
|
|
|
|
|
(and (pair? elt)
|
|
|
|
|
|
(valid-quality? (car elt))
|
|
|
|
|
|
(string? (cdr elt))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-quality-list l port)
|
|
|
|
|
|
(write-list l port
|
|
|
|
|
|
(lambda (x port)
|
|
|
|
|
|
(let ((q (car x))
|
|
|
|
|
|
(str (cdr x)))
|
|
|
|
|
|
(display str port)
|
|
|
|
|
|
(if (< q 1000)
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display ";q=" port)
|
|
|
|
|
|
(write-quality q port)))))
|
|
|
|
|
|
","))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (parse-non-negative-integer val #:optional (start 0)
|
|
|
|
|
|
(end (string-length val)))
|
|
|
|
|
|
(define (char->decimal c)
|
|
|
|
|
|
(let ((i (- (char->integer c) (char->integer #\0))))
|
|
|
|
|
|
(if (and (<= 0 i) (< i 10))
|
|
|
|
|
|
i
|
|
|
|
|
|
(bad-header-component 'non-negative-integer val))))
|
|
|
|
|
|
(if (not (< start end))
|
|
|
|
|
|
(bad-header-component 'non-negative-integer val)
|
|
|
|
|
|
(let lp ((i start) (out 0))
|
|
|
|
|
|
(if (< i end)
|
|
|
|
|
|
(lp (1+ i)
|
|
|
|
|
|
(+ (* out 10) (char->decimal (string-ref val i))))
|
|
|
|
|
|
out))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (non-negative-integer? code)
|
|
|
|
|
|
(and (number? code) (>= code 0) (exact? code) (integer? code)))
|
|
|
|
|
|
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(define (default-val-parser k val)
|
|
|
|
|
|
val)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(define (default-val-validator k val)
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(or (not val) (string? val)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (default-val-writer k val port)
|
|
|
|
|
|
(if (or (string-index val #\;)
|
|
|
|
|
|
(string-index val #\,)
|
|
|
|
|
|
(string-index val #\"))
|
|
|
|
|
|
(write-qstring val port)
|
|
|
|
|
|
(display val port)))
|
|
|
|
|
|
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(define* (parse-key-value-list str #:optional
|
|
|
|
|
|
(val-parser default-val-parser)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(start 0) (end (string-length str)))
|
|
|
|
|
|
(let lp ((i start) (out '()))
|
|
|
|
|
|
(if (not (< i end))
|
|
|
|
|
|
(reverse! out)
|
|
|
|
|
|
(let* ((i (skip-whitespace str i end))
|
|
|
|
|
|
(eq (string-index str #\= i end))
|
|
|
|
|
|
(comma (string-index str #\, i end))
|
|
|
|
|
|
(delim (min (or eq end) (or comma end)))
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(k (string->symbol
|
|
|
|
|
|
(substring str i (trim-whitespace str i delim)))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(call-with-values
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(if (and eq (or (not comma) (< eq comma)))
|
|
|
|
|
|
(let ((i (skip-whitespace str (1+ eq) end)))
|
|
|
|
|
|
(if (and (< i end) (eqv? (string-ref str i) #\"))
|
|
|
|
|
|
(parse-qstring str i end #:incremental? #t)
|
|
|
|
|
|
(values (substring str i
|
|
|
|
|
|
(trim-whitespace str i
|
|
|
|
|
|
(or comma end)))
|
|
|
|
|
|
(or comma end))))
|
|
|
|
|
|
(values #f delim)))
|
|
|
|
|
|
(lambda (v-str next-i)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(let ((v (val-parser k v-str))
|
|
|
|
|
|
(i (skip-whitespace str next-i end)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(if (or (= i end) (eqv? (string-ref str i) #\,))
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(lp (1+ i) (cons (if v (cons k v) k) out))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(bad-header-component 'key-value-list
|
|
|
|
|
|
(substring str start end))))))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (key-value-list? list #:optional
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(valid? default-val-validator))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(list-of? list
|
|
|
|
|
|
(lambda (elt)
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((pair? elt)
|
|
|
|
|
|
(let ((k (car elt))
|
|
|
|
|
|
(v (cdr elt)))
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(and (symbol? k)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(valid? k v))))
|
2011-11-23 20:56:10 +01:00
|
|
|
|
((symbol? elt)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(valid? elt #f))
|
|
|
|
|
|
(else #f)))))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (write-key-value-list list port #:optional
|
|
|
|
|
|
(val-writer default-val-writer) (delim ", "))
|
|
|
|
|
|
(write-list
|
|
|
|
|
|
list port
|
|
|
|
|
|
(lambda (x port)
|
|
|
|
|
|
(let ((k (if (pair? x) (car x) x))
|
|
|
|
|
|
(v (if (pair? x) (cdr x) #f)))
|
|
|
|
|
|
(display k port)
|
|
|
|
|
|
(if v
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display #\= port)
|
|
|
|
|
|
(val-writer k v port)))))
|
|
|
|
|
|
delim))
|
|
|
|
|
|
|
|
|
|
|
|
;; param-component = token [ "=" (token | quoted-string) ] \
|
|
|
|
|
|
;; *(";" token [ "=" (token | quoted-string) ])
|
|
|
|
|
|
;;
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(define param-delimiters (char-set #\, #\; #\=))
|
|
|
|
|
|
(define param-value-delimiters (char-set-adjoin char-set:whitespace #\, #\;))
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(define* (parse-param-component str #:optional
|
|
|
|
|
|
(val-parser default-val-parser)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(start 0) (end (string-length str)))
|
|
|
|
|
|
(let lp ((i start) (out '()))
|
|
|
|
|
|
(if (not (< i end))
|
|
|
|
|
|
(values (reverse! out) end)
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(let ((delim (string-index str param-delimiters i)))
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(let ((k (string->symbol
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(substring str i (trim-whitespace str i (or delim end)))))
|
|
|
|
|
|
(delimc (and delim (string-ref str delim))))
|
|
|
|
|
|
(case delimc
|
|
|
|
|
|
((#\=)
|
|
|
|
|
|
(call-with-values
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(let ((i (skip-whitespace str (1+ delim) end)))
|
|
|
|
|
|
(if (and (< i end) (eqv? (string-ref str i) #\"))
|
|
|
|
|
|
(parse-qstring str i end #:incremental? #t)
|
|
|
|
|
|
(let ((delim
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(or (string-index str param-value-delimiters
|
|
|
|
|
|
i end)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
end)))
|
|
|
|
|
|
(values (substring str i delim)
|
|
|
|
|
|
delim)))))
|
|
|
|
|
|
(lambda (v-str next-i)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(let* ((v (val-parser k v-str))
|
|
|
|
|
|
(x (if v (cons k v) k))
|
|
|
|
|
|
(i (skip-whitespace str next-i end)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(case (and (< i end) (string-ref str i))
|
|
|
|
|
|
((#f)
|
|
|
|
|
|
(values (reverse! (cons x out)) end))
|
|
|
|
|
|
((#\;)
|
|
|
|
|
|
(lp (skip-whitespace str (1+ i) end)
|
|
|
|
|
|
(cons x out)))
|
|
|
|
|
|
(else ; including #\,
|
|
|
|
|
|
(values (reverse! (cons x out)) i)))))))
|
|
|
|
|
|
((#\;)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(let ((v (val-parser k #f)))
|
|
|
|
|
|
(lp (skip-whitespace str (1+ delim) end)
|
|
|
|
|
|
(cons (if v (cons k v) k) out))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(else ;; either the end of the string or a #\,
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(let ((v (val-parser k #f)))
|
|
|
|
|
|
(values (reverse! (cons (if v (cons k v) k) out))
|
|
|
|
|
|
(or delim end))))))))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define* (parse-param-list str #:optional
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(val-parser default-val-parser)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(start 0) (end (string-length str)))
|
|
|
|
|
|
(let lp ((i start) (out '()))
|
|
|
|
|
|
(call-with-values
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(lambda () (parse-param-component str val-parser i end))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (item i)
|
|
|
|
|
|
(if (< i end)
|
|
|
|
|
|
(if (eqv? (string-ref str i) #\,)
|
|
|
|
|
|
(lp (skip-whitespace str (1+ i) end)
|
|
|
|
|
|
(cons item out))
|
|
|
|
|
|
(bad-header-component 'param-list str))
|
|
|
|
|
|
(reverse! (cons item out)))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (validate-param-list list #:optional
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(valid? default-val-validator))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(list-of? list
|
|
|
|
|
|
(lambda (elt)
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(key-value-list? elt valid?))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define* (write-param-list list port #:optional
|
|
|
|
|
|
(val-writer default-val-writer))
|
|
|
|
|
|
(write-list
|
|
|
|
|
|
list port
|
|
|
|
|
|
(lambda (item port)
|
|
|
|
|
|
(write-key-value-list item port val-writer ";"))
|
|
|
|
|
|
","))
|
|
|
|
|
|
|
2011-08-04 15:07:27 +02:00
|
|
|
|
(define-syntax string-match?
|
|
|
|
|
|
(lambda (x)
|
|
|
|
|
|
(syntax-case x ()
|
|
|
|
|
|
((_ str pat) (string? (syntax->datum #'pat))
|
|
|
|
|
|
(let ((p (syntax->datum #'pat)))
|
|
|
|
|
|
#`(let ((s str))
|
|
|
|
|
|
(and
|
|
|
|
|
|
(= (string-length s) #,(string-length p))
|
|
|
|
|
|
#,@(let lp ((i 0) (tests '()))
|
|
|
|
|
|
(if (< i (string-length p))
|
|
|
|
|
|
(let ((c (string-ref p i)))
|
|
|
|
|
|
(lp (1+ i)
|
|
|
|
|
|
(case c
|
|
|
|
|
|
((#\.) ; Whatever.
|
|
|
|
|
|
tests)
|
|
|
|
|
|
((#\d) ; Digit.
|
|
|
|
|
|
(cons #`(char-numeric? (string-ref s #,i))
|
|
|
|
|
|
tests))
|
|
|
|
|
|
((#\a) ; Alphabetic.
|
|
|
|
|
|
(cons #`(char-alphabetic? (string-ref s #,i))
|
|
|
|
|
|
tests))
|
|
|
|
|
|
(else ; Literal.
|
|
|
|
|
|
(cons #`(eqv? (string-ref s #,i) #,c)
|
|
|
|
|
|
tests)))))
|
|
|
|
|
|
tests)))))))))
|
|
|
|
|
|
|
|
|
|
|
|
;; "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun"
|
|
|
|
|
|
;; "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
|
|
|
|
|
|
|
|
|
|
|
|
(define (parse-month str start end)
|
|
|
|
|
|
(define (bad)
|
|
|
|
|
|
(bad-header-component 'month (substring str start end)))
|
|
|
|
|
|
(if (not (= (- end start) 3))
|
|
|
|
|
|
(bad)
|
|
|
|
|
|
(let ((a (string-ref str (+ start 0)))
|
|
|
|
|
|
(b (string-ref str (+ start 1)))
|
|
|
|
|
|
(c (string-ref str (+ start 2))))
|
|
|
|
|
|
(case a
|
|
|
|
|
|
((#\J)
|
|
|
|
|
|
(case b
|
|
|
|
|
|
((#\a) (case c ((#\n) 1) (else (bad))))
|
|
|
|
|
|
((#\u) (case c ((#\n) 6) ((#\l) 7) (else (bad))))
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
((#\F)
|
|
|
|
|
|
(case b
|
|
|
|
|
|
((#\e) (case c ((#\b) 2) (else (bad))))
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
((#\M)
|
|
|
|
|
|
(case b
|
|
|
|
|
|
((#\a) (case c ((#\r) 3) ((#\y) 5) (else (bad))))
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
((#\A)
|
|
|
|
|
|
(case b
|
|
|
|
|
|
((#\p) (case c ((#\r) 4) (else (bad))))
|
|
|
|
|
|
((#\u) (case c ((#\g) 8) (else (bad))))
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
((#\S)
|
|
|
|
|
|
(case b
|
|
|
|
|
|
((#\e) (case c ((#\p) 9) (else (bad))))
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
((#\O)
|
|
|
|
|
|
(case b
|
|
|
|
|
|
((#\c) (case c ((#\t) 10) (else (bad))))
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
((#\N)
|
|
|
|
|
|
(case b
|
|
|
|
|
|
((#\o) (case c ((#\v) 11) (else (bad))))
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
((#\D)
|
|
|
|
|
|
(case b
|
|
|
|
|
|
((#\e) (case c ((#\c) 12) (else (bad))))
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
(else (bad))))))
|
|
|
|
|
|
|
2013-03-15 22:25:10 +08:00
|
|
|
|
;; "GMT" | "+" 4DIGIT | "-" 4DIGIT
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; RFC 2616 requires date values to use "GMT", but recommends accepting
|
|
|
|
|
|
;; the others as they are commonly generated by e.g. RFC 822 sources.
|
|
|
|
|
|
(define (parse-zone-offset str start)
|
|
|
|
|
|
(let ((s (substring str start)))
|
|
|
|
|
|
(define (bad)
|
|
|
|
|
|
(bad-header-component 'zone-offset s))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((string=? s "GMT")
|
|
|
|
|
|
0)
|
2013-11-18 00:44:17 -05:00
|
|
|
|
((string=? s "UTC")
|
|
|
|
|
|
0)
|
2013-03-15 22:25:10 +08:00
|
|
|
|
((string-match? s ".dddd")
|
|
|
|
|
|
(let ((sign (case (string-ref s 0)
|
|
|
|
|
|
((#\+) +1)
|
|
|
|
|
|
((#\-) -1)
|
|
|
|
|
|
(else (bad))))
|
|
|
|
|
|
(hours (parse-non-negative-integer s 1 3))
|
|
|
|
|
|
(minutes (parse-non-negative-integer s 3 5)))
|
|
|
|
|
|
(* sign 60 (+ (* 60 hours) minutes)))) ; seconds east of Greenwich
|
|
|
|
|
|
(else (bad)))))
|
|
|
|
|
|
|
2011-08-04 15:07:27 +02:00
|
|
|
|
;; RFC 822, updated by RFC 1123
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; Sun, 06 Nov 1994 08:49:37 GMT
|
|
|
|
|
|
;; 01234567890123456789012345678
|
|
|
|
|
|
;; 0 1 2
|
2013-03-15 22:25:10 +08:00
|
|
|
|
(define (parse-rfc-822-date str space zone-offset)
|
2011-08-04 15:07:27 +02:00
|
|
|
|
;; We could verify the day of the week but we don't.
|
2013-03-15 22:25:10 +08:00
|
|
|
|
(cond ((string-match? (substring str 0 space) "aaa, dd aaa dddd dd:dd:dd")
|
2011-09-09 20:02:34 +01:00
|
|
|
|
(let ((date (parse-non-negative-integer str 5 7))
|
|
|
|
|
|
(month (parse-month str 8 11))
|
|
|
|
|
|
(year (parse-non-negative-integer str 12 16))
|
|
|
|
|
|
(hour (parse-non-negative-integer str 17 19))
|
|
|
|
|
|
(minute (parse-non-negative-integer str 20 22))
|
|
|
|
|
|
(second (parse-non-negative-integer str 23 25)))
|
2013-03-15 22:25:10 +08:00
|
|
|
|
(make-date 0 second minute hour date month year zone-offset)))
|
|
|
|
|
|
((string-match? (substring str 0 space) "aaa, d aaa dddd dd:dd:dd")
|
2011-09-09 20:02:34 +01:00
|
|
|
|
(let ((date (parse-non-negative-integer str 5 6))
|
|
|
|
|
|
(month (parse-month str 7 10))
|
|
|
|
|
|
(year (parse-non-negative-integer str 11 15))
|
|
|
|
|
|
(hour (parse-non-negative-integer str 16 18))
|
|
|
|
|
|
(minute (parse-non-negative-integer str 19 21))
|
|
|
|
|
|
(second (parse-non-negative-integer str 22 24)))
|
2013-03-15 22:25:10 +08:00
|
|
|
|
(make-date 0 second minute hour date month year zone-offset)))
|
2011-09-09 20:02:34 +01:00
|
|
|
|
(else
|
|
|
|
|
|
(bad-header 'date str) ; prevent tail call
|
|
|
|
|
|
#f)))
|
2011-08-04 15:07:27 +02:00
|
|
|
|
|
|
|
|
|
|
;; RFC 850, updated by RFC 1036
|
|
|
|
|
|
;; Sunday, 06-Nov-94 08:49:37 GMT
|
|
|
|
|
|
;; 0123456789012345678901
|
|
|
|
|
|
;; 0 1 2
|
2013-03-15 22:25:10 +08:00
|
|
|
|
(define (parse-rfc-850-date str comma space zone-offset)
|
2011-08-04 15:07:27 +02:00
|
|
|
|
;; We could verify the day of the week but we don't.
|
2013-03-15 22:25:10 +08:00
|
|
|
|
(let ((tail (substring str (1+ comma) space)))
|
|
|
|
|
|
(if (not (string-match? tail " dd-aaa-dd dd:dd:dd"))
|
2011-08-04 15:07:27 +02:00
|
|
|
|
(bad-header 'date str))
|
|
|
|
|
|
(let ((date (parse-non-negative-integer tail 1 3))
|
|
|
|
|
|
(month (parse-month tail 4 7))
|
|
|
|
|
|
(year (parse-non-negative-integer tail 8 10))
|
|
|
|
|
|
(hour (parse-non-negative-integer tail 11 13))
|
|
|
|
|
|
(minute (parse-non-negative-integer tail 14 16))
|
|
|
|
|
|
(second (parse-non-negative-integer tail 17 19)))
|
|
|
|
|
|
(make-date 0 second minute hour date month
|
|
|
|
|
|
(let* ((now (date-year (current-date)))
|
|
|
|
|
|
(then (+ now year (- (modulo now 100)))))
|
|
|
|
|
|
(cond ((< (+ then 50) now) (+ then 100))
|
|
|
|
|
|
((< (+ now 50) then) (- then 100))
|
|
|
|
|
|
(else then)))
|
2013-03-15 22:25:10 +08:00
|
|
|
|
zone-offset))))
|
2011-08-04 15:07:27 +02:00
|
|
|
|
|
|
|
|
|
|
;; ANSI C's asctime() format
|
|
|
|
|
|
;; Sun Nov 6 08:49:37 1994
|
|
|
|
|
|
;; 012345678901234567890123
|
|
|
|
|
|
;; 0 1 2
|
|
|
|
|
|
(define (parse-asctime-date str)
|
|
|
|
|
|
(if (not (string-match? str "aaa aaa .d dd:dd:dd dddd"))
|
|
|
|
|
|
(bad-header 'date str))
|
|
|
|
|
|
(let ((date (parse-non-negative-integer
|
|
|
|
|
|
str
|
|
|
|
|
|
(if (eqv? (string-ref str 8) #\space) 9 8)
|
|
|
|
|
|
10))
|
|
|
|
|
|
(month (parse-month str 4 7))
|
|
|
|
|
|
(year (parse-non-negative-integer str 20 24))
|
|
|
|
|
|
(hour (parse-non-negative-integer str 11 13))
|
|
|
|
|
|
(minute (parse-non-negative-integer str 14 16))
|
|
|
|
|
|
(second (parse-non-negative-integer str 17 19)))
|
|
|
|
|
|
(make-date 0 second minute hour date month year 0)))
|
|
|
|
|
|
|
2013-03-15 22:25:10 +08:00
|
|
|
|
;; Convert all date values to GMT time zone, as per RFC 2616 appendix C.
|
|
|
|
|
|
(define (normalize-date date)
|
|
|
|
|
|
(if (zero? (date-zone-offset date))
|
|
|
|
|
|
date
|
|
|
|
|
|
(time-utc->date (date->time-utc date) 0)))
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(define (parse-date str)
|
2013-03-15 22:25:10 +08:00
|
|
|
|
(let* ((space (string-rindex str #\space))
|
|
|
|
|
|
(zone-offset (and space (false-if-exception
|
|
|
|
|
|
(parse-zone-offset str (1+ space))))))
|
|
|
|
|
|
(normalize-date
|
|
|
|
|
|
(if zone-offset
|
|
|
|
|
|
(let ((comma (string-index str #\,)))
|
|
|
|
|
|
(cond ((not comma) (bad-header 'date str))
|
|
|
|
|
|
((= comma 3) (parse-rfc-822-date str space zone-offset))
|
|
|
|
|
|
(else (parse-rfc-850-date str comma space zone-offset))))
|
|
|
|
|
|
(parse-asctime-date str)))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (write-date date port)
|
2011-08-04 15:07:27 +02:00
|
|
|
|
(define (display-digits n digits port)
|
|
|
|
|
|
(define zero (char->integer #\0))
|
|
|
|
|
|
(let lp ((tens (expt 10 (1- digits))))
|
|
|
|
|
|
(if (> tens 0)
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display (integer->char (+ zero (modulo (truncate/ n tens) 10)))
|
|
|
|
|
|
port)
|
|
|
|
|
|
(lp (floor/ tens 10))))))
|
|
|
|
|
|
(let ((date (if (zero? (date-zone-offset date))
|
|
|
|
|
|
date
|
|
|
|
|
|
(time-tai->date (date->time-tai date) 0))))
|
|
|
|
|
|
(display (case (date-week-day date)
|
2011-10-17 09:48:19 +02:00
|
|
|
|
((0) "Sun, ") ((1) "Mon, ") ((2) "Tue, ")
|
2011-08-04 15:07:27 +02:00
|
|
|
|
((3) "Wed, ") ((4) "Thu, ") ((5) "Fri, ")
|
|
|
|
|
|
((6) "Sat, ") (else (error "bad date" date)))
|
|
|
|
|
|
port)
|
|
|
|
|
|
(display-digits (date-day date) 2 port)
|
|
|
|
|
|
(display (case (date-month date)
|
2012-03-12 16:58:15 +01:00
|
|
|
|
((1) " Jan ") ((2) " Feb ") ((3) " Mar ")
|
2011-08-04 15:07:27 +02:00
|
|
|
|
((4) " Apr ") ((5) " May ") ((6) " Jun ")
|
|
|
|
|
|
((7) " Jul ") ((8) " Aug ") ((9) " Sep ")
|
|
|
|
|
|
((10) " Oct ") ((11) " Nov ") ((12) " Dec ")
|
|
|
|
|
|
(else (error "bad date" date)))
|
|
|
|
|
|
port)
|
|
|
|
|
|
(display-digits (date-year date) 4 port)
|
|
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(display-digits (date-hour date) 2 port)
|
|
|
|
|
|
(display #\: port)
|
|
|
|
|
|
(display-digits (date-minute date) 2 port)
|
|
|
|
|
|
(display #\: port)
|
|
|
|
|
|
(display-digits (date-second date) 2 port)
|
|
|
|
|
|
(display " GMT" port)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (parse-entity-tag val)
|
|
|
|
|
|
(if (string-prefix? "W/" val)
|
|
|
|
|
|
(cons (parse-qstring val 2) #f)
|
|
|
|
|
|
(cons (parse-qstring val) #t)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (entity-tag? val)
|
|
|
|
|
|
(and (pair? val)
|
|
|
|
|
|
(string? (car val))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-entity-tag val port)
|
2010-12-06 12:58:16 +01:00
|
|
|
|
(if (not (cdr val))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(display "W/" port))
|
|
|
|
|
|
(write-qstring (car val) port))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (parse-entity-tag-list val #:optional
|
|
|
|
|
|
(start 0) (end (string-length val)))
|
|
|
|
|
|
(let ((strong? (not (string-prefix? "W/" val 0 2 start end))))
|
|
|
|
|
|
(call-with-values (lambda ()
|
|
|
|
|
|
(parse-qstring val (if strong? start (+ start 2))
|
|
|
|
|
|
end #:incremental? #t))
|
|
|
|
|
|
(lambda (tag next)
|
|
|
|
|
|
(acons tag strong?
|
|
|
|
|
|
(let ((next (skip-whitespace val next end)))
|
|
|
|
|
|
(if (< next end)
|
|
|
|
|
|
(if (eqv? (string-ref val next) #\,)
|
|
|
|
|
|
(parse-entity-tag-list
|
|
|
|
|
|
val
|
|
|
|
|
|
(skip-whitespace val (1+ next) end)
|
|
|
|
|
|
end)
|
|
|
|
|
|
(bad-header-component 'entity-tag-list val))
|
|
|
|
|
|
'())))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (entity-tag-list? val)
|
|
|
|
|
|
(list-of? val entity-tag?))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-entity-tag-list val port)
|
|
|
|
|
|
(write-list val port write-entity-tag ", "))
|
|
|
|
|
|
|
2011-01-10 08:20:29 -08:00
|
|
|
|
;; credentials = auth-scheme #auth-param
|
|
|
|
|
|
;; auth-scheme = token
|
|
|
|
|
|
;; auth-param = token "=" ( token | quoted-string )
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; That's what the spec says. In reality the Basic scheme doesn't have
|
|
|
|
|
|
;; k-v pairs, just one auth token, so we give that token as a string.
|
|
|
|
|
|
;;
|
|
|
|
|
|
(define* (parse-credentials str #:optional (val-parser default-val-parser)
|
|
|
|
|
|
(start 0) (end (string-length str)))
|
|
|
|
|
|
(let* ((start (skip-whitespace str start end))
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(delim (or (string-index str char-set:whitespace start end) end)))
|
2011-01-10 08:20:29 -08:00
|
|
|
|
(if (= start end)
|
|
|
|
|
|
(bad-header-component 'authorization str))
|
|
|
|
|
|
(let ((scheme (string->symbol
|
|
|
|
|
|
(string-downcase (substring str start (or delim end))))))
|
|
|
|
|
|
(case scheme
|
|
|
|
|
|
((basic)
|
|
|
|
|
|
(let* ((start (skip-whitespace str delim end)))
|
|
|
|
|
|
(if (< start end)
|
|
|
|
|
|
(cons scheme (substring str start end))
|
|
|
|
|
|
(bad-header-component 'credentials str))))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(cons scheme (parse-key-value-list str default-val-parser delim end)))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (validate-credentials val)
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(and (pair? val) (symbol? (car val))
|
|
|
|
|
|
(case (car val)
|
|
|
|
|
|
((basic) (string? (cdr val)))
|
|
|
|
|
|
(else (key-value-list? (cdr val))))))
|
2011-01-10 08:20:29 -08:00
|
|
|
|
|
|
|
|
|
|
(define (write-credentials val port)
|
|
|
|
|
|
(display (car val) port)
|
2014-01-21 15:50:58 -05:00
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(case (car val)
|
|
|
|
|
|
((basic) (display (cdr val) port))
|
|
|
|
|
|
(else (write-key-value-list (cdr val) port))))
|
2011-01-10 08:20:29 -08:00
|
|
|
|
|
|
|
|
|
|
;; challenges = 1#challenge
|
|
|
|
|
|
;; challenge = auth-scheme 1*SP 1#auth-param
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; A pain to parse, as both challenges and auth params are delimited by
|
|
|
|
|
|
;; commas, and qstrings can contain anything. We rely on auth params
|
|
|
|
|
|
;; necessarily having "=" in them.
|
|
|
|
|
|
;;
|
|
|
|
|
|
(define* (parse-challenge str #:optional
|
|
|
|
|
|
(start 0) (end (string-length str)))
|
|
|
|
|
|
(let* ((start (skip-whitespace str start end))
|
|
|
|
|
|
(sp (string-index str #\space start end))
|
|
|
|
|
|
(scheme (if sp
|
|
|
|
|
|
(string->symbol (string-downcase (substring str start sp)))
|
|
|
|
|
|
(bad-header-component 'challenge str))))
|
|
|
|
|
|
(let lp ((i sp) (out (list scheme)))
|
|
|
|
|
|
(if (not (< i end))
|
|
|
|
|
|
(values (reverse! out) end)
|
|
|
|
|
|
(let* ((i (skip-whitespace str i end))
|
|
|
|
|
|
(eq (string-index str #\= i end))
|
|
|
|
|
|
(comma (string-index str #\, i end))
|
|
|
|
|
|
(delim (min (or eq end) (or comma end)))
|
|
|
|
|
|
(token-end (trim-whitespace str i delim)))
|
|
|
|
|
|
(if (string-index str #\space i token-end)
|
|
|
|
|
|
(values (reverse! out) i)
|
|
|
|
|
|
(let ((k (string->symbol (substring str i token-end))))
|
|
|
|
|
|
(call-with-values
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(if (and eq (or (not comma) (< eq comma)))
|
|
|
|
|
|
(let ((i (skip-whitespace str (1+ eq) end)))
|
|
|
|
|
|
(if (and (< i end) (eqv? (string-ref str i) #\"))
|
|
|
|
|
|
(parse-qstring str i end #:incremental? #t)
|
|
|
|
|
|
(values (substring
|
|
|
|
|
|
str i
|
|
|
|
|
|
(trim-whitespace str i
|
|
|
|
|
|
(or comma end)))
|
|
|
|
|
|
(or comma end))))
|
|
|
|
|
|
(values #f delim)))
|
|
|
|
|
|
(lambda (v next-i)
|
|
|
|
|
|
(let ((i (skip-whitespace str next-i end)))
|
|
|
|
|
|
(if (or (= i end) (eqv? (string-ref str i) #\,))
|
|
|
|
|
|
(lp (1+ i) (cons (if v (cons k v) k) out))
|
|
|
|
|
|
(bad-header-component
|
|
|
|
|
|
'challenge
|
|
|
|
|
|
(substring str start end)))))))))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (parse-challenges str #:optional (val-parser default-val-parser)
|
|
|
|
|
|
(start 0) (end (string-length str)))
|
|
|
|
|
|
(let lp ((i start) (ret '()))
|
|
|
|
|
|
(let ((i (skip-whitespace str i end)))
|
|
|
|
|
|
(if (< i end)
|
|
|
|
|
|
(call-with-values (lambda () (parse-challenge str i end))
|
|
|
|
|
|
(lambda (challenge i)
|
|
|
|
|
|
(lp i (cons challenge ret))))
|
|
|
|
|
|
(reverse ret)))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (validate-challenges val)
|
|
|
|
|
|
(list-of? val (lambda (x)
|
|
|
|
|
|
(and (pair? x) (symbol? (car x))
|
|
|
|
|
|
(key-value-list? (cdr x))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-challenge val port)
|
|
|
|
|
|
(display (car val) port)
|
|
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(write-key-value-list (cdr val) port))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-challenges val port)
|
|
|
|
|
|
(write-list val port write-challenge ", "))
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Request-Line and Response-Line
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
;; Hmm.
|
|
|
|
|
|
(define (bad-request message . args)
|
|
|
|
|
|
(throw 'bad-request message args))
|
|
|
|
|
|
(define (bad-response message . args)
|
|
|
|
|
|
(throw 'bad-response message args))
|
|
|
|
|
|
|
|
|
|
|
|
(define *known-versions* '())
|
|
|
|
|
|
|
2010-11-12 12:10:44 +01:00
|
|
|
|
(define* (parse-http-version str #:optional (start 0) (end (string-length str)))
|
2013-03-16 17:53:53 +08:00
|
|
|
|
"Parse an HTTP version from STR, returning it as a major–minor
|
2012-11-24 14:10:12 +08:00
|
|
|
|
pair. For example, ‘HTTP/1.1’ parses as the pair of integers,
|
|
|
|
|
|
‘(1 . 1)’."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(or (let lp ((known *known-versions*))
|
|
|
|
|
|
(and (pair? known)
|
|
|
|
|
|
(if (string= str (caar known) start end)
|
|
|
|
|
|
(cdar known)
|
|
|
|
|
|
(lp (cdr known)))))
|
|
|
|
|
|
(let ((dot-idx (string-index str #\. start end)))
|
|
|
|
|
|
(if (and (string-prefix? "HTTP/" str 0 5 start end)
|
|
|
|
|
|
dot-idx
|
|
|
|
|
|
(= dot-idx (string-rindex str #\. start end)))
|
|
|
|
|
|
(cons (parse-non-negative-integer str (+ start 5) dot-idx)
|
|
|
|
|
|
(parse-non-negative-integer str (1+ dot-idx) end))
|
|
|
|
|
|
(bad-header-component 'http-version (substring str start end))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-http-version val port)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Write the given major-minor version pair to PORT."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(display "HTTP/" port)
|
|
|
|
|
|
(display (car val) port)
|
|
|
|
|
|
(display #\. port)
|
|
|
|
|
|
(display (cdr val) port))
|
|
|
|
|
|
|
|
|
|
|
|
(for-each
|
|
|
|
|
|
(lambda (v)
|
|
|
|
|
|
(set! *known-versions*
|
|
|
|
|
|
(acons v (parse-http-version v 0 (string-length v))
|
|
|
|
|
|
*known-versions*)))
|
|
|
|
|
|
'("HTTP/1.0" "HTTP/1.1"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; Request-URI = "*" | absoluteURI | abs_path | authority
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; The `authority' form is only permissible for the CONNECT method, so
|
|
|
|
|
|
;; because we don't expect people to implement CONNECT, we save
|
|
|
|
|
|
;; ourselves the trouble of that case, and disallow the CONNECT method.
|
|
|
|
|
|
;;
|
2010-11-12 12:10:44 +01:00
|
|
|
|
(define* (parse-http-method str #:optional (start 0) (end (string-length str)))
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Parse an HTTP method from STR. The result is an upper-case
|
|
|
|
|
|
symbol, like ‘GET’."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(cond
|
|
|
|
|
|
((string= str "GET" start end) 'GET)
|
|
|
|
|
|
((string= str "HEAD" start end) 'HEAD)
|
|
|
|
|
|
((string= str "POST" start end) 'POST)
|
|
|
|
|
|
((string= str "PUT" start end) 'PUT)
|
|
|
|
|
|
((string= str "DELETE" start end) 'DELETE)
|
|
|
|
|
|
((string= str "OPTIONS" start end) 'OPTIONS)
|
|
|
|
|
|
((string= str "TRACE" start end) 'TRACE)
|
|
|
|
|
|
(else (bad-request "Invalid method: ~a" (substring str start end)))))
|
|
|
|
|
|
|
2010-11-12 12:10:44 +01:00
|
|
|
|
(define* (parse-request-uri str #:optional (start 0) (end (string-length str)))
|
2010-12-16 12:02:53 +01:00
|
|
|
|
"Parse a URI from an HTTP request line. Note that URIs in requests do
|
|
|
|
|
|
not have to have a scheme or host name. The result is a URI object."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(cond
|
|
|
|
|
|
((= start end)
|
|
|
|
|
|
(bad-request "Missing Request-URI"))
|
|
|
|
|
|
((string= str "*" start end)
|
|
|
|
|
|
#f)
|
|
|
|
|
|
((eq? (string-ref str start) #\/)
|
|
|
|
|
|
(let* ((q (string-index str #\? start end))
|
|
|
|
|
|
(f (string-index str #\# start end))
|
|
|
|
|
|
(q (and q (or (not f) (< q f)) q)))
|
|
|
|
|
|
(build-uri 'http
|
|
|
|
|
|
#:path (substring str start (or q f end))
|
|
|
|
|
|
#:query (and q (substring str (1+ q) (or f end)))
|
|
|
|
|
|
#:fragment (and f (substring str (1+ f) end)))))
|
|
|
|
|
|
(else
|
2010-12-31 12:44:11 -05:00
|
|
|
|
(or (string->uri (substring str start end))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(bad-request "Invalid URI: ~a" (substring str start end))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (read-request-line port)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Read the first line of an HTTP request from PORT, returning
|
2010-12-16 12:02:53 +01:00
|
|
|
|
three values: the method, the URI, and the version."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(let* ((line (read-line* port))
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(d0 (string-index line char-set:whitespace)) ; "delimiter zero"
|
|
|
|
|
|
(d1 (string-rindex line char-set:whitespace)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(if (and d0 d1 (< d0 d1))
|
2010-11-12 12:10:44 +01:00
|
|
|
|
(values (parse-http-method line 0 d0)
|
|
|
|
|
|
(parse-request-uri line (skip-whitespace line (1+ d0) d1) d1)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(parse-http-version line (1+ d1) (string-length line)))
|
|
|
|
|
|
(bad-request "Bad Request-Line: ~s" line))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-uri uri port)
|
2014-10-15 11:49:41 +02:00
|
|
|
|
(when (uri-host uri)
|
|
|
|
|
|
(when (uri-scheme uri)
|
|
|
|
|
|
(display (uri-scheme uri) port)
|
|
|
|
|
|
(display #\: port))
|
|
|
|
|
|
(display "//" port)
|
|
|
|
|
|
(when (uri-userinfo uri)
|
|
|
|
|
|
(display (uri-userinfo uri) port)
|
|
|
|
|
|
(display #\@ port))
|
|
|
|
|
|
(display (uri-host uri) port)
|
|
|
|
|
|
(let ((p (uri-port uri)))
|
|
|
|
|
|
(when (and p (not (eqv? p 80)))
|
|
|
|
|
|
(display #\: port)
|
|
|
|
|
|
(display p port))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(let* ((path (uri-path uri))
|
|
|
|
|
|
(len (string-length path)))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((and (> len 0) (not (eqv? (string-ref path 0) #\/)))
|
|
|
|
|
|
(bad-request "Non-absolute URI path: ~s" path))
|
|
|
|
|
|
((and (zero? len) (not (uri-host uri)))
|
|
|
|
|
|
(bad-request "Empty path and no host for URI: ~s" uri))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(display path port))))
|
2014-10-15 11:49:41 +02:00
|
|
|
|
(when (uri-query uri)
|
|
|
|
|
|
(display #\? port)
|
|
|
|
|
|
(display (uri-query uri) port)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
(define (write-request-line method uri version port)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Write the first line of an HTTP request to PORT."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(display method port)
|
|
|
|
|
|
(display #\space port)
|
2013-06-07 00:47:33 -04:00
|
|
|
|
(when (http-proxy-port? port)
|
|
|
|
|
|
(let ((scheme (uri-scheme uri))
|
|
|
|
|
|
(host (uri-host uri))
|
|
|
|
|
|
(host-port (uri-port uri)))
|
|
|
|
|
|
(when (and scheme host)
|
|
|
|
|
|
(display scheme port)
|
|
|
|
|
|
(display "://" port)
|
|
|
|
|
|
(if (string-index host #\:)
|
|
|
|
|
|
(begin (display #\[ port)
|
|
|
|
|
|
(display host port)
|
|
|
|
|
|
(display #\] port))
|
|
|
|
|
|
(display host port))
|
|
|
|
|
|
(unless ((@@ (web uri) default-port?) scheme host-port)
|
|
|
|
|
|
(display #\: port)
|
|
|
|
|
|
(display host-port port)))))
|
2011-09-29 03:12:00 +01:00
|
|
|
|
(let ((path (uri-path uri))
|
|
|
|
|
|
(query (uri-query uri)))
|
2013-08-19 03:43:48 +01:00
|
|
|
|
(if (string-null? path)
|
|
|
|
|
|
(display "/" port)
|
2011-09-29 03:12:00 +01:00
|
|
|
|
(display path port))
|
|
|
|
|
|
(if query
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display "?" port)
|
2013-08-19 03:43:48 +01:00
|
|
|
|
(display query port))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(write-http-version version port)
|
|
|
|
|
|
(display "\r\n" port))
|
|
|
|
|
|
|
|
|
|
|
|
(define (read-response-line port)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Read the first line of an HTTP response from PORT, returning
|
2010-12-16 12:02:53 +01:00
|
|
|
|
three values: the HTTP version, the response code, and the \"reason
|
|
|
|
|
|
phrase\"."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(let* ((line (read-line* port))
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(d0 (string-index line char-set:whitespace)) ; "delimiter zero"
|
|
|
|
|
|
(d1 (and d0 (string-index line char-set:whitespace
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(skip-whitespace line d0)))))
|
|
|
|
|
|
(if (and d0 d1)
|
|
|
|
|
|
(values (parse-http-version line 0 d0)
|
|
|
|
|
|
(parse-non-negative-integer line (skip-whitespace line d0 d1)
|
|
|
|
|
|
d1)
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(string-trim-both line char-set:whitespace d1))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(bad-response "Bad Response-Line: ~s" line))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-response-line version code reason-phrase port)
|
2012-11-24 14:10:12 +08:00
|
|
|
|
"Write the first line of an HTTP response to PORT."
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(write-http-version version port)
|
|
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(display code port)
|
|
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(display reason-phrase port)
|
|
|
|
|
|
(display "\r\n" port))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
;;; Helpers for declaring headers
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;;
|
|
|
|
|
|
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
;; emacs: (put 'declare-header! 'scheme-indent-function 1)
|
|
|
|
|
|
;; emacs: (put 'declare-opaque!-header 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-opaque-header! name)
|
2012-05-08 00:18:59 +01:00
|
|
|
|
"Declares a given header as \"opaque\", meaning that its value is not
|
|
|
|
|
|
treated specially, and is just returned as a plain string."
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! name
|
|
|
|
|
|
parse-opaque-string validate-opaque-string write-opaque-string))
|
|
|
|
|
|
|
|
|
|
|
|
;; emacs: (put 'declare-date-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-date-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
parse-date date? write-date))
|
|
|
|
|
|
|
|
|
|
|
|
;; emacs: (put 'declare-string-list-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-string-list-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
split-and-trim list-of-strings? write-list-of-strings))
|
|
|
|
|
|
|
2011-01-08 21:32:14 -08:00
|
|
|
|
;; emacs: (put 'declare-symbol-list-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-symbol-list-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(map string->symbol (split-and-trim str)))
|
|
|
|
|
|
(lambda (v)
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(list-of? v symbol?))
|
2011-01-08 21:32:14 -08:00
|
|
|
|
(lambda (v port)
|
|
|
|
|
|
(write-list v port display ", "))))
|
|
|
|
|
|
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
;; emacs: (put 'declare-header-list-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-header-list-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
split-header-names list-of-header-names? write-header-list))
|
|
|
|
|
|
|
|
|
|
|
|
;; emacs: (put 'declare-integer-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-integer-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
parse-non-negative-integer non-negative-integer? display))
|
|
|
|
|
|
|
|
|
|
|
|
;; emacs: (put 'declare-uri-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-uri-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
(lambda (str) (or (string->uri str) (bad-header-component 'uri str)))
|
2012-11-27 16:48:41 +08:00
|
|
|
|
(@@ (web uri) absolute-uri?)
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
write-uri))
|
|
|
|
|
|
|
2014-10-15 11:49:41 +02:00
|
|
|
|
;; emacs: (put 'declare-uri-reference-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-uri-reference-header! name)
|
2012-11-27 00:10:09 +01:00
|
|
|
|
(declare-header! name
|
|
|
|
|
|
(lambda (str)
|
2014-10-15 11:49:41 +02:00
|
|
|
|
(or (string->uri-reference str)
|
2012-11-27 16:48:41 +08:00
|
|
|
|
(bad-header-component 'uri str)))
|
2012-11-27 00:10:09 +01:00
|
|
|
|
uri?
|
|
|
|
|
|
write-uri))
|
|
|
|
|
|
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
;; emacs: (put 'declare-quality-list-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-quality-list-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
parse-quality-list validate-quality-list write-quality-list))
|
|
|
|
|
|
|
|
|
|
|
|
;; emacs: (put 'declare-param-list-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define* (declare-param-list-header! name #:optional
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(val-parser default-val-parser)
|
|
|
|
|
|
(val-validator default-val-validator)
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(val-writer default-val-writer))
|
|
|
|
|
|
(declare-header! name
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(lambda (str) (parse-param-list str val-parser))
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(lambda (val) (validate-param-list val val-validator))
|
|
|
|
|
|
(lambda (val port) (write-param-list val port val-writer))))
|
|
|
|
|
|
|
|
|
|
|
|
;; emacs: (put 'declare-key-value-list-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define* (declare-key-value-list-header! name #:optional
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(val-parser default-val-parser)
|
|
|
|
|
|
(val-validator default-val-validator)
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(val-writer default-val-writer))
|
|
|
|
|
|
(declare-header! name
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(lambda (str) (parse-key-value-list str val-parser))
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(lambda (val) (key-value-list? val val-validator))
|
|
|
|
|
|
(lambda (val port) (write-key-value-list val port val-writer))))
|
|
|
|
|
|
|
|
|
|
|
|
;; emacs: (put 'declare-entity-tag-list-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-entity-tag-list-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
(lambda (str) (if (string=? str "*") '* (parse-entity-tag-list str)))
|
|
|
|
|
|
(lambda (val) (or (eq? val '*) (entity-tag-list? val)))
|
|
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(if (eq? val '*)
|
|
|
|
|
|
(display "*" port)
|
|
|
|
|
|
(write-entity-tag-list val port)))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
2011-01-10 08:20:29 -08:00
|
|
|
|
;; emacs: (put 'declare-credentials-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-credentials-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
parse-credentials validate-credentials write-credentials))
|
|
|
|
|
|
|
|
|
|
|
|
;; emacs: (put 'declare-challenge-list-header! 'scheme-indent-function 1)
|
|
|
|
|
|
(define (declare-challenge-list-header! name)
|
|
|
|
|
|
(declare-header! name
|
|
|
|
|
|
parse-challenges validate-challenges write-challenges))
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; General headers
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
;; Cache-Control = 1#(cache-directive)
|
|
|
|
|
|
;; cache-directive = cache-request-directive | cache-response-directive
|
|
|
|
|
|
;; cache-request-directive =
|
|
|
|
|
|
;; "no-cache" ; Section 14.9.1
|
|
|
|
|
|
;; | "no-store" ; Section 14.9.2
|
|
|
|
|
|
;; | "max-age" "=" delta-seconds ; Section 14.9.3, 14.9.4
|
|
|
|
|
|
;; | "max-stale" [ "=" delta-seconds ] ; Section 14.9.3
|
|
|
|
|
|
;; | "min-fresh" "=" delta-seconds ; Section 14.9.3
|
|
|
|
|
|
;; | "no-transform" ; Section 14.9.5
|
|
|
|
|
|
;; | "only-if-cached" ; Section 14.9.4
|
|
|
|
|
|
;; | cache-extension ; Section 14.9.6
|
|
|
|
|
|
;; cache-response-directive =
|
|
|
|
|
|
;; "public" ; Section 14.9.1
|
|
|
|
|
|
;; | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
|
|
|
|
|
|
;; | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
|
|
|
|
|
|
;; | "no-store" ; Section 14.9.2
|
|
|
|
|
|
;; | "no-transform" ; Section 14.9.5
|
|
|
|
|
|
;; | "must-revalidate" ; Section 14.9.4
|
|
|
|
|
|
;; | "proxy-revalidate" ; Section 14.9.4
|
|
|
|
|
|
;; | "max-age" "=" delta-seconds ; Section 14.9.3
|
|
|
|
|
|
;; | "s-maxage" "=" delta-seconds ; Section 14.9.3
|
|
|
|
|
|
;; | cache-extension ; Section 14.9.6
|
|
|
|
|
|
;; cache-extension = token [ "=" ( token | quoted-string ) ]
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-key-value-list-header! "Cache-Control"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (k v-str)
|
|
|
|
|
|
(case k
|
2011-11-27 22:37:24 +08:00
|
|
|
|
((max-age min-fresh s-maxage)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(parse-non-negative-integer v-str))
|
2011-11-27 22:37:24 +08:00
|
|
|
|
((max-stale)
|
|
|
|
|
|
(and v-str (parse-non-negative-integer v-str)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
((private no-cache)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(and v-str (split-header-names v-str)))
|
|
|
|
|
|
(else v-str)))
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(lambda (k v)
|
|
|
|
|
|
(case k
|
2011-11-27 22:37:24 +08:00
|
|
|
|
((max-age min-fresh s-maxage)
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(non-negative-integer? v))
|
2011-11-27 22:37:24 +08:00
|
|
|
|
((max-stale)
|
|
|
|
|
|
(or (not v) (non-negative-integer? v)))
|
2011-11-23 20:56:10 +01:00
|
|
|
|
((private no-cache)
|
|
|
|
|
|
(or (not v) (list-of-header-names? v)))
|
2011-11-27 22:37:24 +08:00
|
|
|
|
((no-store no-transform only-if-cache must-revalidate proxy-revalidate)
|
|
|
|
|
|
(not v))
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(else
|
2011-11-27 22:37:24 +08:00
|
|
|
|
(or (not v) (string? v)))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (k v port)
|
|
|
|
|
|
(cond
|
2011-12-22 08:18:05 -05:00
|
|
|
|
((string? v) (default-val-writer k v port))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
((pair? v)
|
2010-12-16 18:12:08 +01:00
|
|
|
|
(display #\" port)
|
|
|
|
|
|
(write-header-list v port)
|
|
|
|
|
|
(display #\" port))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
((integer? v)
|
|
|
|
|
|
(display v port))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(bad-header-component 'cache-control v)))))
|
|
|
|
|
|
|
|
|
|
|
|
;; Connection = "Connection" ":" 1#(connection-token)
|
|
|
|
|
|
;; connection-token = token
|
|
|
|
|
|
;; e.g.
|
2013-01-07 23:21:16 +01:00
|
|
|
|
;; Connection: close, Foo-Header
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;
|
2013-01-07 23:21:16 +01:00
|
|
|
|
(declare-header! "Connection"
|
|
|
|
|
|
split-header-names
|
|
|
|
|
|
list-of-header-names?
|
|
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(write-list val port
|
|
|
|
|
|
(lambda (x port)
|
|
|
|
|
|
(display (if (eq? x 'close)
|
|
|
|
|
|
"close"
|
|
|
|
|
|
(header->string x))
|
|
|
|
|
|
port))
|
|
|
|
|
|
", ")))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Date = "Date" ":" HTTP-date
|
|
|
|
|
|
;; e.g.
|
|
|
|
|
|
;; Date: Tue, 15 Nov 1994 08:12:31 GMT
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-date-header! "Date")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Pragma = "Pragma" ":" 1#pragma-directive
|
|
|
|
|
|
;; pragma-directive = "no-cache" | extension-pragma
|
|
|
|
|
|
;; extension-pragma = token [ "=" ( token | quoted-string ) ]
|
|
|
|
|
|
;;
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(declare-key-value-list-header! "Pragma")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Trailer = "Trailer" ":" 1#field-name
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header-list-header! "Trailer")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
|
|
|
|
|
|
;;
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(declare-param-list-header! "Transfer-Encoding")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Upgrade = "Upgrade" ":" 1#product
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-string-list-header! "Upgrade")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
|
|
|
|
|
|
;; received-protocol = [ protocol-name "/" ] protocol-version
|
|
|
|
|
|
;; protocol-name = token
|
|
|
|
|
|
;; protocol-version = token
|
|
|
|
|
|
;; received-by = ( host [ ":" port ] ) | pseudonym
|
|
|
|
|
|
;; pseudonym = token
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "Via"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
split-and-trim
|
|
|
|
|
|
list-of-strings?
|
|
|
|
|
|
write-list-of-strings
|
|
|
|
|
|
#:multiple? #t)
|
|
|
|
|
|
|
|
|
|
|
|
;; Warning = "Warning" ":" 1#warning-value
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; warning-value = warn-code SP warn-agent SP warn-text
|
|
|
|
|
|
;; [SP warn-date]
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; warn-code = 3DIGIT
|
|
|
|
|
|
;; warn-agent = ( host [ ":" port ] ) | pseudonym
|
|
|
|
|
|
;; ; the name or pseudonym of the server adding
|
|
|
|
|
|
;; ; the Warning header, for use in debugging
|
|
|
|
|
|
;; warn-text = quoted-string
|
|
|
|
|
|
;; warn-date = <"> HTTP-date <">
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "Warning"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(let ((len (string-length str)))
|
|
|
|
|
|
(let lp ((i (skip-whitespace str 0)))
|
|
|
|
|
|
(let* ((idx1 (string-index str #\space i))
|
|
|
|
|
|
(idx2 (string-index str #\space (1+ idx1))))
|
|
|
|
|
|
(if (and idx1 idx2)
|
|
|
|
|
|
(let ((code (parse-non-negative-integer str i idx1))
|
|
|
|
|
|
(agent (substring str (1+ idx1) idx2)))
|
|
|
|
|
|
(call-with-values
|
|
|
|
|
|
(lambda () (parse-qstring str (1+ idx2) #:incremental? #t))
|
|
|
|
|
|
(lambda (text i)
|
|
|
|
|
|
(call-with-values
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(let ((c (and (< i len) (string-ref str i))))
|
|
|
|
|
|
(case c
|
|
|
|
|
|
((#\space)
|
|
|
|
|
|
;; we have a date.
|
|
|
|
|
|
(call-with-values
|
|
|
|
|
|
(lambda () (parse-qstring str (1+ i)
|
|
|
|
|
|
#:incremental? #t))
|
|
|
|
|
|
(lambda (date i)
|
|
|
|
|
|
(values text (parse-date date) i))))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(values text #f i)))))
|
|
|
|
|
|
(lambda (text date i)
|
|
|
|
|
|
(let ((w (list code agent text date))
|
|
|
|
|
|
(c (and (< i len) (string-ref str i))))
|
|
|
|
|
|
(case c
|
|
|
|
|
|
((#f) (list w))
|
|
|
|
|
|
((#\,) (cons w (lp (skip-whitespace str (1+ i)))))
|
|
|
|
|
|
(else (bad-header 'warning str))))))))))))))
|
|
|
|
|
|
(lambda (val)
|
|
|
|
|
|
(list-of? val
|
|
|
|
|
|
(lambda (elt)
|
|
|
|
|
|
(and (list? elt)
|
|
|
|
|
|
(= (length elt) 4)
|
|
|
|
|
|
(apply (lambda (code host text date)
|
|
|
|
|
|
(and (non-negative-integer? code) (< code 1000)
|
|
|
|
|
|
(string? host)
|
|
|
|
|
|
(string? text)
|
|
|
|
|
|
(or (not date) (date? date))))
|
|
|
|
|
|
elt)))))
|
|
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(write-list
|
|
|
|
|
|
val port
|
|
|
|
|
|
(lambda (w port)
|
|
|
|
|
|
(apply
|
|
|
|
|
|
(lambda (code host text date)
|
|
|
|
|
|
(display code port)
|
|
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(display host port)
|
|
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(write-qstring text port)
|
|
|
|
|
|
(if date
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(write-date date port))))
|
|
|
|
|
|
w))
|
|
|
|
|
|
", "))
|
|
|
|
|
|
#:multiple? #t)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Entity headers
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
;; Allow = #Method
|
|
|
|
|
|
;;
|
2011-01-08 21:32:14 -08:00
|
|
|
|
(declare-symbol-list-header! "Allow")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
2014-01-18 21:08:52 +01:00
|
|
|
|
;; Content-Disposition = disposition-type *( ";" disposition-parm )
|
|
|
|
|
|
;; disposition-type = "attachment" | disp-extension-token
|
|
|
|
|
|
;; disposition-parm = filename-parm | disp-extension-parm
|
|
|
|
|
|
;; filename-parm = "filename" "=" quoted-string
|
|
|
|
|
|
;; disp-extension-token = token
|
|
|
|
|
|
;; disp-extension-parm = token "=" ( token | quoted-string )
|
|
|
|
|
|
;;
|
|
|
|
|
|
(declare-header! "Content-Disposition"
|
|
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(let ((disposition (parse-param-list str default-val-parser)))
|
|
|
|
|
|
;; Lazily reuse the param list parser.
|
|
|
|
|
|
(unless (and (pair? disposition)
|
|
|
|
|
|
(null? (cdr disposition)))
|
|
|
|
|
|
(bad-header-component 'content-disposition str))
|
|
|
|
|
|
(car disposition)))
|
|
|
|
|
|
(lambda (val)
|
|
|
|
|
|
(and (pair? val)
|
|
|
|
|
|
(symbol? (car val))
|
|
|
|
|
|
(list-of? (cdr val)
|
|
|
|
|
|
(lambda (x)
|
|
|
|
|
|
(and (pair? x) (symbol? (car x)) (string? (cdr x)))))))
|
|
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(write-param-list (list val) port)))
|
|
|
|
|
|
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;; Content-Encoding = 1#content-coding
|
|
|
|
|
|
;;
|
2011-01-08 21:32:14 -08:00
|
|
|
|
(declare-symbol-list-header! "Content-Encoding")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Content-Language = 1#language-tag
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-string-list-header! "Content-Language")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Content-Length = 1*DIGIT
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-integer-header! "Content-Length")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
2014-10-15 11:49:41 +02:00
|
|
|
|
;; Content-Location = URI-reference
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;
|
2014-10-15 11:49:41 +02:00
|
|
|
|
(declare-uri-reference-header! "Content-Location")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Content-MD5 = <base64 of 128 bit MD5 digest as per RFC 1864>
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-opaque-header! "Content-MD5")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Content-Range = content-range-spec
|
|
|
|
|
|
;; content-range-spec = byte-content-range-spec
|
|
|
|
|
|
;; byte-content-range-spec = bytes-unit SP
|
|
|
|
|
|
;; byte-range-resp-spec "/"
|
|
|
|
|
|
;; ( instance-length | "*" )
|
|
|
|
|
|
;; byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
|
|
|
|
|
|
;; | "*"
|
|
|
|
|
|
;; instance-length = 1*DIGIT
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "Content-Range"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(let ((dash (string-index str #\-))
|
|
|
|
|
|
(slash (string-index str #\/)))
|
|
|
|
|
|
(if (and (string-prefix? "bytes " str) slash)
|
|
|
|
|
|
(list 'bytes
|
|
|
|
|
|
(cond
|
|
|
|
|
|
(dash
|
|
|
|
|
|
(cons
|
|
|
|
|
|
(parse-non-negative-integer str 6 dash)
|
|
|
|
|
|
(parse-non-negative-integer str (1+ dash) slash)))
|
|
|
|
|
|
((string= str "*" 6 slash)
|
|
|
|
|
|
'*)
|
|
|
|
|
|
(else
|
|
|
|
|
|
(bad-header 'content-range str)))
|
|
|
|
|
|
(if (string= str "*" (1+ slash))
|
|
|
|
|
|
'*
|
|
|
|
|
|
(parse-non-negative-integer str (1+ slash))))
|
|
|
|
|
|
(bad-header 'content-range str))))
|
|
|
|
|
|
(lambda (val)
|
|
|
|
|
|
(and (list? val) (= (length val) 3)
|
|
|
|
|
|
(symbol? (car val))
|
|
|
|
|
|
(let ((x (cadr val)))
|
|
|
|
|
|
(or (eq? x '*)
|
|
|
|
|
|
(and (pair? x)
|
|
|
|
|
|
(non-negative-integer? (car x))
|
|
|
|
|
|
(non-negative-integer? (cdr x)))))
|
|
|
|
|
|
(let ((x (caddr val)))
|
|
|
|
|
|
(or (eq? x '*)
|
|
|
|
|
|
(non-negative-integer? x)))))
|
|
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(display (car val) port)
|
|
|
|
|
|
(display #\space port)
|
|
|
|
|
|
(if (eq? (cadr val) '*)
|
|
|
|
|
|
(display #\* port)
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display (caadr val) port)
|
|
|
|
|
|
(display #\- port)
|
|
|
|
|
|
(display (caadr val) port)))
|
|
|
|
|
|
(if (eq? (caddr val) '*)
|
|
|
|
|
|
(display #\* port)
|
|
|
|
|
|
(display (caddr val) port))))
|
|
|
|
|
|
|
|
|
|
|
|
;; Content-Type = media-type
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "Content-Type"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(let ((parts (string-split str #\;)))
|
2010-11-13 18:17:28 +01:00
|
|
|
|
(cons (parse-media-type (car parts))
|
|
|
|
|
|
(map (lambda (x)
|
|
|
|
|
|
(let ((eq (string-index x #\=)))
|
|
|
|
|
|
(if (and eq (= eq (string-rindex x #\=)))
|
2012-03-07 12:39:30 +01:00
|
|
|
|
(cons
|
|
|
|
|
|
(string->symbol
|
|
|
|
|
|
(string-trim x char-set:whitespace 0 eq))
|
|
|
|
|
|
(string-trim-right x char-set:whitespace (1+ eq)))
|
2010-11-13 18:17:28 +01:00
|
|
|
|
(bad-header 'content-type str))))
|
|
|
|
|
|
(cdr parts)))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (val)
|
2010-11-13 18:17:28 +01:00
|
|
|
|
(and (pair? val)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(symbol? (car val))
|
2010-11-13 18:17:28 +01:00
|
|
|
|
(list-of? (cdr val)
|
|
|
|
|
|
(lambda (x)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(and (pair? x) (symbol? (car x)) (string? (cdr x)))))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(display (car val) port)
|
2010-11-13 18:17:28 +01:00
|
|
|
|
(if (pair? (cdr val))
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display ";" port)
|
|
|
|
|
|
(write-list
|
|
|
|
|
|
(cdr val) port
|
|
|
|
|
|
(lambda (pair port)
|
|
|
|
|
|
(display (car pair) port)
|
|
|
|
|
|
(display #\= port)
|
|
|
|
|
|
(display (cdr pair) port))
|
|
|
|
|
|
";")))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Expires = HTTP-date
|
|
|
|
|
|
;;
|
2011-12-27 22:24:28 +08:00
|
|
|
|
(define *date-in-the-past* (parse-date "Thu, 01 Jan 1970 00:00:00 GMT"))
|
|
|
|
|
|
|
|
|
|
|
|
(declare-header! "Expires"
|
|
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(if (member str '("0" "-1"))
|
|
|
|
|
|
*date-in-the-past*
|
|
|
|
|
|
(parse-date str)))
|
|
|
|
|
|
date?
|
|
|
|
|
|
write-date)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Last-Modified = HTTP-date
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-date-header! "Last-Modified")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Request headers
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
;; Accept = #( media-range [ accept-params ] )
|
|
|
|
|
|
;; media-range = ( "*/*" | ( type "/" "*" ) | ( type "/" subtype ) )
|
|
|
|
|
|
;; *( ";" parameter )
|
|
|
|
|
|
;; accept-params = ";" "q" "=" qvalue *( accept-extension )
|
|
|
|
|
|
;; accept-extension = ";" token [ "=" ( token | quoted-string ) ]
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-param-list-header! "Accept"
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
;; -> (type/subtype (sym-prop . str-val) ...) ...)
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
;; with the exception of prop `q', in which case the val will be a
|
|
|
|
|
|
;; valid quality value
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;
|
|
|
|
|
|
(lambda (k v)
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(if (eq? k 'q)
|
|
|
|
|
|
(parse-quality v)
|
|
|
|
|
|
v))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (k v)
|
|
|
|
|
|
(if (eq? k 'q)
|
|
|
|
|
|
(valid-quality? v)
|
2011-11-23 20:56:10 +01:00
|
|
|
|
(or (not v) (string? v))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (k v port)
|
|
|
|
|
|
(if (eq? k 'q)
|
|
|
|
|
|
(write-quality v port)
|
|
|
|
|
|
(default-val-writer k v port))))
|
|
|
|
|
|
|
|
|
|
|
|
;; Accept-Charset = 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-quality-list-header! "Accept-Charset")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Accept-Encoding = 1#( codings [ ";" "q" "=" qvalue ] )
|
|
|
|
|
|
;; codings = ( content-coding | "*" )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-quality-list-header! "Accept-Encoding")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Accept-Language = 1#( language-range [ ";" "q" "=" qvalue ] )
|
|
|
|
|
|
;; language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-quality-list-header! "Accept-Language")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Authorization = credentials
|
2011-01-10 08:20:29 -08:00
|
|
|
|
;; credentials = auth-scheme #auth-param
|
|
|
|
|
|
;; auth-scheme = token
|
|
|
|
|
|
;; auth-param = token "=" ( token | quoted-string )
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;
|
2011-01-10 08:20:29 -08:00
|
|
|
|
(declare-credentials-header! "Authorization")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Expect = 1#expectation
|
|
|
|
|
|
;; expectation = "100-continue" | expectation-extension
|
|
|
|
|
|
;; expectation-extension = token [ "=" ( token | quoted-string )
|
|
|
|
|
|
;; *expect-params ]
|
|
|
|
|
|
;; expect-params = ";" token [ "=" ( token | quoted-string ) ]
|
|
|
|
|
|
;;
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(declare-param-list-header! "Expect")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; From = mailbox
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; Should be an email address; we just pass on the string as-is.
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-opaque-header! "From")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Host = host [ ":" port ]
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "Host"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (str)
|
2013-03-16 19:53:07 +08:00
|
|
|
|
(let* ((rbracket (string-index str #\]))
|
|
|
|
|
|
(colon (string-index str #\: (or rbracket 0)))
|
|
|
|
|
|
(host (cond
|
|
|
|
|
|
(rbracket
|
|
|
|
|
|
(unless (eqv? (string-ref str 0) #\[)
|
|
|
|
|
|
(bad-header 'host str))
|
|
|
|
|
|
(substring str 1 rbracket))
|
|
|
|
|
|
(colon
|
|
|
|
|
|
(substring str 0 colon))
|
|
|
|
|
|
(else
|
|
|
|
|
|
str)))
|
|
|
|
|
|
(port (and colon
|
|
|
|
|
|
(parse-non-negative-integer str (1+ colon)))))
|
|
|
|
|
|
(cons host port)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (val)
|
|
|
|
|
|
(and (pair? val)
|
|
|
|
|
|
(string? (car val))
|
|
|
|
|
|
(or (not (cdr val))
|
|
|
|
|
|
(non-negative-integer? (cdr val)))))
|
|
|
|
|
|
(lambda (val port)
|
2013-03-16 19:53:07 +08:00
|
|
|
|
(if (string-index (car val) #\:)
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display #\[ port)
|
|
|
|
|
|
(display (car val) port)
|
|
|
|
|
|
(display #\] port))
|
|
|
|
|
|
(display (car val) port))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(if (cdr val)
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(display #\: port)
|
|
|
|
|
|
(display (cdr val) port)))))
|
|
|
|
|
|
|
|
|
|
|
|
;; If-Match = ( "*" | 1#entity-tag )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-entity-tag-list-header! "If-Match")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; If-Modified-Since = HTTP-date
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-date-header! "If-Modified-Since")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; If-None-Match = ( "*" | 1#entity-tag )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-entity-tag-list-header! "If-None-Match")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; If-Range = ( entity-tag | HTTP-date )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "If-Range"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(if (or (string-prefix? "\"" str)
|
|
|
|
|
|
(string-prefix? "W/" str))
|
|
|
|
|
|
(parse-entity-tag str)
|
|
|
|
|
|
(parse-date str)))
|
|
|
|
|
|
(lambda (val)
|
|
|
|
|
|
(or (date? val) (entity-tag? val)))
|
|
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(if (date? val)
|
|
|
|
|
|
(write-date val port)
|
|
|
|
|
|
(write-entity-tag val port))))
|
|
|
|
|
|
|
|
|
|
|
|
;; If-Unmodified-Since = HTTP-date
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-date-header! "If-Unmodified-Since")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Max-Forwards = 1*DIGIT
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-integer-header! "Max-Forwards")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Proxy-Authorization = credentials
|
|
|
|
|
|
;;
|
2011-01-10 08:20:29 -08:00
|
|
|
|
(declare-credentials-header! "Proxy-Authorization")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Range = "Range" ":" ranges-specifier
|
|
|
|
|
|
;; ranges-specifier = byte-ranges-specifier
|
|
|
|
|
|
;; byte-ranges-specifier = bytes-unit "=" byte-range-set
|
|
|
|
|
|
;; byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
|
|
|
|
|
|
;; byte-range-spec = first-byte-pos "-" [last-byte-pos]
|
|
|
|
|
|
;; first-byte-pos = 1*DIGIT
|
|
|
|
|
|
;; last-byte-pos = 1*DIGIT
|
|
|
|
|
|
;; suffix-byte-range-spec = "-" suffix-length
|
|
|
|
|
|
;; suffix-length = 1*DIGIT
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "Range"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(if (string-prefix? "bytes=" str)
|
|
|
|
|
|
(cons
|
|
|
|
|
|
'bytes
|
|
|
|
|
|
(map (lambda (x)
|
|
|
|
|
|
(let ((dash (string-index x #\-)))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((not dash)
|
|
|
|
|
|
(bad-header 'range str))
|
|
|
|
|
|
((zero? dash)
|
|
|
|
|
|
(cons #f (parse-non-negative-integer x 1)))
|
|
|
|
|
|
((= dash (1- (string-length x)))
|
|
|
|
|
|
(cons (parse-non-negative-integer x 0 dash) #f))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(cons (parse-non-negative-integer x 0 dash)
|
|
|
|
|
|
(parse-non-negative-integer x (1+ dash)))))))
|
|
|
|
|
|
(string-split (substring str 6) #\,)))
|
|
|
|
|
|
(bad-header 'range str)))
|
|
|
|
|
|
(lambda (val)
|
|
|
|
|
|
(and (pair? val)
|
|
|
|
|
|
(symbol? (car val))
|
|
|
|
|
|
(list-of? (cdr val)
|
|
|
|
|
|
(lambda (elt)
|
|
|
|
|
|
(and (pair? elt)
|
|
|
|
|
|
(let ((x (car elt)) (y (cdr elt)))
|
|
|
|
|
|
(and (or x y)
|
|
|
|
|
|
(or (not x) (non-negative-integer? x))
|
|
|
|
|
|
(or (not y) (non-negative-integer? y)))))))))
|
|
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(display (car val) port)
|
|
|
|
|
|
(display #\= port)
|
|
|
|
|
|
(write-list
|
|
|
|
|
|
(cdr val) port
|
|
|
|
|
|
(lambda (pair port)
|
|
|
|
|
|
(if (car pair)
|
|
|
|
|
|
(display (car pair) port))
|
|
|
|
|
|
(display #\- port)
|
|
|
|
|
|
(if (cdr pair)
|
|
|
|
|
|
(display (cdr pair) port)))
|
|
|
|
|
|
",")))
|
|
|
|
|
|
|
2014-10-15 11:49:41 +02:00
|
|
|
|
;; Referer = URI-reference
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;
|
2014-10-15 11:49:41 +02:00
|
|
|
|
(declare-uri-reference-header! "Referer")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; TE = #( t-codings )
|
|
|
|
|
|
;; t-codings = "trailers" | ( transfer-extension [ accept-params ] )
|
|
|
|
|
|
;;
|
(web http): keys are always symbols
* module/web/http.scm (parse-media-type): Parse media types as symbols.
(parse-key-value-list, parse-param-component, parse-param-list):
Change kons to val-parser. Always parse keys as symbols, and always
either cons, if there is a val, or just have the key, if there is no
val. Easier to explain and just as correct.
(declare-param-list-header!, declare-key-value-list-header!): Adapt to
key-list and param-list kons change.
("Cache-Control", "Pragma", "Transfer-Encoding", "Accept", "Expect")
("TE"): Likewise, adapt.
("Content-Type"): Param keys are symbols.
2011-01-08 20:50:46 -08:00
|
|
|
|
(declare-param-list-header! "TE")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; User-Agent = 1*( product | comment )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-opaque-header! "User-Agent")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Reponse headers
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
;; Accept-Ranges = acceptable-ranges
|
|
|
|
|
|
;; acceptable-ranges = 1#range-unit | "none"
|
|
|
|
|
|
;;
|
2011-01-08 21:32:14 -08:00
|
|
|
|
(declare-symbol-list-header! "Accept-Ranges")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Age = age-value
|
|
|
|
|
|
;; age-value = delta-seconds
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-integer-header! "Age")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; ETag = entity-tag
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "ETag"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
parse-entity-tag
|
|
|
|
|
|
entity-tag?
|
|
|
|
|
|
write-entity-tag)
|
|
|
|
|
|
|
2014-10-15 11:49:41 +02:00
|
|
|
|
;; Location = URI-reference
|
|
|
|
|
|
;;
|
|
|
|
|
|
;; In RFC 2616, Location was specified as being an absolute URI. This
|
|
|
|
|
|
;; was changed in RFC 7231 to permit URI references generally, which
|
|
|
|
|
|
;; matches web reality.
|
2010-10-24 12:36:17 +02:00
|
|
|
|
;;
|
2014-10-15 11:49:41 +02:00
|
|
|
|
(declare-uri-reference-header! "Location")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Proxy-Authenticate = 1#challenge
|
|
|
|
|
|
;;
|
2011-01-10 08:20:29 -08:00
|
|
|
|
(declare-challenge-list-header! "Proxy-Authenticate")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Retry-After = ( HTTP-date | delta-seconds )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "Retry-After"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(if (and (not (string-null? str))
|
|
|
|
|
|
(char-numeric? (string-ref str 0)))
|
|
|
|
|
|
(parse-non-negative-integer str)
|
|
|
|
|
|
(parse-date str)))
|
|
|
|
|
|
(lambda (val)
|
|
|
|
|
|
(or (date? val) (non-negative-integer? val)))
|
|
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(if (date? val)
|
|
|
|
|
|
(write-date val port)
|
|
|
|
|
|
(display val port))))
|
|
|
|
|
|
|
|
|
|
|
|
;; Server = 1*( product | comment )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-opaque-header! "Server")
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; Vary = ( "*" | 1#field-name )
|
|
|
|
|
|
;;
|
(web http): header names always represented as symbols
* module/web/http.scm (declare-header!): No need to specify `sym', as it
can be derived from `name'. Change to take parser, validator, and
writer as positional arguments, and multiple? as a keyword.
(parse-header): Change to take the header as a symbol already, and
just return the parsed value. All headers are symbols now, including
unknown headers. I feel OK doing this given that the symbol GC works
now.
(lookup-header-decl): Only look up headers by symbol.
(read-header): Adapt to parse-header change.
(valid-header?, write-header): Adapt to all headers being symbols.
(split-header-names, list-of-header-names?, write-header-list):
Represent all header names as symbols.
(declare-opaque-header!, declare-date-header!)
(declare-string-list-header!, declare-header-list-header!)
(declare-integer-header!, declare-uri-header!)
(declare-quality-list-header!, declare-param-list-header!)
(declare-key-value-list-header!, declare-entity-tag-list-header!):
Change to be functions instead of syntax, and no need to specify the
symbolic name. Update all header declarations accordingly.
* module/web/request.scm (validate-headers):
* module/web/response.scm (validate-headers): Adapt to all headers being
symbols.
* test-suite/tests/web-http.test (pass-if-parse, pass-if-any-error)
(pass-if-parse-error): Update for parse-header change.
("general headers"): Update header list examples to be all symbols.
2011-01-08 10:54:07 -08:00
|
|
|
|
(declare-header! "Vary"
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (str)
|
|
|
|
|
|
(if (equal? str "*")
|
|
|
|
|
|
'*
|
2010-12-16 18:12:08 +01:00
|
|
|
|
(split-header-names str)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (val)
|
2010-12-16 18:12:08 +01:00
|
|
|
|
(or (eq? val '*) (list-of-header-names? val)))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
(lambda (val port)
|
|
|
|
|
|
(if (eq? val '*)
|
|
|
|
|
|
(display "*" port)
|
2010-12-16 18:12:08 +01:00
|
|
|
|
(write-header-list val port))))
|
2010-10-24 12:36:17 +02:00
|
|
|
|
|
|
|
|
|
|
;; WWW-Authenticate = 1#challenge
|
|
|
|
|
|
;;
|
2011-01-10 08:20:29 -08:00
|
|
|
|
(declare-challenge-list-header! "WWW-Authenticate")
|
2012-05-08 00:06:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; Chunked Responses
|
|
|
|
|
|
(define (read-chunk-header port)
|
|
|
|
|
|
(let* ((str (read-line port))
|
|
|
|
|
|
(extension-start (string-index str (lambda (c) (or (char=? c #\;)
|
|
|
|
|
|
(char=? c #\return)))))
|
|
|
|
|
|
(size (string->number (if extension-start ; unnecessary?
|
|
|
|
|
|
(substring str 0 extension-start)
|
|
|
|
|
|
str)
|
|
|
|
|
|
16)))
|
|
|
|
|
|
size))
|
|
|
|
|
|
|
|
|
|
|
|
(define (read-chunk port)
|
|
|
|
|
|
(let ((size (read-chunk-header port)))
|
|
|
|
|
|
(read-chunk-body port size)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (read-chunk-body port size)
|
|
|
|
|
|
(let ((bv (get-bytevector-n port size)))
|
|
|
|
|
|
(get-u8 port) ; CR
|
|
|
|
|
|
(get-u8 port) ; LF
|
|
|
|
|
|
bv))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (make-chunked-input-port port #:key (keep-alive? #f))
|
|
|
|
|
|
"Returns a new port which translates HTTP chunked transfer encoded
|
2012-11-24 14:10:12 +08:00
|
|
|
|
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."
|
2012-05-08 00:06:01 +01:00
|
|
|
|
(define (next-chunk)
|
|
|
|
|
|
(read-chunk port))
|
|
|
|
|
|
(define finished? #f)
|
|
|
|
|
|
(define (close)
|
|
|
|
|
|
(unless keep-alive?
|
|
|
|
|
|
(close-port port)))
|
|
|
|
|
|
(define buffer #vu8())
|
|
|
|
|
|
(define buffer-size 0)
|
|
|
|
|
|
(define buffer-pointer 0)
|
|
|
|
|
|
(define (read! bv idx to-read)
|
|
|
|
|
|
(define (loop to-read num-read)
|
|
|
|
|
|
(cond ((or finished? (zero? to-read))
|
|
|
|
|
|
num-read)
|
|
|
|
|
|
((<= to-read (- buffer-size buffer-pointer))
|
|
|
|
|
|
(bytevector-copy! buffer buffer-pointer
|
|
|
|
|
|
bv (+ idx num-read)
|
|
|
|
|
|
to-read)
|
|
|
|
|
|
(set! buffer-pointer (+ buffer-pointer to-read))
|
|
|
|
|
|
(loop 0 (+ num-read to-read)))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(let ((n (- buffer-size buffer-pointer)))
|
|
|
|
|
|
(bytevector-copy! buffer buffer-pointer
|
|
|
|
|
|
bv (+ idx num-read)
|
|
|
|
|
|
n)
|
|
|
|
|
|
(set! buffer (next-chunk))
|
|
|
|
|
|
(set! buffer-pointer 0)
|
|
|
|
|
|
(set! buffer-size (bytevector-length buffer))
|
|
|
|
|
|
(set! finished? (= buffer-size 0))
|
|
|
|
|
|
(loop (- to-read n)
|
|
|
|
|
|
(+ num-read n))))))
|
|
|
|
|
|
(loop to-read 0))
|
|
|
|
|
|
(make-custom-binary-input-port "chunked input port" read! #f #f close))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (make-chunked-output-port port #:key (keep-alive? #f))
|
|
|
|
|
|
"Returns a new port which translates non-encoded data into a HTTP
|
2012-11-24 14:10:12 +08:00
|
|
|
|
chunked transfer encoded data and writes this to PORT. Data
|
2012-05-08 00:06:01 +01:00
|
|
|
|
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
|
2012-11-24 14:10:12 +08:00
|
|
|
|
chunk. When the port is closed it will also close PORT, unless
|
2012-05-08 00:06:01 +01:00
|
|
|
|
KEEP-ALIVE? is true."
|
|
|
|
|
|
(define (q-for-each f q)
|
|
|
|
|
|
(while (not (q-empty? q))
|
|
|
|
|
|
(f (deq! q))))
|
|
|
|
|
|
(define queue (make-q))
|
|
|
|
|
|
(define (put-char c)
|
|
|
|
|
|
(enq! queue c))
|
|
|
|
|
|
(define (put-string s)
|
|
|
|
|
|
(string-for-each (lambda (c) (enq! queue c))
|
|
|
|
|
|
s))
|
|
|
|
|
|
(define (flush)
|
|
|
|
|
|
;; It is important that we do _not_ write a chunk if the queue is
|
|
|
|
|
|
;; empty, since it will be treated as the final chunk.
|
|
|
|
|
|
(unless (q-empty? queue)
|
|
|
|
|
|
(let ((len (q-length queue)))
|
|
|
|
|
|
(display (number->string len 16) port)
|
|
|
|
|
|
(display "\r\n" port)
|
|
|
|
|
|
(q-for-each (lambda (elem) (write-char elem port))
|
|
|
|
|
|
queue)
|
|
|
|
|
|
(display "\r\n" port))))
|
|
|
|
|
|
(define (close)
|
|
|
|
|
|
(flush)
|
|
|
|
|
|
(display "0\r\n" port)
|
|
|
|
|
|
(force-output port)
|
|
|
|
|
|
(unless keep-alive?
|
|
|
|
|
|
(close-port port)))
|
|
|
|
|
|
(make-soft-port (vector put-char put-string flush #f close) "w"))
|
2013-06-07 00:47:33 -04:00
|
|
|
|
|
|
|
|
|
|
(define %http-proxy-port? (make-object-property))
|
|
|
|
|
|
(define (http-proxy-port? port) (%http-proxy-port? port))
|
|
|
|
|
|
(define (set-http-proxy-port?! port flag)
|
|
|
|
|
|
(set! (%http-proxy-port? port) flag))
|