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