fix validators for various list-style headers
[bpt/guile.git] / module / web / http.scm
1 ;;; HTTP messages
2
3 ;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
4
5 ;; This library is free software; you can redistribute it and/or
6 ;; modify it under the terms of the GNU Lesser General Public
7 ;; License as published by the Free Software Foundation; either
8 ;; version 3 of the License, or (at your option) any later version.
9 ;;
10 ;; This library is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;; Lesser General Public License for more details.
14 ;;
15 ;; You should have received a copy of the GNU Lesser General Public
16 ;; License along with this library; if not, write to the Free Software
17 ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 ;; 02110-1301 USA
19
20 ;;; Commentary:
21 ;;;
22 ;;; This module has a number of routines to parse textual
23 ;;; representations of HTTP data into native Scheme data structures.
24 ;;;
25 ;;; It tries to follow RFCs fairly strictly---the road to perdition
26 ;;; being paved with compatibility hacks---though some allowances are
27 ;;; made for not-too-divergent texts (like a quality of .2 which should
28 ;;; be 0.2, etc).
29 ;;;
30 ;;; Code:
31
32 (define-module (web http)
33 #:use-module ((srfi srfi-1) #:select (append-map! map!))
34 #:use-module (srfi srfi-9)
35 #:use-module (srfi srfi-19)
36 #:use-module (ice-9 rdelim)
37 #:use-module (web uri)
38 #:export (string->header
39 header->string
40
41 declare-header!
42 known-header?
43 header-parser
44 header-validator
45 header-writer
46
47 read-header
48 parse-header
49 valid-header?
50 write-header
51
52 read-headers
53 write-headers
54
55 parse-http-method
56 parse-http-version
57 parse-request-uri
58
59 read-request-line
60 write-request-line
61 read-response-line
62 write-response-line))
63
64
65 ;;; TODO
66 ;;;
67 ;;; Look at quality lists with more insight.
68 ;;; Think about `accept' a bit more.
69 ;;;
70
71
72 (define (string->header name)
73 "Parse @var{name} to a symbolic header name."
74 (string->symbol (string-downcase name)))
75
76 (define-record-type <header-decl>
77 (make-header-decl name parser validator writer multiple?)
78 header-decl?
79 (name header-decl-name)
80 (parser header-decl-parser)
81 (validator header-decl-validator)
82 (writer header-decl-writer)
83 (multiple? header-decl-multiple?))
84
85 ;; sym -> header
86 (define *declared-headers* (make-hash-table))
87
88 (define (lookup-header-decl sym)
89 (hashq-ref *declared-headers* sym))
90
91 (define* (declare-header! name
92 parser
93 validator
94 writer
95 #:key multiple?)
96 "Define a parser, validator, and writer for the HTTP header, @var{name}.
97
98 @var{parser} should be a procedure that takes a string and returns a
99 Scheme value. @var{validator} is a predicate for whether the given
100 Scheme value is valid for this header. @var{writer} takes a value and a
101 port, and writes the value to the port."
102 (if (and (string? name) parser validator writer)
103 (let ((decl (make-header-decl name parser validator writer multiple?)))
104 (hashq-set! *declared-headers* (string->header name) decl)
105 decl)
106 (error "bad header decl" name parser validator writer multiple?)))
107
108 (define (header->string sym)
109 "Return the string form for the header named @var{sym}."
110 (let ((decl (lookup-header-decl sym)))
111 (if decl
112 (header-decl-name decl)
113 (string-titlecase (symbol->string sym)))))
114
115 (define (known-header? sym)
116 "Return @code{#t} if there are parsers and writers registered for this
117 header, otherwise @code{#f}."
118 (and (lookup-header-decl sym) #t))
119
120 (define (header-parser sym)
121 "Returns a procedure to parse values for the given header."
122 (let ((decl (lookup-header-decl sym)))
123 (if decl
124 (header-decl-parser decl)
125 (lambda (x) x))))
126
127 (define (header-validator sym)
128 "Returns a procedure to validate values for the given header."
129 (let ((decl (lookup-header-decl sym)))
130 (if decl
131 (header-decl-validator decl)
132 string?)))
133
134 (define (header-writer sym)
135 "Returns a procedure to write values for the given header to a given
136 port."
137 (let ((decl (lookup-header-decl sym)))
138 (if decl
139 (header-decl-writer decl)
140 display)))
141
142 (define (read-line* port)
143 (let* ((pair (%read-line port))
144 (line (car pair))
145 (delim (cdr pair)))
146 (if (and (string? line) (char? delim))
147 (let ((orig-len (string-length line)))
148 (let lp ((len orig-len))
149 (if (and (> len 0)
150 (char-whitespace? (string-ref line (1- len))))
151 (lp (1- len))
152 (if (= len orig-len)
153 line
154 (substring line 0 len)))))
155 (bad-header '%read line))))
156
157 (define (read-continuation-line port val)
158 (if (or (eqv? (peek-char port) #\space)
159 (eqv? (peek-char port) #\tab))
160 (read-continuation-line port
161 (string-append val
162 (begin
163 (read-line* port))))
164 val))
165
166 (define *eof* (call-with-input-string "" read))
167
168 (define (read-header port)
169 "Reads one HTTP header from @var{port}. Returns two values: the header
170 name and the parsed Scheme value. May raise an exception if the header
171 was known but the value was invalid.
172
173 Returns the end-of-file object for both values if the end of the message
174 body was reached (i.e., a blank line)."
175 (let ((line (read-line* port)))
176 (if (or (string-null? line)
177 (string=? line "\r"))
178 (values *eof* *eof*)
179 (let* ((delim (or (string-index line #\:)
180 (bad-header '%read line)))
181 (sym (string->header (substring line 0 delim))))
182 (values
183 sym
184 (parse-header
185 sym
186 (read-continuation-line
187 port
188 (string-trim-both line char-whitespace? (1+ delim)))))))))
189
190 (define (parse-header sym val)
191 "Parse @var{val}, a string, with the parser registered for the header
192 named @var{sym}.
193
194 Returns the parsed value. If a parser was not found, the value is
195 returned as a string."
196 ((header-parser sym) val))
197
198 (define (valid-header? sym val)
199 "Returns a true value iff @var{val} is a valid Scheme value for the
200 header with name @var{sym}."
201 (if (symbol? sym)
202 ((header-validator sym) val)
203 (error "header name not a symbol" sym)))
204
205 (define (write-header sym val port)
206 "Writes the given header name and value to @var{port}. If @var{sym}
207 is a known header, uses the specific writer registered for that header.
208 Otherwise the value is written using @var{display}."
209 (display (header->string sym) port)
210 (display ": " port)
211 ((header-writer sym) val port)
212 (display "\r\n" port))
213
214 (define (read-headers port)
215 "Read an HTTP message from @var{port}, returning the headers as an
216 ordered alist."
217 (let lp ((headers '()))
218 (call-with-values (lambda () (read-header port))
219 (lambda (k v)
220 (if (eof-object? k)
221 (reverse! headers)
222 (lp (acons k v headers)))))))
223
224 (define (write-headers headers port)
225 "Write the given header alist to @var{port}. Doesn't write the final
226 \\r\\n, as the user might want to add another header."
227 (let lp ((headers headers))
228 (if (pair? headers)
229 (begin
230 (write-header (caar headers) (cdar headers) port)
231 (lp (cdr headers))))))
232
233
234 \f
235
236 ;;;
237 ;;; Utilities
238 ;;;
239
240 (define (bad-header sym val)
241 (throw 'bad-header sym val))
242 (define (bad-header-component sym val)
243 (throw 'bad-header sym val))
244
245 (define (parse-opaque-string str)
246 str)
247 (define (validate-opaque-string val)
248 (string? val))
249 (define (write-opaque-string val port)
250 (display val port))
251
252 (define separators-without-slash
253 (string->char-set "[^][()<>@,;:\\\"?= \t]"))
254 (define (validate-media-type str)
255 (let ((idx (string-index str #\/)))
256 (and idx (= idx (string-rindex str #\/))
257 (not (string-index str separators-without-slash)))))
258 (define (parse-media-type str)
259 (if (validate-media-type str)
260 (string->symbol str)
261 (bad-header-component 'media-type str)))
262
263 (define* (skip-whitespace str #:optional (start 0) (end (string-length str)))
264 (let lp ((i start))
265 (if (and (< i end) (char-whitespace? (string-ref str i)))
266 (lp (1+ i))
267 i)))
268
269 (define* (trim-whitespace str #:optional (start 0) (end (string-length str)))
270 (let lp ((i end))
271 (if (and (< start i) (char-whitespace? (string-ref str (1- i))))
272 (lp (1- i))
273 i)))
274
275 (define* (split-and-trim str #:optional (delim #\,)
276 (start 0) (end (string-length str)))
277 (let lp ((i start))
278 (if (< i end)
279 (let* ((idx (string-index str delim i end))
280 (tok (string-trim-both str char-whitespace? i (or idx end))))
281 (cons tok (split-and-trim str delim (if idx (1+ idx) end) end)))
282 '())))
283
284 (define (list-of-strings? val)
285 (list-of? val string?))
286
287 (define (write-list-of-strings val port)
288 (write-list val port display ", "))
289
290 (define (split-header-names str)
291 (map string->header (split-and-trim str)))
292
293 (define (list-of-header-names? val)
294 (list-of? val symbol?))
295
296 (define (write-header-list val port)
297 (write-list val port
298 (lambda (x port)
299 (display (header->string x) port))
300 ", "))
301
302 (define (collect-escaped-string from start len escapes)
303 (let ((to (make-string len)))
304 (let lp ((start start) (i 0) (escapes escapes))
305 (if (null? escapes)
306 (begin
307 (substring-move! from start (+ start (- len i)) to i)
308 to)
309 (let* ((e (car escapes))
310 (next-start (+ start (- e i) 2)))
311 (substring-move! from start (- next-start 2) to i)
312 (string-set! to e (string-ref from (- next-start 1)))
313 (lp next-start (1+ e) (cdr escapes)))))))
314
315 ;; in incremental mode, returns two values: the string, and the index at
316 ;; which the string ended
317 (define* (parse-qstring str #:optional
318 (start 0) (end (trim-whitespace str start))
319 #:key incremental?)
320 (if (and (< start end) (eqv? (string-ref str start) #\"))
321 (let lp ((i (1+ start)) (qi 0) (escapes '()))
322 (if (< i end)
323 (case (string-ref str i)
324 ((#\\)
325 (lp (+ i 2) (1+ qi) (cons qi escapes)))
326 ((#\")
327 (let ((out (collect-escaped-string str (1+ start) qi escapes)))
328 (if incremental?
329 (values out (1+ i))
330 (if (= (1+ i) end)
331 out
332 (bad-header-component 'qstring str)))))
333 (else
334 (lp (1+ i) (1+ qi) escapes)))
335 (bad-header-component 'qstring str)))
336 (bad-header-component 'qstring str)))
337
338 (define (write-list l port write-item delim)
339 (if (pair? l)
340 (let lp ((l l))
341 (write-item (car l) port)
342 (if (pair? (cdr l))
343 (begin
344 (display delim port)
345 (lp (cdr l)))))))
346
347 (define (write-qstring str port)
348 (display #\" port)
349 (if (string-index str #\")
350 ;; optimize me
351 (write-list (string-split str #\") port display "\\\"")
352 (display str port))
353 (display #\" port))
354
355 (define* (parse-quality str #:optional (start 0) (end (string-length str)))
356 (define (char->decimal c)
357 (let ((i (- (char->integer c) (char->integer #\0))))
358 (if (and (<= 0 i) (< i 10))
359 i
360 (bad-header-component 'quality str))))
361 (cond
362 ((not (< start end))
363 (bad-header-component 'quality str))
364 ((eqv? (string-ref str start) #\1)
365 (if (or (string= str "1" start end)
366 (string= str "1." start end)
367 (string= str "1.0" start end)
368 (string= str "1.00" start end)
369 (string= str "1.000" start end))
370 1000
371 (bad-header-component 'quality str)))
372 ((eqv? (string-ref str start) #\0)
373 (if (or (string= str "0" start end)
374 (string= str "0." start end))
375 0
376 (if (< 2 (- end start) 6)
377 (let lp ((place 1) (i (+ start 4)) (q 0))
378 (if (= i (1+ start))
379 (if (eqv? (string-ref str (1+ start)) #\.)
380 q
381 (bad-header-component 'quality str))
382 (lp (* 10 place) (1- i)
383 (if (< i end)
384 (+ q (* place (char->decimal (string-ref str i))))
385 q))))
386 (bad-header-component 'quality str))))
387 ;; Allow the nonstandard .2 instead of 0.2.
388 ((and (eqv? (string-ref str start) #\.)
389 (< 1 (- end start) 5))
390 (let lp ((place 1) (i (+ start 3)) (q 0))
391 (if (= i start)
392 q
393 (lp (* 10 place) (1- i)
394 (if (< i end)
395 (+ q (* place (char->decimal (string-ref str i))))
396 q)))))
397 (else
398 (bad-header-component 'quality str))))
399
400 (define (valid-quality? q)
401 (and (non-negative-integer? q) (<= q 1000)))
402
403 (define (write-quality q port)
404 (define (digit->char d)
405 (integer->char (+ (char->integer #\0) d)))
406 (display (digit->char (modulo (quotient q 1000) 10)) port)
407 (display #\. port)
408 (display (digit->char (modulo (quotient q 100) 10)) port)
409 (display (digit->char (modulo (quotient q 10) 10)) port)
410 (display (digit->char (modulo q 10)) port))
411
412 (define (list-of? val pred)
413 (or (null? val)
414 (and (pair? val)
415 (pred (car val))
416 (list-of? (cdr val) pred))))
417
418 (define* (parse-quality-list str)
419 (map (lambda (part)
420 (cond
421 ((string-rindex part #\;)
422 => (lambda (idx)
423 (let ((qpart (string-trim-both part char-whitespace? (1+ idx))))
424 (if (string-prefix? "q=" qpart)
425 (cons (parse-quality qpart 2)
426 (string-trim-both part char-whitespace? 0 idx))
427 (bad-header-component 'quality qpart)))))
428 (else
429 (cons 1000 (string-trim-both part char-whitespace?)))))
430 (string-split str #\,)))
431
432 (define (validate-quality-list l)
433 (list-of? l
434 (lambda (elt)
435 (and (pair? elt)
436 (valid-quality? (car elt))
437 (string? (cdr elt))))))
438
439 (define (write-quality-list l port)
440 (write-list l port
441 (lambda (x port)
442 (let ((q (car x))
443 (str (cdr x)))
444 (display str port)
445 (if (< q 1000)
446 (begin
447 (display ";q=" port)
448 (write-quality q port)))))
449 ","))
450
451 (define* (parse-non-negative-integer val #:optional (start 0)
452 (end (string-length val)))
453 (define (char->decimal c)
454 (let ((i (- (char->integer c) (char->integer #\0))))
455 (if (and (<= 0 i) (< i 10))
456 i
457 (bad-header-component 'non-negative-integer val))))
458 (if (not (< start end))
459 (bad-header-component 'non-negative-integer val)
460 (let lp ((i start) (out 0))
461 (if (< i end)
462 (lp (1+ i)
463 (+ (* out 10) (char->decimal (string-ref val i))))
464 out))))
465
466 (define (non-negative-integer? code)
467 (and (number? code) (>= code 0) (exact? code) (integer? code)))
468
469 (define (default-val-parser k val)
470 val)
471
472 (define (default-val-validator k val)
473 (or (not val) (string? val)))
474
475 (define (default-val-writer k val port)
476 (if (or (string-index val #\;)
477 (string-index val #\,)
478 (string-index val #\"))
479 (write-qstring val port)
480 (display val port)))
481
482 (define* (parse-key-value-list str #:optional
483 (val-parser default-val-parser)
484 (start 0) (end (string-length str)))
485 (let lp ((i start) (out '()))
486 (if (not (< i end))
487 (reverse! out)
488 (let* ((i (skip-whitespace str i end))
489 (eq (string-index str #\= i end))
490 (comma (string-index str #\, i end))
491 (delim (min (or eq end) (or comma end)))
492 (k (string->symbol
493 (substring str i (trim-whitespace str i delim)))))
494 (call-with-values
495 (lambda ()
496 (if (and eq (or (not comma) (< eq comma)))
497 (let ((i (skip-whitespace str (1+ eq) end)))
498 (if (and (< i end) (eqv? (string-ref str i) #\"))
499 (parse-qstring str i end #:incremental? #t)
500 (values (substring str i
501 (trim-whitespace str i
502 (or comma end)))
503 (or comma end))))
504 (values #f delim)))
505 (lambda (v-str next-i)
506 (let ((v (val-parser k v-str))
507 (i (skip-whitespace str next-i end)))
508 (if (or (= i end) (eqv? (string-ref str i) #\,))
509 (lp (1+ i) (cons (if v (cons k v) k) out))
510 (bad-header-component 'key-value-list
511 (substring str start end))))))))))
512
513 (define* (key-value-list? list #:optional
514 (valid? default-val-validator))
515 (list-of? list
516 (lambda (elt)
517 (cond
518 ((pair? elt)
519 (let ((k (car elt))
520 (v (cdr elt)))
521 (and (symbol? k)
522 (valid? k v))))
523 ((symbol? elt)
524 (valid? elt #f))
525 (else #f)))))
526
527 (define* (write-key-value-list list port #:optional
528 (val-writer default-val-writer) (delim ", "))
529 (write-list
530 list port
531 (lambda (x port)
532 (let ((k (if (pair? x) (car x) x))
533 (v (if (pair? x) (cdr x) #f)))
534 (display k port)
535 (if v
536 (begin
537 (display #\= port)
538 (val-writer k v port)))))
539 delim))
540
541 ;; param-component = token [ "=" (token | quoted-string) ] \
542 ;; *(";" token [ "=" (token | quoted-string) ])
543 ;;
544 (define* (parse-param-component str #:optional
545 (val-parser default-val-parser)
546 (start 0) (end (string-length str)))
547 (let lp ((i start) (out '()))
548 (if (not (< i end))
549 (values (reverse! out) end)
550 (let ((delim (string-index str
551 (lambda (c) (memq c '(#\, #\; #\=)))
552 i)))
553 (let ((k (string->symbol
554 (substring str i (trim-whitespace str i (or delim end)))))
555 (delimc (and delim (string-ref str delim))))
556 (case delimc
557 ((#\=)
558 (call-with-values
559 (lambda ()
560 (let ((i (skip-whitespace str (1+ delim) end)))
561 (if (and (< i end) (eqv? (string-ref str i) #\"))
562 (parse-qstring str i end #:incremental? #t)
563 (let ((delim
564 (or (string-index
565 str
566 (lambda (c)
567 (or (eqv? c #\;)
568 (eqv? c #\,)
569 (char-whitespace? c)))
570 i end)
571 end)))
572 (values (substring str i delim)
573 delim)))))
574 (lambda (v-str next-i)
575 (let* ((v (val-parser k v-str))
576 (x (if v (cons k v) k))
577 (i (skip-whitespace str next-i end)))
578 (case (and (< i end) (string-ref str i))
579 ((#f)
580 (values (reverse! (cons x out)) end))
581 ((#\;)
582 (lp (skip-whitespace str (1+ i) end)
583 (cons x out)))
584 (else ; including #\,
585 (values (reverse! (cons x out)) i)))))))
586 ((#\;)
587 (let ((v (val-parser k #f)))
588 (lp (skip-whitespace str (1+ delim) end)
589 (cons (if v (cons k v) k) out))))
590
591 (else ;; either the end of the string or a #\,
592 (let ((v (val-parser k #f)))
593 (values (reverse! (cons (if v (cons k v) k) out))
594 (or delim end))))))))))
595
596 (define* (parse-param-list str #:optional
597 (val-parser default-val-parser)
598 (start 0) (end (string-length str)))
599 (let lp ((i start) (out '()))
600 (call-with-values
601 (lambda () (parse-param-component str val-parser i end))
602 (lambda (item i)
603 (if (< i end)
604 (if (eqv? (string-ref str i) #\,)
605 (lp (skip-whitespace str (1+ i) end)
606 (cons item out))
607 (bad-header-component 'param-list str))
608 (reverse! (cons item out)))))))
609
610 (define* (validate-param-list list #:optional
611 (valid? default-val-validator))
612 (list-of? list
613 (lambda (elt)
614 (key-value-list? elt valid?))))
615
616 (define* (write-param-list list port #:optional
617 (val-writer default-val-writer))
618 (write-list
619 list port
620 (lambda (item port)
621 (write-key-value-list item port val-writer ";"))
622 ","))
623
624 (define-syntax string-match?
625 (lambda (x)
626 (syntax-case x ()
627 ((_ str pat) (string? (syntax->datum #'pat))
628 (let ((p (syntax->datum #'pat)))
629 #`(let ((s str))
630 (and
631 (= (string-length s) #,(string-length p))
632 #,@(let lp ((i 0) (tests '()))
633 (if (< i (string-length p))
634 (let ((c (string-ref p i)))
635 (lp (1+ i)
636 (case c
637 ((#\.) ; Whatever.
638 tests)
639 ((#\d) ; Digit.
640 (cons #`(char-numeric? (string-ref s #,i))
641 tests))
642 ((#\a) ; Alphabetic.
643 (cons #`(char-alphabetic? (string-ref s #,i))
644 tests))
645 (else ; Literal.
646 (cons #`(eqv? (string-ref s #,i) #,c)
647 tests)))))
648 tests)))))))))
649
650 ;; "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun"
651 ;; "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
652
653 (define (parse-month str start end)
654 (define (bad)
655 (bad-header-component 'month (substring str start end)))
656 (if (not (= (- end start) 3))
657 (bad)
658 (let ((a (string-ref str (+ start 0)))
659 (b (string-ref str (+ start 1)))
660 (c (string-ref str (+ start 2))))
661 (case a
662 ((#\J)
663 (case b
664 ((#\a) (case c ((#\n) 1) (else (bad))))
665 ((#\u) (case c ((#\n) 6) ((#\l) 7) (else (bad))))
666 (else (bad))))
667 ((#\F)
668 (case b
669 ((#\e) (case c ((#\b) 2) (else (bad))))
670 (else (bad))))
671 ((#\M)
672 (case b
673 ((#\a) (case c ((#\r) 3) ((#\y) 5) (else (bad))))
674 (else (bad))))
675 ((#\A)
676 (case b
677 ((#\p) (case c ((#\r) 4) (else (bad))))
678 ((#\u) (case c ((#\g) 8) (else (bad))))
679 (else (bad))))
680 ((#\S)
681 (case b
682 ((#\e) (case c ((#\p) 9) (else (bad))))
683 (else (bad))))
684 ((#\O)
685 (case b
686 ((#\c) (case c ((#\t) 10) (else (bad))))
687 (else (bad))))
688 ((#\N)
689 (case b
690 ((#\o) (case c ((#\v) 11) (else (bad))))
691 (else (bad))))
692 ((#\D)
693 (case b
694 ((#\e) (case c ((#\c) 12) (else (bad))))
695 (else (bad))))
696 (else (bad))))))
697
698 ;; RFC 822, updated by RFC 1123
699 ;;
700 ;; Sun, 06 Nov 1994 08:49:37 GMT
701 ;; 01234567890123456789012345678
702 ;; 0 1 2
703 (define (parse-rfc-822-date str)
704 ;; We could verify the day of the week but we don't.
705 (cond ((string-match? str "aaa, dd aaa dddd dd:dd:dd GMT")
706 (let ((date (parse-non-negative-integer str 5 7))
707 (month (parse-month str 8 11))
708 (year (parse-non-negative-integer str 12 16))
709 (hour (parse-non-negative-integer str 17 19))
710 (minute (parse-non-negative-integer str 20 22))
711 (second (parse-non-negative-integer str 23 25)))
712 (make-date 0 second minute hour date month year 0)))
713 ((string-match? str "aaa, d aaa dddd dd:dd:dd GMT")
714 (let ((date (parse-non-negative-integer str 5 6))
715 (month (parse-month str 7 10))
716 (year (parse-non-negative-integer str 11 15))
717 (hour (parse-non-negative-integer str 16 18))
718 (minute (parse-non-negative-integer str 19 21))
719 (second (parse-non-negative-integer str 22 24)))
720 (make-date 0 second minute hour date month year 0)))
721 (else
722 (bad-header 'date str) ; prevent tail call
723 #f)))
724
725 ;; RFC 850, updated by RFC 1036
726 ;; Sunday, 06-Nov-94 08:49:37 GMT
727 ;; 0123456789012345678901
728 ;; 0 1 2
729 (define (parse-rfc-850-date str comma)
730 ;; We could verify the day of the week but we don't.
731 (let ((tail (substring str (1+ comma))))
732 (if (not (string-match? tail " dd-aaa-dd dd:dd:dd GMT"))
733 (bad-header 'date str))
734 (let ((date (parse-non-negative-integer tail 1 3))
735 (month (parse-month tail 4 7))
736 (year (parse-non-negative-integer tail 8 10))
737 (hour (parse-non-negative-integer tail 11 13))
738 (minute (parse-non-negative-integer tail 14 16))
739 (second (parse-non-negative-integer tail 17 19)))
740 (make-date 0 second minute hour date month
741 (let* ((now (date-year (current-date)))
742 (then (+ now year (- (modulo now 100)))))
743 (cond ((< (+ then 50) now) (+ then 100))
744 ((< (+ now 50) then) (- then 100))
745 (else then)))
746 0))))
747
748 ;; ANSI C's asctime() format
749 ;; Sun Nov 6 08:49:37 1994
750 ;; 012345678901234567890123
751 ;; 0 1 2
752 (define (parse-asctime-date str)
753 (if (not (string-match? str "aaa aaa .d dd:dd:dd dddd"))
754 (bad-header 'date str))
755 (let ((date (parse-non-negative-integer
756 str
757 (if (eqv? (string-ref str 8) #\space) 9 8)
758 10))
759 (month (parse-month str 4 7))
760 (year (parse-non-negative-integer str 20 24))
761 (hour (parse-non-negative-integer str 11 13))
762 (minute (parse-non-negative-integer str 14 16))
763 (second (parse-non-negative-integer str 17 19)))
764 (make-date 0 second minute hour date month year 0)))
765
766 (define (parse-date str)
767 (if (string-suffix? " GMT" str)
768 (let ((comma (string-index str #\,)))
769 (cond ((not comma) (bad-header 'date str))
770 ((= comma 3) (parse-rfc-822-date str))
771 (else (parse-rfc-850-date str comma))))
772 (parse-asctime-date str)))
773
774 (define (write-date date port)
775 (define (display-digits n digits port)
776 (define zero (char->integer #\0))
777 (let lp ((tens (expt 10 (1- digits))))
778 (if (> tens 0)
779 (begin
780 (display (integer->char (+ zero (modulo (truncate/ n tens) 10)))
781 port)
782 (lp (floor/ tens 10))))))
783 (let ((date (if (zero? (date-zone-offset date))
784 date
785 (time-tai->date (date->time-tai date) 0))))
786 (display (case (date-week-day date)
787 ((0) "Sun, ") ((1) "Mon, ") ((2) "Tue, ")
788 ((3) "Wed, ") ((4) "Thu, ") ((5) "Fri, ")
789 ((6) "Sat, ") (else (error "bad date" date)))
790 port)
791 (display-digits (date-day date) 2 port)
792 (display (case (date-month date)
793 ((1) " Jan ") ((2) " Feb ") ((3) " Ma ")
794 ((4) " Apr ") ((5) " May ") ((6) " Jun ")
795 ((7) " Jul ") ((8) " Aug ") ((9) " Sep ")
796 ((10) " Oct ") ((11) " Nov ") ((12) " Dec ")
797 (else (error "bad date" date)))
798 port)
799 (display-digits (date-year date) 4 port)
800 (display #\space port)
801 (display-digits (date-hour date) 2 port)
802 (display #\: port)
803 (display-digits (date-minute date) 2 port)
804 (display #\: port)
805 (display-digits (date-second date) 2 port)
806 (display " GMT" port)))
807
808 (define (write-uri uri port)
809 (display (uri->string uri) port))
810
811 (define (parse-entity-tag val)
812 (if (string-prefix? "W/" val)
813 (cons (parse-qstring val 2) #f)
814 (cons (parse-qstring val) #t)))
815
816 (define (entity-tag? val)
817 (and (pair? val)
818 (string? (car val))))
819
820 (define (write-entity-tag val port)
821 (if (not (cdr val))
822 (display "W/" port))
823 (write-qstring (car val) port))
824
825 (define* (parse-entity-tag-list val #:optional
826 (start 0) (end (string-length val)))
827 (let ((strong? (not (string-prefix? "W/" val 0 2 start end))))
828 (call-with-values (lambda ()
829 (parse-qstring val (if strong? start (+ start 2))
830 end #:incremental? #t))
831 (lambda (tag next)
832 (acons tag strong?
833 (let ((next (skip-whitespace val next end)))
834 (if (< next end)
835 (if (eqv? (string-ref val next) #\,)
836 (parse-entity-tag-list
837 val
838 (skip-whitespace val (1+ next) end)
839 end)
840 (bad-header-component 'entity-tag-list val))
841 '())))))))
842
843 (define (entity-tag-list? val)
844 (list-of? val entity-tag?))
845
846 (define (write-entity-tag-list val port)
847 (write-list val port write-entity-tag ", "))
848
849 ;; credentials = auth-scheme #auth-param
850 ;; auth-scheme = token
851 ;; auth-param = token "=" ( token | quoted-string )
852 ;;
853 ;; That's what the spec says. In reality the Basic scheme doesn't have
854 ;; k-v pairs, just one auth token, so we give that token as a string.
855 ;;
856 (define* (parse-credentials str #:optional (val-parser default-val-parser)
857 (start 0) (end (string-length str)))
858 (let* ((start (skip-whitespace str start end))
859 (delim (or (string-index str char-whitespace? start end) end)))
860 (if (= start end)
861 (bad-header-component 'authorization str))
862 (let ((scheme (string->symbol
863 (string-downcase (substring str start (or delim end))))))
864 (case scheme
865 ((basic)
866 (let* ((start (skip-whitespace str delim end)))
867 (if (< start end)
868 (cons scheme (substring str start end))
869 (bad-header-component 'credentials str))))
870 (else
871 (cons scheme (parse-key-value-list str default-val-parser delim end)))))))
872
873 (define (validate-credentials val)
874 (and (pair? val) (symbol? (car val))
875 (case (car val)
876 ((basic) (string? (cdr val)))
877 (else (key-value-list? (cdr val))))))
878
879 (define (write-credentials val port)
880 (display (car val) port)
881 (if (pair? (cdr val))
882 (begin
883 (display #\space port)
884 (write-key-value-list (cdr val) port))))
885
886 ;; challenges = 1#challenge
887 ;; challenge = auth-scheme 1*SP 1#auth-param
888 ;;
889 ;; A pain to parse, as both challenges and auth params are delimited by
890 ;; commas, and qstrings can contain anything. We rely on auth params
891 ;; necessarily having "=" in them.
892 ;;
893 (define* (parse-challenge str #:optional
894 (start 0) (end (string-length str)))
895 (let* ((start (skip-whitespace str start end))
896 (sp (string-index str #\space start end))
897 (scheme (if sp
898 (string->symbol (string-downcase (substring str start sp)))
899 (bad-header-component 'challenge str))))
900 (let lp ((i sp) (out (list scheme)))
901 (if (not (< i end))
902 (values (reverse! out) end)
903 (let* ((i (skip-whitespace str i end))
904 (eq (string-index str #\= i end))
905 (comma (string-index str #\, i end))
906 (delim (min (or eq end) (or comma end)))
907 (token-end (trim-whitespace str i delim)))
908 (if (string-index str #\space i token-end)
909 (values (reverse! out) i)
910 (let ((k (string->symbol (substring str i token-end))))
911 (call-with-values
912 (lambda ()
913 (if (and eq (or (not comma) (< eq comma)))
914 (let ((i (skip-whitespace str (1+ eq) end)))
915 (if (and (< i end) (eqv? (string-ref str i) #\"))
916 (parse-qstring str i end #:incremental? #t)
917 (values (substring
918 str i
919 (trim-whitespace str i
920 (or comma end)))
921 (or comma end))))
922 (values #f delim)))
923 (lambda (v next-i)
924 (let ((i (skip-whitespace str next-i end)))
925 (if (or (= i end) (eqv? (string-ref str i) #\,))
926 (lp (1+ i) (cons (if v (cons k v) k) out))
927 (bad-header-component
928 'challenge
929 (substring str start end)))))))))))))
930
931 (define* (parse-challenges str #:optional (val-parser default-val-parser)
932 (start 0) (end (string-length str)))
933 (let lp ((i start) (ret '()))
934 (let ((i (skip-whitespace str i end)))
935 (if (< i end)
936 (call-with-values (lambda () (parse-challenge str i end))
937 (lambda (challenge i)
938 (lp i (cons challenge ret))))
939 (reverse ret)))))
940
941 (define (validate-challenges val)
942 (list-of? val (lambda (x)
943 (and (pair? x) (symbol? (car x))
944 (key-value-list? (cdr x))))))
945
946 (define (write-challenge val port)
947 (display (car val) port)
948 (display #\space port)
949 (write-key-value-list (cdr val) port))
950
951 (define (write-challenges val port)
952 (write-list val port write-challenge ", "))
953
954
955 \f
956
957 ;;;
958 ;;; Request-Line and Response-Line
959 ;;;
960
961 ;; Hmm.
962 (define (bad-request message . args)
963 (throw 'bad-request message args))
964 (define (bad-response message . args)
965 (throw 'bad-response message args))
966
967 (define *known-versions* '())
968
969 (define* (parse-http-version str #:optional (start 0) (end (string-length str)))
970 "Parse an HTTP version from @var{str}, returning it as a major-minor
971 pair. For example, @code{HTTP/1.1} parses as the pair of integers,
972 @code{(1 . 1)}."
973 (or (let lp ((known *known-versions*))
974 (and (pair? known)
975 (if (string= str (caar known) start end)
976 (cdar known)
977 (lp (cdr known)))))
978 (let ((dot-idx (string-index str #\. start end)))
979 (if (and (string-prefix? "HTTP/" str 0 5 start end)
980 dot-idx
981 (= dot-idx (string-rindex str #\. start end)))
982 (cons (parse-non-negative-integer str (+ start 5) dot-idx)
983 (parse-non-negative-integer str (1+ dot-idx) end))
984 (bad-header-component 'http-version (substring str start end))))))
985
986 (define (write-http-version val port)
987 "Write the given major-minor version pair to @var{port}."
988 (display "HTTP/" port)
989 (display (car val) port)
990 (display #\. port)
991 (display (cdr val) port))
992
993 (for-each
994 (lambda (v)
995 (set! *known-versions*
996 (acons v (parse-http-version v 0 (string-length v))
997 *known-versions*)))
998 '("HTTP/1.0" "HTTP/1.1"))
999
1000
1001 ;; Request-URI = "*" | absoluteURI | abs_path | authority
1002 ;;
1003 ;; The `authority' form is only permissible for the CONNECT method, so
1004 ;; because we don't expect people to implement CONNECT, we save
1005 ;; ourselves the trouble of that case, and disallow the CONNECT method.
1006 ;;
1007 (define* (parse-http-method str #:optional (start 0) (end (string-length str)))
1008 "Parse an HTTP method from @var{str}. The result is an upper-case
1009 symbol, like @code{GET}."
1010 (cond
1011 ((string= str "GET" start end) 'GET)
1012 ((string= str "HEAD" start end) 'HEAD)
1013 ((string= str "POST" start end) 'POST)
1014 ((string= str "PUT" start end) 'PUT)
1015 ((string= str "DELETE" start end) 'DELETE)
1016 ((string= str "OPTIONS" start end) 'OPTIONS)
1017 ((string= str "TRACE" start end) 'TRACE)
1018 (else (bad-request "Invalid method: ~a" (substring str start end)))))
1019
1020 (define* (parse-request-uri str #:optional (start 0) (end (string-length str)))
1021 "Parse a URI from an HTTP request line. Note that URIs in requests do
1022 not have to have a scheme or host name. The result is a URI object."
1023 (cond
1024 ((= start end)
1025 (bad-request "Missing Request-URI"))
1026 ((string= str "*" start end)
1027 #f)
1028 ((eq? (string-ref str start) #\/)
1029 (let* ((q (string-index str #\? start end))
1030 (f (string-index str #\# start end))
1031 (q (and q (or (not f) (< q f)) q)))
1032 (build-uri 'http
1033 #:path (substring str start (or q f end))
1034 #:query (and q (substring str (1+ q) (or f end)))
1035 #:fragment (and f (substring str (1+ f) end)))))
1036 (else
1037 (or (string->uri (substring str start end))
1038 (bad-request "Invalid URI: ~a" (substring str start end))))))
1039
1040 (define (read-request-line port)
1041 "Read the first line of an HTTP request from @var{port}, returning
1042 three values: the method, the URI, and the version."
1043 (let* ((line (read-line* port))
1044 (d0 (string-index line char-whitespace?)) ; "delimiter zero"
1045 (d1 (string-rindex line char-whitespace?)))
1046 (if (and d0 d1 (< d0 d1))
1047 (values (parse-http-method line 0 d0)
1048 (parse-request-uri line (skip-whitespace line (1+ d0) d1) d1)
1049 (parse-http-version line (1+ d1) (string-length line)))
1050 (bad-request "Bad Request-Line: ~s" line))))
1051
1052 (define (write-uri uri port)
1053 (if (uri-host uri)
1054 (begin
1055 (display (uri-scheme uri) port)
1056 (display "://" port)
1057 (if (uri-userinfo uri)
1058 (begin
1059 (display (uri-userinfo uri) port)
1060 (display #\@ port)))
1061 (display (uri-host uri) port)
1062 (let ((p (uri-port uri)))
1063 (if (and p (not (eqv? p 80)))
1064 (begin
1065 (display #\: port)
1066 (display p port))))))
1067 (let* ((path (uri-path uri))
1068 (len (string-length path)))
1069 (cond
1070 ((and (> len 0) (not (eqv? (string-ref path 0) #\/)))
1071 (bad-request "Non-absolute URI path: ~s" path))
1072 ((and (zero? len) (not (uri-host uri)))
1073 (bad-request "Empty path and no host for URI: ~s" uri))
1074 (else
1075 (display path port))))
1076 (if (uri-query uri)
1077 (begin
1078 (display #\? port)
1079 (display (uri-query uri) port))))
1080
1081 (define (write-request-line method uri version port)
1082 "Write the first line of an HTTP request to @var{port}."
1083 (display method port)
1084 (display #\space port)
1085 (write-uri uri port)
1086 (display #\space port)
1087 (write-http-version version port)
1088 (display "\r\n" port))
1089
1090 (define (read-response-line port)
1091 "Read the first line of an HTTP response from @var{port}, returning
1092 three values: the HTTP version, the response code, and the \"reason
1093 phrase\"."
1094 (let* ((line (read-line* port))
1095 (d0 (string-index line char-whitespace?)) ; "delimiter zero"
1096 (d1 (and d0 (string-index line char-whitespace?
1097 (skip-whitespace line d0)))))
1098 (if (and d0 d1)
1099 (values (parse-http-version line 0 d0)
1100 (parse-non-negative-integer line (skip-whitespace line d0 d1)
1101 d1)
1102 (string-trim-both line char-whitespace? d1))
1103 (bad-response "Bad Response-Line: ~s" line))))
1104
1105 (define (write-response-line version code reason-phrase port)
1106 "Write the first line of an HTTP response to @var{port}."
1107 (write-http-version version port)
1108 (display #\space port)
1109 (display code port)
1110 (display #\space port)
1111 (display reason-phrase port)
1112 (display "\r\n" port))
1113
1114
1115 \f
1116
1117 ;;;
1118 ;;; Helpers for declaring headers
1119 ;;;
1120
1121 ;; emacs: (put 'declare-header! 'scheme-indent-function 1)
1122 ;; emacs: (put 'declare-opaque!-header 'scheme-indent-function 1)
1123 (define (declare-opaque-header! name)
1124 (declare-header! name
1125 parse-opaque-string validate-opaque-string write-opaque-string))
1126
1127 ;; emacs: (put 'declare-date-header! 'scheme-indent-function 1)
1128 (define (declare-date-header! name)
1129 (declare-header! name
1130 parse-date date? write-date))
1131
1132 ;; emacs: (put 'declare-string-list-header! 'scheme-indent-function 1)
1133 (define (declare-string-list-header! name)
1134 (declare-header! name
1135 split-and-trim list-of-strings? write-list-of-strings))
1136
1137 ;; emacs: (put 'declare-symbol-list-header! 'scheme-indent-function 1)
1138 (define (declare-symbol-list-header! name)
1139 (declare-header! name
1140 (lambda (str)
1141 (map string->symbol (split-and-trim str)))
1142 (lambda (v)
1143 (list-of? v symbol?))
1144 (lambda (v port)
1145 (write-list v port display ", "))))
1146
1147 ;; emacs: (put 'declare-header-list-header! 'scheme-indent-function 1)
1148 (define (declare-header-list-header! name)
1149 (declare-header! name
1150 split-header-names list-of-header-names? write-header-list))
1151
1152 ;; emacs: (put 'declare-integer-header! 'scheme-indent-function 1)
1153 (define (declare-integer-header! name)
1154 (declare-header! name
1155 parse-non-negative-integer non-negative-integer? display))
1156
1157 ;; emacs: (put 'declare-uri-header! 'scheme-indent-function 1)
1158 (define (declare-uri-header! name)
1159 (declare-header! name
1160 (lambda (str) (or (string->uri str) (bad-header-component 'uri str)))
1161 uri?
1162 write-uri))
1163
1164 ;; emacs: (put 'declare-quality-list-header! 'scheme-indent-function 1)
1165 (define (declare-quality-list-header! name)
1166 (declare-header! name
1167 parse-quality-list validate-quality-list write-quality-list))
1168
1169 ;; emacs: (put 'declare-param-list-header! 'scheme-indent-function 1)
1170 (define* (declare-param-list-header! name #:optional
1171 (val-parser default-val-parser)
1172 (val-validator default-val-validator)
1173 (val-writer default-val-writer))
1174 (declare-header! name
1175 (lambda (str) (parse-param-list str val-parser))
1176 (lambda (val) (validate-param-list val val-validator))
1177 (lambda (val port) (write-param-list val port val-writer))))
1178
1179 ;; emacs: (put 'declare-key-value-list-header! 'scheme-indent-function 1)
1180 (define* (declare-key-value-list-header! name #:optional
1181 (val-parser default-val-parser)
1182 (val-validator default-val-validator)
1183 (val-writer default-val-writer))
1184 (declare-header! name
1185 (lambda (str) (parse-key-value-list str val-parser))
1186 (lambda (val) (key-value-list? val val-validator))
1187 (lambda (val port) (write-key-value-list val port val-writer))))
1188
1189 ;; emacs: (put 'declare-entity-tag-list-header! 'scheme-indent-function 1)
1190 (define (declare-entity-tag-list-header! name)
1191 (declare-header! name
1192 (lambda (str) (if (string=? str "*") '* (parse-entity-tag-list str)))
1193 (lambda (val) (or (eq? val '*) (entity-tag-list? val)))
1194 (lambda (val port)
1195 (if (eq? val '*)
1196 (display "*" port)
1197 (write-entity-tag-list val port)))))
1198
1199 ;; emacs: (put 'declare-credentials-header! 'scheme-indent-function 1)
1200 (define (declare-credentials-header! name)
1201 (declare-header! name
1202 parse-credentials validate-credentials write-credentials))
1203
1204 ;; emacs: (put 'declare-challenge-list-header! 'scheme-indent-function 1)
1205 (define (declare-challenge-list-header! name)
1206 (declare-header! name
1207 parse-challenges validate-challenges write-challenges))
1208
1209
1210 \f
1211
1212 ;;;
1213 ;;; General headers
1214 ;;;
1215
1216 ;; Cache-Control = 1#(cache-directive)
1217 ;; cache-directive = cache-request-directive | cache-response-directive
1218 ;; cache-request-directive =
1219 ;; "no-cache" ; Section 14.9.1
1220 ;; | "no-store" ; Section 14.9.2
1221 ;; | "max-age" "=" delta-seconds ; Section 14.9.3, 14.9.4
1222 ;; | "max-stale" [ "=" delta-seconds ] ; Section 14.9.3
1223 ;; | "min-fresh" "=" delta-seconds ; Section 14.9.3
1224 ;; | "no-transform" ; Section 14.9.5
1225 ;; | "only-if-cached" ; Section 14.9.4
1226 ;; | cache-extension ; Section 14.9.6
1227 ;; cache-response-directive =
1228 ;; "public" ; Section 14.9.1
1229 ;; | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
1230 ;; | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
1231 ;; | "no-store" ; Section 14.9.2
1232 ;; | "no-transform" ; Section 14.9.5
1233 ;; | "must-revalidate" ; Section 14.9.4
1234 ;; | "proxy-revalidate" ; Section 14.9.4
1235 ;; | "max-age" "=" delta-seconds ; Section 14.9.3
1236 ;; | "s-maxage" "=" delta-seconds ; Section 14.9.3
1237 ;; | cache-extension ; Section 14.9.6
1238 ;; cache-extension = token [ "=" ( token | quoted-string ) ]
1239 ;;
1240 (declare-key-value-list-header! "Cache-Control"
1241 (lambda (k v-str)
1242 (case k
1243 ((max-age max-stale min-fresh s-maxage)
1244 (parse-non-negative-integer v-str))
1245 ((private no-cache)
1246 (and v-str (split-header-names v-str)))
1247 (else v-str)))
1248 (lambda (k v)
1249 (case k
1250 ((max-age max-stale min-fresh s-maxage)
1251 (non-negative-integer? v))
1252 ((private no-cache)
1253 (or (not v) (list-of-header-names? v)))
1254 (else
1255 (not v))))
1256 (lambda (k v port)
1257 (cond
1258 ((string? v) (display v port))
1259 ((pair? v)
1260 (display #\" port)
1261 (write-header-list v port)
1262 (display #\" port))
1263 ((integer? v)
1264 (display v port))
1265 (else
1266 (bad-header-component 'cache-control v)))))
1267
1268 ;; Connection = "Connection" ":" 1#(connection-token)
1269 ;; connection-token = token
1270 ;; e.g.
1271 ;; Connection: close, foo-header
1272 ;;
1273 (declare-header-list-header! "Connection")
1274
1275 ;; Date = "Date" ":" HTTP-date
1276 ;; e.g.
1277 ;; Date: Tue, 15 Nov 1994 08:12:31 GMT
1278 ;;
1279 (declare-date-header! "Date")
1280
1281 ;; Pragma = "Pragma" ":" 1#pragma-directive
1282 ;; pragma-directive = "no-cache" | extension-pragma
1283 ;; extension-pragma = token [ "=" ( token | quoted-string ) ]
1284 ;;
1285 (declare-key-value-list-header! "Pragma")
1286
1287 ;; Trailer = "Trailer" ":" 1#field-name
1288 ;;
1289 (declare-header-list-header! "Trailer")
1290
1291 ;; Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
1292 ;;
1293 (declare-param-list-header! "Transfer-Encoding")
1294
1295 ;; Upgrade = "Upgrade" ":" 1#product
1296 ;;
1297 (declare-string-list-header! "Upgrade")
1298
1299 ;; Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
1300 ;; received-protocol = [ protocol-name "/" ] protocol-version
1301 ;; protocol-name = token
1302 ;; protocol-version = token
1303 ;; received-by = ( host [ ":" port ] ) | pseudonym
1304 ;; pseudonym = token
1305 ;;
1306 (declare-header! "Via"
1307 split-and-trim
1308 list-of-strings?
1309 write-list-of-strings
1310 #:multiple? #t)
1311
1312 ;; Warning = "Warning" ":" 1#warning-value
1313 ;;
1314 ;; warning-value = warn-code SP warn-agent SP warn-text
1315 ;; [SP warn-date]
1316 ;;
1317 ;; warn-code = 3DIGIT
1318 ;; warn-agent = ( host [ ":" port ] ) | pseudonym
1319 ;; ; the name or pseudonym of the server adding
1320 ;; ; the Warning header, for use in debugging
1321 ;; warn-text = quoted-string
1322 ;; warn-date = <"> HTTP-date <">
1323 (declare-header! "Warning"
1324 (lambda (str)
1325 (let ((len (string-length str)))
1326 (let lp ((i (skip-whitespace str 0)))
1327 (let* ((idx1 (string-index str #\space i))
1328 (idx2 (string-index str #\space (1+ idx1))))
1329 (if (and idx1 idx2)
1330 (let ((code (parse-non-negative-integer str i idx1))
1331 (agent (substring str (1+ idx1) idx2)))
1332 (call-with-values
1333 (lambda () (parse-qstring str (1+ idx2) #:incremental? #t))
1334 (lambda (text i)
1335 (call-with-values
1336 (lambda ()
1337 (let ((c (and (< i len) (string-ref str i))))
1338 (case c
1339 ((#\space)
1340 ;; we have a date.
1341 (call-with-values
1342 (lambda () (parse-qstring str (1+ i)
1343 #:incremental? #t))
1344 (lambda (date i)
1345 (values text (parse-date date) i))))
1346 (else
1347 (values text #f i)))))
1348 (lambda (text date i)
1349 (let ((w (list code agent text date))
1350 (c (and (< i len) (string-ref str i))))
1351 (case c
1352 ((#f) (list w))
1353 ((#\,) (cons w (lp (skip-whitespace str (1+ i)))))
1354 (else (bad-header 'warning str))))))))))))))
1355 (lambda (val)
1356 (list-of? val
1357 (lambda (elt)
1358 (and (list? elt)
1359 (= (length elt) 4)
1360 (apply (lambda (code host text date)
1361 (and (non-negative-integer? code) (< code 1000)
1362 (string? host)
1363 (string? text)
1364 (or (not date) (date? date))))
1365 elt)))))
1366 (lambda (val port)
1367 (write-list
1368 val port
1369 (lambda (w port)
1370 (apply
1371 (lambda (code host text date)
1372 (display code port)
1373 (display #\space port)
1374 (display host port)
1375 (display #\space port)
1376 (write-qstring text port)
1377 (if date
1378 (begin
1379 (display #\space port)
1380 (write-date date port))))
1381 w))
1382 ", "))
1383 #:multiple? #t)
1384
1385
1386 \f
1387
1388 ;;;
1389 ;;; Entity headers
1390 ;;;
1391
1392 ;; Allow = #Method
1393 ;;
1394 (declare-symbol-list-header! "Allow")
1395
1396 ;; Content-Encoding = 1#content-coding
1397 ;;
1398 (declare-symbol-list-header! "Content-Encoding")
1399
1400 ;; Content-Language = 1#language-tag
1401 ;;
1402 (declare-string-list-header! "Content-Language")
1403
1404 ;; Content-Length = 1*DIGIT
1405 ;;
1406 (declare-integer-header! "Content-Length")
1407
1408 ;; Content-Location = ( absoluteURI | relativeURI )
1409 ;;
1410 (declare-uri-header! "Content-Location")
1411
1412 ;; Content-MD5 = <base64 of 128 bit MD5 digest as per RFC 1864>
1413 ;;
1414 (declare-opaque-header! "Content-MD5")
1415
1416 ;; Content-Range = content-range-spec
1417 ;; content-range-spec = byte-content-range-spec
1418 ;; byte-content-range-spec = bytes-unit SP
1419 ;; byte-range-resp-spec "/"
1420 ;; ( instance-length | "*" )
1421 ;; byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
1422 ;; | "*"
1423 ;; instance-length = 1*DIGIT
1424 ;;
1425 (declare-header! "Content-Range"
1426 (lambda (str)
1427 (let ((dash (string-index str #\-))
1428 (slash (string-index str #\/)))
1429 (if (and (string-prefix? "bytes " str) slash)
1430 (list 'bytes
1431 (cond
1432 (dash
1433 (cons
1434 (parse-non-negative-integer str 6 dash)
1435 (parse-non-negative-integer str (1+ dash) slash)))
1436 ((string= str "*" 6 slash)
1437 '*)
1438 (else
1439 (bad-header 'content-range str)))
1440 (if (string= str "*" (1+ slash))
1441 '*
1442 (parse-non-negative-integer str (1+ slash))))
1443 (bad-header 'content-range str))))
1444 (lambda (val)
1445 (and (list? val) (= (length val) 3)
1446 (symbol? (car val))
1447 (let ((x (cadr val)))
1448 (or (eq? x '*)
1449 (and (pair? x)
1450 (non-negative-integer? (car x))
1451 (non-negative-integer? (cdr x)))))
1452 (let ((x (caddr val)))
1453 (or (eq? x '*)
1454 (non-negative-integer? x)))))
1455 (lambda (val port)
1456 (display (car val) port)
1457 (display #\space port)
1458 (if (eq? (cadr val) '*)
1459 (display #\* port)
1460 (begin
1461 (display (caadr val) port)
1462 (display #\- port)
1463 (display (caadr val) port)))
1464 (if (eq? (caddr val) '*)
1465 (display #\* port)
1466 (display (caddr val) port))))
1467
1468 ;; Content-Type = media-type
1469 ;;
1470 (declare-header! "Content-Type"
1471 (lambda (str)
1472 (let ((parts (string-split str #\;)))
1473 (cons (parse-media-type (car parts))
1474 (map (lambda (x)
1475 (let ((eq (string-index x #\=)))
1476 (if (and eq (= eq (string-rindex x #\=)))
1477 (cons (string->symbol
1478 (string-trim x char-whitespace? 0 eq))
1479 (string-trim-right x char-whitespace? (1+ eq)))
1480 (bad-header 'content-type str))))
1481 (cdr parts)))))
1482 (lambda (val)
1483 (and (pair? val)
1484 (symbol? (car val))
1485 (list-of? (cdr val)
1486 (lambda (x)
1487 (and (pair? x) (symbol? (car x)) (string? (cdr x)))))))
1488 (lambda (val port)
1489 (display (car val) port)
1490 (if (pair? (cdr val))
1491 (begin
1492 (display ";" port)
1493 (write-list
1494 (cdr val) port
1495 (lambda (pair port)
1496 (display (car pair) port)
1497 (display #\= port)
1498 (display (cdr pair) port))
1499 ";")))))
1500
1501 ;; Expires = HTTP-date
1502 ;;
1503 (declare-date-header! "Expires")
1504
1505 ;; Last-Modified = HTTP-date
1506 ;;
1507 (declare-date-header! "Last-Modified")
1508
1509
1510 \f
1511
1512 ;;;
1513 ;;; Request headers
1514 ;;;
1515
1516 ;; Accept = #( media-range [ accept-params ] )
1517 ;; media-range = ( "*/*" | ( type "/" "*" ) | ( type "/" subtype ) )
1518 ;; *( ";" parameter )
1519 ;; accept-params = ";" "q" "=" qvalue *( accept-extension )
1520 ;; accept-extension = ";" token [ "=" ( token | quoted-string ) ]
1521 ;;
1522 (declare-param-list-header! "Accept"
1523 ;; -> (type/subtype (sym-prop . str-val) ...) ...)
1524 ;;
1525 ;; with the exception of prop `q', in which case the val will be a
1526 ;; valid quality value
1527 ;;
1528 (lambda (k v)
1529 (if (eq? k 'q)
1530 (parse-quality v)
1531 v))
1532 (lambda (k v)
1533 (if (eq? k 'q)
1534 (valid-quality? v)
1535 (or (not v) (string? v))))
1536 (lambda (k v port)
1537 (if (eq? k 'q)
1538 (write-quality v port)
1539 (default-val-writer k v port))))
1540
1541 ;; Accept-Charset = 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] )
1542 ;;
1543 (declare-quality-list-header! "Accept-Charset")
1544
1545 ;; Accept-Encoding = 1#( codings [ ";" "q" "=" qvalue ] )
1546 ;; codings = ( content-coding | "*" )
1547 ;;
1548 (declare-quality-list-header! "Accept-Encoding")
1549
1550 ;; Accept-Language = 1#( language-range [ ";" "q" "=" qvalue ] )
1551 ;; language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
1552 ;;
1553 (declare-quality-list-header! "Accept-Language")
1554
1555 ;; Authorization = credentials
1556 ;; credentials = auth-scheme #auth-param
1557 ;; auth-scheme = token
1558 ;; auth-param = token "=" ( token | quoted-string )
1559 ;;
1560 (declare-credentials-header! "Authorization")
1561
1562 ;; Expect = 1#expectation
1563 ;; expectation = "100-continue" | expectation-extension
1564 ;; expectation-extension = token [ "=" ( token | quoted-string )
1565 ;; *expect-params ]
1566 ;; expect-params = ";" token [ "=" ( token | quoted-string ) ]
1567 ;;
1568 (declare-param-list-header! "Expect")
1569
1570 ;; From = mailbox
1571 ;;
1572 ;; Should be an email address; we just pass on the string as-is.
1573 ;;
1574 (declare-opaque-header! "From")
1575
1576 ;; Host = host [ ":" port ]
1577 ;;
1578 (declare-header! "Host"
1579 (lambda (str)
1580 (let ((colon (string-index str #\:)))
1581 (if colon
1582 (cons (substring str 0 colon)
1583 (parse-non-negative-integer str (1+ colon)))
1584 (cons str #f))))
1585 (lambda (val)
1586 (and (pair? val)
1587 (string? (car val))
1588 (or (not (cdr val))
1589 (non-negative-integer? (cdr val)))))
1590 (lambda (val port)
1591 (display (car val) port)
1592 (if (cdr val)
1593 (begin
1594 (display #\: port)
1595 (display (cdr val) port)))))
1596
1597 ;; If-Match = ( "*" | 1#entity-tag )
1598 ;;
1599 (declare-entity-tag-list-header! "If-Match")
1600
1601 ;; If-Modified-Since = HTTP-date
1602 ;;
1603 (declare-date-header! "If-Modified-Since")
1604
1605 ;; If-None-Match = ( "*" | 1#entity-tag )
1606 ;;
1607 (declare-entity-tag-list-header! "If-None-Match")
1608
1609 ;; If-Range = ( entity-tag | HTTP-date )
1610 ;;
1611 (declare-header! "If-Range"
1612 (lambda (str)
1613 (if (or (string-prefix? "\"" str)
1614 (string-prefix? "W/" str))
1615 (parse-entity-tag str)
1616 (parse-date str)))
1617 (lambda (val)
1618 (or (date? val) (entity-tag? val)))
1619 (lambda (val port)
1620 (if (date? val)
1621 (write-date val port)
1622 (write-entity-tag val port))))
1623
1624 ;; If-Unmodified-Since = HTTP-date
1625 ;;
1626 (declare-date-header! "If-Unmodified-Since")
1627
1628 ;; Max-Forwards = 1*DIGIT
1629 ;;
1630 (declare-integer-header! "Max-Forwards")
1631
1632 ;; Proxy-Authorization = credentials
1633 ;;
1634 (declare-credentials-header! "Proxy-Authorization")
1635
1636 ;; Range = "Range" ":" ranges-specifier
1637 ;; ranges-specifier = byte-ranges-specifier
1638 ;; byte-ranges-specifier = bytes-unit "=" byte-range-set
1639 ;; byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
1640 ;; byte-range-spec = first-byte-pos "-" [last-byte-pos]
1641 ;; first-byte-pos = 1*DIGIT
1642 ;; last-byte-pos = 1*DIGIT
1643 ;; suffix-byte-range-spec = "-" suffix-length
1644 ;; suffix-length = 1*DIGIT
1645 ;;
1646 (declare-header! "Range"
1647 (lambda (str)
1648 (if (string-prefix? "bytes=" str)
1649 (cons
1650 'bytes
1651 (map (lambda (x)
1652 (let ((dash (string-index x #\-)))
1653 (cond
1654 ((not dash)
1655 (bad-header 'range str))
1656 ((zero? dash)
1657 (cons #f (parse-non-negative-integer x 1)))
1658 ((= dash (1- (string-length x)))
1659 (cons (parse-non-negative-integer x 0 dash) #f))
1660 (else
1661 (cons (parse-non-negative-integer x 0 dash)
1662 (parse-non-negative-integer x (1+ dash)))))))
1663 (string-split (substring str 6) #\,)))
1664 (bad-header 'range str)))
1665 (lambda (val)
1666 (and (pair? val)
1667 (symbol? (car val))
1668 (list-of? (cdr val)
1669 (lambda (elt)
1670 (and (pair? elt)
1671 (let ((x (car elt)) (y (cdr elt)))
1672 (and (or x y)
1673 (or (not x) (non-negative-integer? x))
1674 (or (not y) (non-negative-integer? y)))))))))
1675 (lambda (val port)
1676 (display (car val) port)
1677 (display #\= port)
1678 (write-list
1679 (cdr val) port
1680 (lambda (pair port)
1681 (if (car pair)
1682 (display (car pair) port))
1683 (display #\- port)
1684 (if (cdr pair)
1685 (display (cdr pair) port)))
1686 ",")))
1687
1688 ;; Referer = ( absoluteURI | relativeURI )
1689 ;;
1690 (declare-uri-header! "Referer")
1691
1692 ;; TE = #( t-codings )
1693 ;; t-codings = "trailers" | ( transfer-extension [ accept-params ] )
1694 ;;
1695 (declare-param-list-header! "TE")
1696
1697 ;; User-Agent = 1*( product | comment )
1698 ;;
1699 (declare-opaque-header! "User-Agent")
1700
1701
1702 \f
1703
1704 ;;;
1705 ;;; Reponse headers
1706 ;;;
1707
1708 ;; Accept-Ranges = acceptable-ranges
1709 ;; acceptable-ranges = 1#range-unit | "none"
1710 ;;
1711 (declare-symbol-list-header! "Accept-Ranges")
1712
1713 ;; Age = age-value
1714 ;; age-value = delta-seconds
1715 ;;
1716 (declare-integer-header! "Age")
1717
1718 ;; ETag = entity-tag
1719 ;;
1720 (declare-header! "ETag"
1721 parse-entity-tag
1722 entity-tag?
1723 write-entity-tag)
1724
1725 ;; Location = absoluteURI
1726 ;;
1727 (declare-uri-header! "Location")
1728
1729 ;; Proxy-Authenticate = 1#challenge
1730 ;;
1731 (declare-challenge-list-header! "Proxy-Authenticate")
1732
1733 ;; Retry-After = ( HTTP-date | delta-seconds )
1734 ;;
1735 (declare-header! "Retry-After"
1736 (lambda (str)
1737 (if (and (not (string-null? str))
1738 (char-numeric? (string-ref str 0)))
1739 (parse-non-negative-integer str)
1740 (parse-date str)))
1741 (lambda (val)
1742 (or (date? val) (non-negative-integer? val)))
1743 (lambda (val port)
1744 (if (date? val)
1745 (write-date val port)
1746 (display val port))))
1747
1748 ;; Server = 1*( product | comment )
1749 ;;
1750 (declare-opaque-header! "Server")
1751
1752 ;; Vary = ( "*" | 1#field-name )
1753 ;;
1754 (declare-header! "Vary"
1755 (lambda (str)
1756 (if (equal? str "*")
1757 '*
1758 (split-header-names str)))
1759 (lambda (val)
1760 (or (eq? val '*) (list-of-header-names? val)))
1761 (lambda (val port)
1762 (if (eq? val '*)
1763 (display "*" port)
1764 (write-header-list val port))))
1765
1766 ;; WWW-Authenticate = 1#challenge
1767 ;;
1768 (declare-challenge-list-header! "WWW-Authenticate")