879923f817ce7b8e4e87e6f2fd23d74350ffcae8
[bpt/guile.git] / module / web / http.scm
1 ;;; HTTP messages
2
3 ;; Copyright (C) 2010, 2011, 2012 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 @code{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) " Mar ")
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 (parse-entity-tag val)
809 (if (string-prefix? "W/" val)
810 (cons (parse-qstring val 2) #f)
811 (cons (parse-qstring val) #t)))
812
813 (define (entity-tag? val)
814 (and (pair? val)
815 (string? (car val))))
816
817 (define (write-entity-tag val port)
818 (if (not (cdr val))
819 (display "W/" port))
820 (write-qstring (car val) port))
821
822 (define* (parse-entity-tag-list val #:optional
823 (start 0) (end (string-length val)))
824 (let ((strong? (not (string-prefix? "W/" val 0 2 start end))))
825 (call-with-values (lambda ()
826 (parse-qstring val (if strong? start (+ start 2))
827 end #:incremental? #t))
828 (lambda (tag next)
829 (acons tag strong?
830 (let ((next (skip-whitespace val next end)))
831 (if (< next end)
832 (if (eqv? (string-ref val next) #\,)
833 (parse-entity-tag-list
834 val
835 (skip-whitespace val (1+ next) end)
836 end)
837 (bad-header-component 'entity-tag-list val))
838 '())))))))
839
840 (define (entity-tag-list? val)
841 (list-of? val entity-tag?))
842
843 (define (write-entity-tag-list val port)
844 (write-list val port write-entity-tag ", "))
845
846 ;; credentials = auth-scheme #auth-param
847 ;; auth-scheme = token
848 ;; auth-param = token "=" ( token | quoted-string )
849 ;;
850 ;; That's what the spec says. In reality the Basic scheme doesn't have
851 ;; k-v pairs, just one auth token, so we give that token as a string.
852 ;;
853 (define* (parse-credentials str #:optional (val-parser default-val-parser)
854 (start 0) (end (string-length str)))
855 (let* ((start (skip-whitespace str start end))
856 (delim (or (string-index str char-whitespace? start end) end)))
857 (if (= start end)
858 (bad-header-component 'authorization str))
859 (let ((scheme (string->symbol
860 (string-downcase (substring str start (or delim end))))))
861 (case scheme
862 ((basic)
863 (let* ((start (skip-whitespace str delim end)))
864 (if (< start end)
865 (cons scheme (substring str start end))
866 (bad-header-component 'credentials str))))
867 (else
868 (cons scheme (parse-key-value-list str default-val-parser delim end)))))))
869
870 (define (validate-credentials val)
871 (and (pair? val) (symbol? (car val))
872 (case (car val)
873 ((basic) (string? (cdr val)))
874 (else (key-value-list? (cdr val))))))
875
876 (define (write-credentials val port)
877 (display (car val) port)
878 (if (pair? (cdr val))
879 (begin
880 (display #\space port)
881 (write-key-value-list (cdr val) port))))
882
883 ;; challenges = 1#challenge
884 ;; challenge = auth-scheme 1*SP 1#auth-param
885 ;;
886 ;; A pain to parse, as both challenges and auth params are delimited by
887 ;; commas, and qstrings can contain anything. We rely on auth params
888 ;; necessarily having "=" in them.
889 ;;
890 (define* (parse-challenge str #:optional
891 (start 0) (end (string-length str)))
892 (let* ((start (skip-whitespace str start end))
893 (sp (string-index str #\space start end))
894 (scheme (if sp
895 (string->symbol (string-downcase (substring str start sp)))
896 (bad-header-component 'challenge str))))
897 (let lp ((i sp) (out (list scheme)))
898 (if (not (< i end))
899 (values (reverse! out) end)
900 (let* ((i (skip-whitespace str i end))
901 (eq (string-index str #\= i end))
902 (comma (string-index str #\, i end))
903 (delim (min (or eq end) (or comma end)))
904 (token-end (trim-whitespace str i delim)))
905 (if (string-index str #\space i token-end)
906 (values (reverse! out) i)
907 (let ((k (string->symbol (substring str i token-end))))
908 (call-with-values
909 (lambda ()
910 (if (and eq (or (not comma) (< eq comma)))
911 (let ((i (skip-whitespace str (1+ eq) end)))
912 (if (and (< i end) (eqv? (string-ref str i) #\"))
913 (parse-qstring str i end #:incremental? #t)
914 (values (substring
915 str i
916 (trim-whitespace str i
917 (or comma end)))
918 (or comma end))))
919 (values #f delim)))
920 (lambda (v next-i)
921 (let ((i (skip-whitespace str next-i end)))
922 (if (or (= i end) (eqv? (string-ref str i) #\,))
923 (lp (1+ i) (cons (if v (cons k v) k) out))
924 (bad-header-component
925 'challenge
926 (substring str start end)))))))))))))
927
928 (define* (parse-challenges str #:optional (val-parser default-val-parser)
929 (start 0) (end (string-length str)))
930 (let lp ((i start) (ret '()))
931 (let ((i (skip-whitespace str i end)))
932 (if (< i end)
933 (call-with-values (lambda () (parse-challenge str i end))
934 (lambda (challenge i)
935 (lp i (cons challenge ret))))
936 (reverse ret)))))
937
938 (define (validate-challenges val)
939 (list-of? val (lambda (x)
940 (and (pair? x) (symbol? (car x))
941 (key-value-list? (cdr x))))))
942
943 (define (write-challenge val port)
944 (display (car val) port)
945 (display #\space port)
946 (write-key-value-list (cdr val) port))
947
948 (define (write-challenges val port)
949 (write-list val port write-challenge ", "))
950
951
952 \f
953
954 ;;;
955 ;;; Request-Line and Response-Line
956 ;;;
957
958 ;; Hmm.
959 (define (bad-request message . args)
960 (throw 'bad-request message args))
961 (define (bad-response message . args)
962 (throw 'bad-response message args))
963
964 (define *known-versions* '())
965
966 (define* (parse-http-version str #:optional (start 0) (end (string-length str)))
967 "Parse an HTTP version from @var{str}, returning it as a major-minor
968 pair. For example, @code{HTTP/1.1} parses as the pair of integers,
969 @code{(1 . 1)}."
970 (or (let lp ((known *known-versions*))
971 (and (pair? known)
972 (if (string= str (caar known) start end)
973 (cdar known)
974 (lp (cdr known)))))
975 (let ((dot-idx (string-index str #\. start end)))
976 (if (and (string-prefix? "HTTP/" str 0 5 start end)
977 dot-idx
978 (= dot-idx (string-rindex str #\. start end)))
979 (cons (parse-non-negative-integer str (+ start 5) dot-idx)
980 (parse-non-negative-integer str (1+ dot-idx) end))
981 (bad-header-component 'http-version (substring str start end))))))
982
983 (define (write-http-version val port)
984 "Write the given major-minor version pair to @var{port}."
985 (display "HTTP/" port)
986 (display (car val) port)
987 (display #\. port)
988 (display (cdr val) port))
989
990 (for-each
991 (lambda (v)
992 (set! *known-versions*
993 (acons v (parse-http-version v 0 (string-length v))
994 *known-versions*)))
995 '("HTTP/1.0" "HTTP/1.1"))
996
997
998 ;; Request-URI = "*" | absoluteURI | abs_path | authority
999 ;;
1000 ;; The `authority' form is only permissible for the CONNECT method, so
1001 ;; because we don't expect people to implement CONNECT, we save
1002 ;; ourselves the trouble of that case, and disallow the CONNECT method.
1003 ;;
1004 (define* (parse-http-method str #:optional (start 0) (end (string-length str)))
1005 "Parse an HTTP method from @var{str}. The result is an upper-case
1006 symbol, like @code{GET}."
1007 (cond
1008 ((string= str "GET" start end) 'GET)
1009 ((string= str "HEAD" start end) 'HEAD)
1010 ((string= str "POST" start end) 'POST)
1011 ((string= str "PUT" start end) 'PUT)
1012 ((string= str "DELETE" start end) 'DELETE)
1013 ((string= str "OPTIONS" start end) 'OPTIONS)
1014 ((string= str "TRACE" start end) 'TRACE)
1015 (else (bad-request "Invalid method: ~a" (substring str start end)))))
1016
1017 (define* (parse-request-uri str #:optional (start 0) (end (string-length str)))
1018 "Parse a URI from an HTTP request line. Note that URIs in requests do
1019 not have to have a scheme or host name. The result is a URI object."
1020 (cond
1021 ((= start end)
1022 (bad-request "Missing Request-URI"))
1023 ((string= str "*" start end)
1024 #f)
1025 ((eq? (string-ref str start) #\/)
1026 (let* ((q (string-index str #\? start end))
1027 (f (string-index str #\# start end))
1028 (q (and q (or (not f) (< q f)) q)))
1029 (build-uri 'http
1030 #:path (substring str start (or q f end))
1031 #:query (and q (substring str (1+ q) (or f end)))
1032 #:fragment (and f (substring str (1+ f) end)))))
1033 (else
1034 (or (string->uri (substring str start end))
1035 (bad-request "Invalid URI: ~a" (substring str start end))))))
1036
1037 (define (read-request-line port)
1038 "Read the first line of an HTTP request from @var{port}, returning
1039 three values: the method, the URI, and the version."
1040 (let* ((line (read-line* port))
1041 (d0 (string-index line char-whitespace?)) ; "delimiter zero"
1042 (d1 (string-rindex line char-whitespace?)))
1043 (if (and d0 d1 (< d0 d1))
1044 (values (parse-http-method line 0 d0)
1045 (parse-request-uri line (skip-whitespace line (1+ d0) d1) d1)
1046 (parse-http-version line (1+ d1) (string-length line)))
1047 (bad-request "Bad Request-Line: ~s" line))))
1048
1049 (define (write-uri uri port)
1050 (if (uri-host uri)
1051 (begin
1052 (display (uri-scheme uri) port)
1053 (display "://" port)
1054 (if (uri-userinfo uri)
1055 (begin
1056 (display (uri-userinfo uri) port)
1057 (display #\@ port)))
1058 (display (uri-host uri) port)
1059 (let ((p (uri-port uri)))
1060 (if (and p (not (eqv? p 80)))
1061 (begin
1062 (display #\: port)
1063 (display p port))))))
1064 (let* ((path (uri-path uri))
1065 (len (string-length path)))
1066 (cond
1067 ((and (> len 0) (not (eqv? (string-ref path 0) #\/)))
1068 (bad-request "Non-absolute URI path: ~s" path))
1069 ((and (zero? len) (not (uri-host uri)))
1070 (bad-request "Empty path and no host for URI: ~s" uri))
1071 (else
1072 (display path port))))
1073 (if (uri-query uri)
1074 (begin
1075 (display #\? port)
1076 (display (uri-query uri) port))))
1077
1078 (define (write-request-line method uri version port)
1079 "Write the first line of an HTTP request to @var{port}."
1080 (display method port)
1081 (display #\space port)
1082 (let ((path (uri-path uri))
1083 (query (uri-query uri)))
1084 (if (not (string-null? path))
1085 (display path port))
1086 (if query
1087 (begin
1088 (display "?" port)
1089 (display query port)))
1090 (if (and (string-null? path)
1091 (not query))
1092 ;; Make sure we display something.
1093 (display "/" port)))
1094 (display #\space port)
1095 (write-http-version version port)
1096 (display "\r\n" port))
1097
1098 (define (read-response-line port)
1099 "Read the first line of an HTTP response from @var{port}, returning
1100 three values: the HTTP version, the response code, and the \"reason
1101 phrase\"."
1102 (let* ((line (read-line* port))
1103 (d0 (string-index line char-whitespace?)) ; "delimiter zero"
1104 (d1 (and d0 (string-index line char-whitespace?
1105 (skip-whitespace line d0)))))
1106 (if (and d0 d1)
1107 (values (parse-http-version line 0 d0)
1108 (parse-non-negative-integer line (skip-whitespace line d0 d1)
1109 d1)
1110 (string-trim-both line char-whitespace? d1))
1111 (bad-response "Bad Response-Line: ~s" line))))
1112
1113 (define (write-response-line version code reason-phrase port)
1114 "Write the first line of an HTTP response to @var{port}."
1115 (write-http-version version port)
1116 (display #\space port)
1117 (display code port)
1118 (display #\space port)
1119 (display reason-phrase port)
1120 (display "\r\n" port))
1121
1122
1123 \f
1124
1125 ;;;
1126 ;;; Helpers for declaring headers
1127 ;;;
1128
1129 ;; emacs: (put 'declare-header! 'scheme-indent-function 1)
1130 ;; emacs: (put 'declare-opaque!-header 'scheme-indent-function 1)
1131 (define (declare-opaque-header! name)
1132 (declare-header! name
1133 parse-opaque-string validate-opaque-string write-opaque-string))
1134
1135 ;; emacs: (put 'declare-date-header! 'scheme-indent-function 1)
1136 (define (declare-date-header! name)
1137 (declare-header! name
1138 parse-date date? write-date))
1139
1140 ;; emacs: (put 'declare-string-list-header! 'scheme-indent-function 1)
1141 (define (declare-string-list-header! name)
1142 (declare-header! name
1143 split-and-trim list-of-strings? write-list-of-strings))
1144
1145 ;; emacs: (put 'declare-symbol-list-header! 'scheme-indent-function 1)
1146 (define (declare-symbol-list-header! name)
1147 (declare-header! name
1148 (lambda (str)
1149 (map string->symbol (split-and-trim str)))
1150 (lambda (v)
1151 (list-of? v symbol?))
1152 (lambda (v port)
1153 (write-list v port display ", "))))
1154
1155 ;; emacs: (put 'declare-header-list-header! 'scheme-indent-function 1)
1156 (define (declare-header-list-header! name)
1157 (declare-header! name
1158 split-header-names list-of-header-names? write-header-list))
1159
1160 ;; emacs: (put 'declare-integer-header! 'scheme-indent-function 1)
1161 (define (declare-integer-header! name)
1162 (declare-header! name
1163 parse-non-negative-integer non-negative-integer? display))
1164
1165 ;; emacs: (put 'declare-uri-header! 'scheme-indent-function 1)
1166 (define (declare-uri-header! name)
1167 (declare-header! name
1168 (lambda (str) (or (string->uri str) (bad-header-component 'uri str)))
1169 uri?
1170 write-uri))
1171
1172 ;; emacs: (put 'declare-quality-list-header! 'scheme-indent-function 1)
1173 (define (declare-quality-list-header! name)
1174 (declare-header! name
1175 parse-quality-list validate-quality-list write-quality-list))
1176
1177 ;; emacs: (put 'declare-param-list-header! 'scheme-indent-function 1)
1178 (define* (declare-param-list-header! name #:optional
1179 (val-parser default-val-parser)
1180 (val-validator default-val-validator)
1181 (val-writer default-val-writer))
1182 (declare-header! name
1183 (lambda (str) (parse-param-list str val-parser))
1184 (lambda (val) (validate-param-list val val-validator))
1185 (lambda (val port) (write-param-list val port val-writer))))
1186
1187 ;; emacs: (put 'declare-key-value-list-header! 'scheme-indent-function 1)
1188 (define* (declare-key-value-list-header! name #:optional
1189 (val-parser default-val-parser)
1190 (val-validator default-val-validator)
1191 (val-writer default-val-writer))
1192 (declare-header! name
1193 (lambda (str) (parse-key-value-list str val-parser))
1194 (lambda (val) (key-value-list? val val-validator))
1195 (lambda (val port) (write-key-value-list val port val-writer))))
1196
1197 ;; emacs: (put 'declare-entity-tag-list-header! 'scheme-indent-function 1)
1198 (define (declare-entity-tag-list-header! name)
1199 (declare-header! name
1200 (lambda (str) (if (string=? str "*") '* (parse-entity-tag-list str)))
1201 (lambda (val) (or (eq? val '*) (entity-tag-list? val)))
1202 (lambda (val port)
1203 (if (eq? val '*)
1204 (display "*" port)
1205 (write-entity-tag-list val port)))))
1206
1207 ;; emacs: (put 'declare-credentials-header! 'scheme-indent-function 1)
1208 (define (declare-credentials-header! name)
1209 (declare-header! name
1210 parse-credentials validate-credentials write-credentials))
1211
1212 ;; emacs: (put 'declare-challenge-list-header! 'scheme-indent-function 1)
1213 (define (declare-challenge-list-header! name)
1214 (declare-header! name
1215 parse-challenges validate-challenges write-challenges))
1216
1217
1218 \f
1219
1220 ;;;
1221 ;;; General headers
1222 ;;;
1223
1224 ;; Cache-Control = 1#(cache-directive)
1225 ;; cache-directive = cache-request-directive | cache-response-directive
1226 ;; cache-request-directive =
1227 ;; "no-cache" ; Section 14.9.1
1228 ;; | "no-store" ; Section 14.9.2
1229 ;; | "max-age" "=" delta-seconds ; Section 14.9.3, 14.9.4
1230 ;; | "max-stale" [ "=" delta-seconds ] ; Section 14.9.3
1231 ;; | "min-fresh" "=" delta-seconds ; Section 14.9.3
1232 ;; | "no-transform" ; Section 14.9.5
1233 ;; | "only-if-cached" ; Section 14.9.4
1234 ;; | cache-extension ; Section 14.9.6
1235 ;; cache-response-directive =
1236 ;; "public" ; Section 14.9.1
1237 ;; | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
1238 ;; | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
1239 ;; | "no-store" ; Section 14.9.2
1240 ;; | "no-transform" ; Section 14.9.5
1241 ;; | "must-revalidate" ; Section 14.9.4
1242 ;; | "proxy-revalidate" ; Section 14.9.4
1243 ;; | "max-age" "=" delta-seconds ; Section 14.9.3
1244 ;; | "s-maxage" "=" delta-seconds ; Section 14.9.3
1245 ;; | cache-extension ; Section 14.9.6
1246 ;; cache-extension = token [ "=" ( token | quoted-string ) ]
1247 ;;
1248 (declare-key-value-list-header! "Cache-Control"
1249 (lambda (k v-str)
1250 (case k
1251 ((max-age min-fresh s-maxage)
1252 (parse-non-negative-integer v-str))
1253 ((max-stale)
1254 (and v-str (parse-non-negative-integer v-str)))
1255 ((private no-cache)
1256 (and v-str (split-header-names v-str)))
1257 (else v-str)))
1258 (lambda (k v)
1259 (case k
1260 ((max-age min-fresh s-maxage)
1261 (non-negative-integer? v))
1262 ((max-stale)
1263 (or (not v) (non-negative-integer? v)))
1264 ((private no-cache)
1265 (or (not v) (list-of-header-names? v)))
1266 ((no-store no-transform only-if-cache must-revalidate proxy-revalidate)
1267 (not v))
1268 (else
1269 (or (not v) (string? v)))))
1270 (lambda (k v port)
1271 (cond
1272 ((string? v) (default-val-writer k v port))
1273 ((pair? v)
1274 (display #\" port)
1275 (write-header-list v port)
1276 (display #\" port))
1277 ((integer? v)
1278 (display v port))
1279 (else
1280 (bad-header-component 'cache-control v)))))
1281
1282 ;; Connection = "Connection" ":" 1#(connection-token)
1283 ;; connection-token = token
1284 ;; e.g.
1285 ;; Connection: close, foo-header
1286 ;;
1287 (declare-header-list-header! "Connection")
1288
1289 ;; Date = "Date" ":" HTTP-date
1290 ;; e.g.
1291 ;; Date: Tue, 15 Nov 1994 08:12:31 GMT
1292 ;;
1293 (declare-date-header! "Date")
1294
1295 ;; Pragma = "Pragma" ":" 1#pragma-directive
1296 ;; pragma-directive = "no-cache" | extension-pragma
1297 ;; extension-pragma = token [ "=" ( token | quoted-string ) ]
1298 ;;
1299 (declare-key-value-list-header! "Pragma")
1300
1301 ;; Trailer = "Trailer" ":" 1#field-name
1302 ;;
1303 (declare-header-list-header! "Trailer")
1304
1305 ;; Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
1306 ;;
1307 (declare-param-list-header! "Transfer-Encoding")
1308
1309 ;; Upgrade = "Upgrade" ":" 1#product
1310 ;;
1311 (declare-string-list-header! "Upgrade")
1312
1313 ;; Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
1314 ;; received-protocol = [ protocol-name "/" ] protocol-version
1315 ;; protocol-name = token
1316 ;; protocol-version = token
1317 ;; received-by = ( host [ ":" port ] ) | pseudonym
1318 ;; pseudonym = token
1319 ;;
1320 (declare-header! "Via"
1321 split-and-trim
1322 list-of-strings?
1323 write-list-of-strings
1324 #:multiple? #t)
1325
1326 ;; Warning = "Warning" ":" 1#warning-value
1327 ;;
1328 ;; warning-value = warn-code SP warn-agent SP warn-text
1329 ;; [SP warn-date]
1330 ;;
1331 ;; warn-code = 3DIGIT
1332 ;; warn-agent = ( host [ ":" port ] ) | pseudonym
1333 ;; ; the name or pseudonym of the server adding
1334 ;; ; the Warning header, for use in debugging
1335 ;; warn-text = quoted-string
1336 ;; warn-date = <"> HTTP-date <">
1337 (declare-header! "Warning"
1338 (lambda (str)
1339 (let ((len (string-length str)))
1340 (let lp ((i (skip-whitespace str 0)))
1341 (let* ((idx1 (string-index str #\space i))
1342 (idx2 (string-index str #\space (1+ idx1))))
1343 (if (and idx1 idx2)
1344 (let ((code (parse-non-negative-integer str i idx1))
1345 (agent (substring str (1+ idx1) idx2)))
1346 (call-with-values
1347 (lambda () (parse-qstring str (1+ idx2) #:incremental? #t))
1348 (lambda (text i)
1349 (call-with-values
1350 (lambda ()
1351 (let ((c (and (< i len) (string-ref str i))))
1352 (case c
1353 ((#\space)
1354 ;; we have a date.
1355 (call-with-values
1356 (lambda () (parse-qstring str (1+ i)
1357 #:incremental? #t))
1358 (lambda (date i)
1359 (values text (parse-date date) i))))
1360 (else
1361 (values text #f i)))))
1362 (lambda (text date i)
1363 (let ((w (list code agent text date))
1364 (c (and (< i len) (string-ref str i))))
1365 (case c
1366 ((#f) (list w))
1367 ((#\,) (cons w (lp (skip-whitespace str (1+ i)))))
1368 (else (bad-header 'warning str))))))))))))))
1369 (lambda (val)
1370 (list-of? val
1371 (lambda (elt)
1372 (and (list? elt)
1373 (= (length elt) 4)
1374 (apply (lambda (code host text date)
1375 (and (non-negative-integer? code) (< code 1000)
1376 (string? host)
1377 (string? text)
1378 (or (not date) (date? date))))
1379 elt)))))
1380 (lambda (val port)
1381 (write-list
1382 val port
1383 (lambda (w port)
1384 (apply
1385 (lambda (code host text date)
1386 (display code port)
1387 (display #\space port)
1388 (display host port)
1389 (display #\space port)
1390 (write-qstring text port)
1391 (if date
1392 (begin
1393 (display #\space port)
1394 (write-date date port))))
1395 w))
1396 ", "))
1397 #:multiple? #t)
1398
1399
1400 \f
1401
1402 ;;;
1403 ;;; Entity headers
1404 ;;;
1405
1406 ;; Allow = #Method
1407 ;;
1408 (declare-symbol-list-header! "Allow")
1409
1410 ;; Content-Encoding = 1#content-coding
1411 ;;
1412 (declare-symbol-list-header! "Content-Encoding")
1413
1414 ;; Content-Language = 1#language-tag
1415 ;;
1416 (declare-string-list-header! "Content-Language")
1417
1418 ;; Content-Length = 1*DIGIT
1419 ;;
1420 (declare-integer-header! "Content-Length")
1421
1422 ;; Content-Location = ( absoluteURI | relativeURI )
1423 ;;
1424 (declare-uri-header! "Content-Location")
1425
1426 ;; Content-MD5 = <base64 of 128 bit MD5 digest as per RFC 1864>
1427 ;;
1428 (declare-opaque-header! "Content-MD5")
1429
1430 ;; Content-Range = content-range-spec
1431 ;; content-range-spec = byte-content-range-spec
1432 ;; byte-content-range-spec = bytes-unit SP
1433 ;; byte-range-resp-spec "/"
1434 ;; ( instance-length | "*" )
1435 ;; byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
1436 ;; | "*"
1437 ;; instance-length = 1*DIGIT
1438 ;;
1439 (declare-header! "Content-Range"
1440 (lambda (str)
1441 (let ((dash (string-index str #\-))
1442 (slash (string-index str #\/)))
1443 (if (and (string-prefix? "bytes " str) slash)
1444 (list 'bytes
1445 (cond
1446 (dash
1447 (cons
1448 (parse-non-negative-integer str 6 dash)
1449 (parse-non-negative-integer str (1+ dash) slash)))
1450 ((string= str "*" 6 slash)
1451 '*)
1452 (else
1453 (bad-header 'content-range str)))
1454 (if (string= str "*" (1+ slash))
1455 '*
1456 (parse-non-negative-integer str (1+ slash))))
1457 (bad-header 'content-range str))))
1458 (lambda (val)
1459 (and (list? val) (= (length val) 3)
1460 (symbol? (car val))
1461 (let ((x (cadr val)))
1462 (or (eq? x '*)
1463 (and (pair? x)
1464 (non-negative-integer? (car x))
1465 (non-negative-integer? (cdr x)))))
1466 (let ((x (caddr val)))
1467 (or (eq? x '*)
1468 (non-negative-integer? x)))))
1469 (lambda (val port)
1470 (display (car val) port)
1471 (display #\space port)
1472 (if (eq? (cadr val) '*)
1473 (display #\* port)
1474 (begin
1475 (display (caadr val) port)
1476 (display #\- port)
1477 (display (caadr val) port)))
1478 (if (eq? (caddr val) '*)
1479 (display #\* port)
1480 (display (caddr val) port))))
1481
1482 ;; Content-Type = media-type
1483 ;;
1484 (declare-header! "Content-Type"
1485 (lambda (str)
1486 (let ((parts (string-split str #\;)))
1487 (cons (parse-media-type (car parts))
1488 (map (lambda (x)
1489 (let ((eq (string-index x #\=)))
1490 (if (and eq (= eq (string-rindex x #\=)))
1491 (cons (string->symbol
1492 (string-trim x char-whitespace? 0 eq))
1493 (string-trim-right x char-whitespace? (1+ eq)))
1494 (bad-header 'content-type str))))
1495 (cdr parts)))))
1496 (lambda (val)
1497 (and (pair? val)
1498 (symbol? (car val))
1499 (list-of? (cdr val)
1500 (lambda (x)
1501 (and (pair? x) (symbol? (car x)) (string? (cdr x)))))))
1502 (lambda (val port)
1503 (display (car val) port)
1504 (if (pair? (cdr val))
1505 (begin
1506 (display ";" port)
1507 (write-list
1508 (cdr val) port
1509 (lambda (pair port)
1510 (display (car pair) port)
1511 (display #\= port)
1512 (display (cdr pair) port))
1513 ";")))))
1514
1515 ;; Expires = HTTP-date
1516 ;;
1517 (define *date-in-the-past* (parse-date "Thu, 01 Jan 1970 00:00:00 GMT"))
1518
1519 (declare-header! "Expires"
1520 (lambda (str)
1521 (if (member str '("0" "-1"))
1522 *date-in-the-past*
1523 (parse-date str)))
1524 date?
1525 write-date)
1526
1527 ;; Last-Modified = HTTP-date
1528 ;;
1529 (declare-date-header! "Last-Modified")
1530
1531
1532 \f
1533
1534 ;;;
1535 ;;; Request headers
1536 ;;;
1537
1538 ;; Accept = #( media-range [ accept-params ] )
1539 ;; media-range = ( "*/*" | ( type "/" "*" ) | ( type "/" subtype ) )
1540 ;; *( ";" parameter )
1541 ;; accept-params = ";" "q" "=" qvalue *( accept-extension )
1542 ;; accept-extension = ";" token [ "=" ( token | quoted-string ) ]
1543 ;;
1544 (declare-param-list-header! "Accept"
1545 ;; -> (type/subtype (sym-prop . str-val) ...) ...)
1546 ;;
1547 ;; with the exception of prop `q', in which case the val will be a
1548 ;; valid quality value
1549 ;;
1550 (lambda (k v)
1551 (if (eq? k 'q)
1552 (parse-quality v)
1553 v))
1554 (lambda (k v)
1555 (if (eq? k 'q)
1556 (valid-quality? v)
1557 (or (not v) (string? v))))
1558 (lambda (k v port)
1559 (if (eq? k 'q)
1560 (write-quality v port)
1561 (default-val-writer k v port))))
1562
1563 ;; Accept-Charset = 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] )
1564 ;;
1565 (declare-quality-list-header! "Accept-Charset")
1566
1567 ;; Accept-Encoding = 1#( codings [ ";" "q" "=" qvalue ] )
1568 ;; codings = ( content-coding | "*" )
1569 ;;
1570 (declare-quality-list-header! "Accept-Encoding")
1571
1572 ;; Accept-Language = 1#( language-range [ ";" "q" "=" qvalue ] )
1573 ;; language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
1574 ;;
1575 (declare-quality-list-header! "Accept-Language")
1576
1577 ;; Authorization = credentials
1578 ;; credentials = auth-scheme #auth-param
1579 ;; auth-scheme = token
1580 ;; auth-param = token "=" ( token | quoted-string )
1581 ;;
1582 (declare-credentials-header! "Authorization")
1583
1584 ;; Expect = 1#expectation
1585 ;; expectation = "100-continue" | expectation-extension
1586 ;; expectation-extension = token [ "=" ( token | quoted-string )
1587 ;; *expect-params ]
1588 ;; expect-params = ";" token [ "=" ( token | quoted-string ) ]
1589 ;;
1590 (declare-param-list-header! "Expect")
1591
1592 ;; From = mailbox
1593 ;;
1594 ;; Should be an email address; we just pass on the string as-is.
1595 ;;
1596 (declare-opaque-header! "From")
1597
1598 ;; Host = host [ ":" port ]
1599 ;;
1600 (declare-header! "Host"
1601 (lambda (str)
1602 (let ((colon (string-index str #\:)))
1603 (if colon
1604 (cons (substring str 0 colon)
1605 (parse-non-negative-integer str (1+ colon)))
1606 (cons str #f))))
1607 (lambda (val)
1608 (and (pair? val)
1609 (string? (car val))
1610 (or (not (cdr val))
1611 (non-negative-integer? (cdr val)))))
1612 (lambda (val port)
1613 (display (car val) port)
1614 (if (cdr val)
1615 (begin
1616 (display #\: port)
1617 (display (cdr val) port)))))
1618
1619 ;; If-Match = ( "*" | 1#entity-tag )
1620 ;;
1621 (declare-entity-tag-list-header! "If-Match")
1622
1623 ;; If-Modified-Since = HTTP-date
1624 ;;
1625 (declare-date-header! "If-Modified-Since")
1626
1627 ;; If-None-Match = ( "*" | 1#entity-tag )
1628 ;;
1629 (declare-entity-tag-list-header! "If-None-Match")
1630
1631 ;; If-Range = ( entity-tag | HTTP-date )
1632 ;;
1633 (declare-header! "If-Range"
1634 (lambda (str)
1635 (if (or (string-prefix? "\"" str)
1636 (string-prefix? "W/" str))
1637 (parse-entity-tag str)
1638 (parse-date str)))
1639 (lambda (val)
1640 (or (date? val) (entity-tag? val)))
1641 (lambda (val port)
1642 (if (date? val)
1643 (write-date val port)
1644 (write-entity-tag val port))))
1645
1646 ;; If-Unmodified-Since = HTTP-date
1647 ;;
1648 (declare-date-header! "If-Unmodified-Since")
1649
1650 ;; Max-Forwards = 1*DIGIT
1651 ;;
1652 (declare-integer-header! "Max-Forwards")
1653
1654 ;; Proxy-Authorization = credentials
1655 ;;
1656 (declare-credentials-header! "Proxy-Authorization")
1657
1658 ;; Range = "Range" ":" ranges-specifier
1659 ;; ranges-specifier = byte-ranges-specifier
1660 ;; byte-ranges-specifier = bytes-unit "=" byte-range-set
1661 ;; byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
1662 ;; byte-range-spec = first-byte-pos "-" [last-byte-pos]
1663 ;; first-byte-pos = 1*DIGIT
1664 ;; last-byte-pos = 1*DIGIT
1665 ;; suffix-byte-range-spec = "-" suffix-length
1666 ;; suffix-length = 1*DIGIT
1667 ;;
1668 (declare-header! "Range"
1669 (lambda (str)
1670 (if (string-prefix? "bytes=" str)
1671 (cons
1672 'bytes
1673 (map (lambda (x)
1674 (let ((dash (string-index x #\-)))
1675 (cond
1676 ((not dash)
1677 (bad-header 'range str))
1678 ((zero? dash)
1679 (cons #f (parse-non-negative-integer x 1)))
1680 ((= dash (1- (string-length x)))
1681 (cons (parse-non-negative-integer x 0 dash) #f))
1682 (else
1683 (cons (parse-non-negative-integer x 0 dash)
1684 (parse-non-negative-integer x (1+ dash)))))))
1685 (string-split (substring str 6) #\,)))
1686 (bad-header 'range str)))
1687 (lambda (val)
1688 (and (pair? val)
1689 (symbol? (car val))
1690 (list-of? (cdr val)
1691 (lambda (elt)
1692 (and (pair? elt)
1693 (let ((x (car elt)) (y (cdr elt)))
1694 (and (or x y)
1695 (or (not x) (non-negative-integer? x))
1696 (or (not y) (non-negative-integer? y)))))))))
1697 (lambda (val port)
1698 (display (car val) port)
1699 (display #\= port)
1700 (write-list
1701 (cdr val) port
1702 (lambda (pair port)
1703 (if (car pair)
1704 (display (car pair) port))
1705 (display #\- port)
1706 (if (cdr pair)
1707 (display (cdr pair) port)))
1708 ",")))
1709
1710 ;; Referer = ( absoluteURI | relativeURI )
1711 ;;
1712 (declare-uri-header! "Referer")
1713
1714 ;; TE = #( t-codings )
1715 ;; t-codings = "trailers" | ( transfer-extension [ accept-params ] )
1716 ;;
1717 (declare-param-list-header! "TE")
1718
1719 ;; User-Agent = 1*( product | comment )
1720 ;;
1721 (declare-opaque-header! "User-Agent")
1722
1723
1724 \f
1725
1726 ;;;
1727 ;;; Reponse headers
1728 ;;;
1729
1730 ;; Accept-Ranges = acceptable-ranges
1731 ;; acceptable-ranges = 1#range-unit | "none"
1732 ;;
1733 (declare-symbol-list-header! "Accept-Ranges")
1734
1735 ;; Age = age-value
1736 ;; age-value = delta-seconds
1737 ;;
1738 (declare-integer-header! "Age")
1739
1740 ;; ETag = entity-tag
1741 ;;
1742 (declare-header! "ETag"
1743 parse-entity-tag
1744 entity-tag?
1745 write-entity-tag)
1746
1747 ;; Location = absoluteURI
1748 ;;
1749 (declare-uri-header! "Location")
1750
1751 ;; Proxy-Authenticate = 1#challenge
1752 ;;
1753 (declare-challenge-list-header! "Proxy-Authenticate")
1754
1755 ;; Retry-After = ( HTTP-date | delta-seconds )
1756 ;;
1757 (declare-header! "Retry-After"
1758 (lambda (str)
1759 (if (and (not (string-null? str))
1760 (char-numeric? (string-ref str 0)))
1761 (parse-non-negative-integer str)
1762 (parse-date str)))
1763 (lambda (val)
1764 (or (date? val) (non-negative-integer? val)))
1765 (lambda (val port)
1766 (if (date? val)
1767 (write-date val port)
1768 (display val port))))
1769
1770 ;; Server = 1*( product | comment )
1771 ;;
1772 (declare-opaque-header! "Server")
1773
1774 ;; Vary = ( "*" | 1#field-name )
1775 ;;
1776 (declare-header! "Vary"
1777 (lambda (str)
1778 (if (equal? str "*")
1779 '*
1780 (split-header-names str)))
1781 (lambda (val)
1782 (or (eq? val '*) (list-of-header-names? val)))
1783 (lambda (val port)
1784 (if (eq? val '*)
1785 (display "*" port)
1786 (write-header-list val port))))
1787
1788 ;; WWW-Authenticate = 1#challenge
1789 ;;
1790 (declare-challenge-list-header! "WWW-Authenticate")