Add support for content-disposition
[bpt/guile.git] / module / web / http.scm
1 ;;; HTTP messages
2
3 ;; Copyright (C) 2010, 2011, 2012, 2013, 2014 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 (ice-9 q)
38 #:use-module (ice-9 binary-ports)
39 #:use-module (rnrs bytevectors)
40 #:use-module (web uri)
41 #:export (string->header
42 header->string
43
44 declare-header!
45 declare-opaque-header!
46 known-header?
47 header-parser
48 header-validator
49 header-writer
50
51 read-header
52 parse-header
53 valid-header?
54 write-header
55
56 read-headers
57 write-headers
58
59 parse-http-method
60 parse-http-version
61 parse-request-uri
62
63 read-request-line
64 write-request-line
65 read-response-line
66 write-response-line
67
68 make-chunked-input-port
69 make-chunked-output-port
70
71 http-proxy-port?
72 set-http-proxy-port?!))
73
74
75 (define (string->header name)
76 "Parse NAME to a symbolic header name."
77 (string->symbol (string-downcase name)))
78
79 (define-record-type <header-decl>
80 (make-header-decl name parser validator writer multiple?)
81 header-decl?
82 (name header-decl-name)
83 (parser header-decl-parser)
84 (validator header-decl-validator)
85 (writer header-decl-writer)
86 (multiple? header-decl-multiple?))
87
88 ;; sym -> header
89 (define *declared-headers* (make-hash-table))
90
91 (define (lookup-header-decl sym)
92 (hashq-ref *declared-headers* sym))
93
94 (define* (declare-header! name
95 parser
96 validator
97 writer
98 #:key multiple?)
99 "Declare a parser, validator, and writer for a given header."
100 (if (and (string? name) parser validator writer)
101 (let ((decl (make-header-decl name parser validator writer multiple?)))
102 (hashq-set! *declared-headers* (string->header name) decl)
103 decl)
104 (error "bad header decl" name parser validator writer multiple?)))
105
106 (define (header->string sym)
107 "Return the string form for the header named SYM."
108 (let ((decl (lookup-header-decl sym)))
109 (if decl
110 (header-decl-name decl)
111 (string-titlecase (symbol->string sym)))))
112
113 (define (known-header? sym)
114 "Return ‘#t’ iff SYM is a known header, with associated
115 parsers and serialization procedures."
116 (and (lookup-header-decl sym) #t))
117
118 (define (header-parser sym)
119 "Return the value parser for headers named SYM. The result is a
120 procedure that takes one argument, a string, and returns the parsed
121 value. If the header isn't known to Guile, a default parser is returned
122 that passes through the string unchanged."
123 (let ((decl (lookup-header-decl sym)))
124 (if decl
125 (header-decl-parser decl)
126 (lambda (x) x))))
127
128 (define (header-validator sym)
129 "Return a predicate which returns ‘#t’ if the given value is valid
130 for headers named SYM. The default validator for unknown headers
131 is ‘string?’."
132 (let ((decl (lookup-header-decl sym)))
133 (if decl
134 (header-decl-validator decl)
135 string?)))
136
137 (define (header-writer sym)
138 "Return a procedure that writes values for headers named SYM to a
139 port. The resulting procedure takes two arguments: a value and a port.
140 The default writer is ‘display’."
141 (let ((decl (lookup-header-decl sym)))
142 (if decl
143 (header-decl-writer decl)
144 display)))
145
146 (define (read-line* port)
147 (let* ((pair (%read-line port))
148 (line (car pair))
149 (delim (cdr pair)))
150 (if (and (string? line) (char? delim))
151 (let ((orig-len (string-length line)))
152 (let lp ((len orig-len))
153 (if (and (> len 0)
154 (char-whitespace? (string-ref line (1- len))))
155 (lp (1- len))
156 (if (= len orig-len)
157 line
158 (substring line 0 len)))))
159 (bad-header '%read line))))
160
161 (define (read-continuation-line port val)
162 (if (or (eqv? (peek-char port) #\space)
163 (eqv? (peek-char port) #\tab))
164 (read-continuation-line port
165 (string-append val
166 (begin
167 (read-line* port))))
168 val))
169
170 (define *eof* (call-with-input-string "" read))
171
172 (define (read-header port)
173 "Read one HTTP header from PORT. Return two values: the header
174 name and the parsed Scheme value. May raise an exception if the header
175 was known but the value was invalid.
176
177 Returns the end-of-file object for both values if the end of the message
178 body was reached (i.e., a blank line)."
179 (let ((line (read-line* port)))
180 (if (or (string-null? line)
181 (string=? line "\r"))
182 (values *eof* *eof*)
183 (let* ((delim (or (string-index line #\:)
184 (bad-header '%read line)))
185 (sym (string->header (substring line 0 delim))))
186 (values
187 sym
188 (parse-header
189 sym
190 (read-continuation-line
191 port
192 (string-trim-both line char-set:whitespace (1+ delim)))))))))
193
194 (define (parse-header sym val)
195 "Parse VAL, a string, with the parser registered for the header
196 named SYM. Returns the parsed value."
197 ((header-parser sym) val))
198
199 (define (valid-header? sym val)
200 "Returns a true value iff VAL is a valid Scheme value for the
201 header with name SYM."
202 (if (symbol? sym)
203 ((header-validator sym) val)
204 (error "header name not a symbol" sym)))
205
206 (define (write-header sym val port)
207 "Write the given header name and value to PORT, using the writer
208 from ‘header-writer’."
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 the headers of an HTTP message from PORT, returning them
216 as an 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 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 ;; "GMT" | "+" 4DIGIT | "-" 4DIGIT
709 ;;
710 ;; RFC 2616 requires date values to use "GMT", but recommends accepting
711 ;; the others as they are commonly generated by e.g. RFC 822 sources.
712 (define (parse-zone-offset str start)
713 (let ((s (substring str start)))
714 (define (bad)
715 (bad-header-component 'zone-offset s))
716 (cond
717 ((string=? s "GMT")
718 0)
719 ((string=? s "UTC")
720 0)
721 ((string-match? s ".dddd")
722 (let ((sign (case (string-ref s 0)
723 ((#\+) +1)
724 ((#\-) -1)
725 (else (bad))))
726 (hours (parse-non-negative-integer s 1 3))
727 (minutes (parse-non-negative-integer s 3 5)))
728 (* sign 60 (+ (* 60 hours) minutes)))) ; seconds east of Greenwich
729 (else (bad)))))
730
731 ;; RFC 822, updated by RFC 1123
732 ;;
733 ;; Sun, 06 Nov 1994 08:49:37 GMT
734 ;; 01234567890123456789012345678
735 ;; 0 1 2
736 (define (parse-rfc-822-date str space zone-offset)
737 ;; We could verify the day of the week but we don't.
738 (cond ((string-match? (substring str 0 space) "aaa, dd aaa dddd dd:dd:dd")
739 (let ((date (parse-non-negative-integer str 5 7))
740 (month (parse-month str 8 11))
741 (year (parse-non-negative-integer str 12 16))
742 (hour (parse-non-negative-integer str 17 19))
743 (minute (parse-non-negative-integer str 20 22))
744 (second (parse-non-negative-integer str 23 25)))
745 (make-date 0 second minute hour date month year zone-offset)))
746 ((string-match? (substring str 0 space) "aaa, d aaa dddd dd:dd:dd")
747 (let ((date (parse-non-negative-integer str 5 6))
748 (month (parse-month str 7 10))
749 (year (parse-non-negative-integer str 11 15))
750 (hour (parse-non-negative-integer str 16 18))
751 (minute (parse-non-negative-integer str 19 21))
752 (second (parse-non-negative-integer str 22 24)))
753 (make-date 0 second minute hour date month year zone-offset)))
754 (else
755 (bad-header 'date str) ; prevent tail call
756 #f)))
757
758 ;; RFC 850, updated by RFC 1036
759 ;; Sunday, 06-Nov-94 08:49:37 GMT
760 ;; 0123456789012345678901
761 ;; 0 1 2
762 (define (parse-rfc-850-date str comma space zone-offset)
763 ;; We could verify the day of the week but we don't.
764 (let ((tail (substring str (1+ comma) space)))
765 (if (not (string-match? tail " dd-aaa-dd dd:dd:dd"))
766 (bad-header 'date str))
767 (let ((date (parse-non-negative-integer tail 1 3))
768 (month (parse-month tail 4 7))
769 (year (parse-non-negative-integer tail 8 10))
770 (hour (parse-non-negative-integer tail 11 13))
771 (minute (parse-non-negative-integer tail 14 16))
772 (second (parse-non-negative-integer tail 17 19)))
773 (make-date 0 second minute hour date month
774 (let* ((now (date-year (current-date)))
775 (then (+ now year (- (modulo now 100)))))
776 (cond ((< (+ then 50) now) (+ then 100))
777 ((< (+ now 50) then) (- then 100))
778 (else then)))
779 zone-offset))))
780
781 ;; ANSI C's asctime() format
782 ;; Sun Nov 6 08:49:37 1994
783 ;; 012345678901234567890123
784 ;; 0 1 2
785 (define (parse-asctime-date str)
786 (if (not (string-match? str "aaa aaa .d dd:dd:dd dddd"))
787 (bad-header 'date str))
788 (let ((date (parse-non-negative-integer
789 str
790 (if (eqv? (string-ref str 8) #\space) 9 8)
791 10))
792 (month (parse-month str 4 7))
793 (year (parse-non-negative-integer str 20 24))
794 (hour (parse-non-negative-integer str 11 13))
795 (minute (parse-non-negative-integer str 14 16))
796 (second (parse-non-negative-integer str 17 19)))
797 (make-date 0 second minute hour date month year 0)))
798
799 ;; Convert all date values to GMT time zone, as per RFC 2616 appendix C.
800 (define (normalize-date date)
801 (if (zero? (date-zone-offset date))
802 date
803 (time-utc->date (date->time-utc date) 0)))
804
805 (define (parse-date str)
806 (let* ((space (string-rindex str #\space))
807 (zone-offset (and space (false-if-exception
808 (parse-zone-offset str (1+ space))))))
809 (normalize-date
810 (if zone-offset
811 (let ((comma (string-index str #\,)))
812 (cond ((not comma) (bad-header 'date str))
813 ((= comma 3) (parse-rfc-822-date str space zone-offset))
814 (else (parse-rfc-850-date str comma space zone-offset))))
815 (parse-asctime-date str)))))
816
817 (define (write-date date port)
818 (define (display-digits n digits port)
819 (define zero (char->integer #\0))
820 (let lp ((tens (expt 10 (1- digits))))
821 (if (> tens 0)
822 (begin
823 (display (integer->char (+ zero (modulo (truncate/ n tens) 10)))
824 port)
825 (lp (floor/ tens 10))))))
826 (let ((date (if (zero? (date-zone-offset date))
827 date
828 (time-tai->date (date->time-tai date) 0))))
829 (display (case (date-week-day date)
830 ((0) "Sun, ") ((1) "Mon, ") ((2) "Tue, ")
831 ((3) "Wed, ") ((4) "Thu, ") ((5) "Fri, ")
832 ((6) "Sat, ") (else (error "bad date" date)))
833 port)
834 (display-digits (date-day date) 2 port)
835 (display (case (date-month date)
836 ((1) " Jan ") ((2) " Feb ") ((3) " Mar ")
837 ((4) " Apr ") ((5) " May ") ((6) " Jun ")
838 ((7) " Jul ") ((8) " Aug ") ((9) " Sep ")
839 ((10) " Oct ") ((11) " Nov ") ((12) " Dec ")
840 (else (error "bad date" date)))
841 port)
842 (display-digits (date-year date) 4 port)
843 (display #\space port)
844 (display-digits (date-hour date) 2 port)
845 (display #\: port)
846 (display-digits (date-minute date) 2 port)
847 (display #\: port)
848 (display-digits (date-second date) 2 port)
849 (display " GMT" port)))
850
851 (define (parse-entity-tag val)
852 (if (string-prefix? "W/" val)
853 (cons (parse-qstring val 2) #f)
854 (cons (parse-qstring val) #t)))
855
856 (define (entity-tag? val)
857 (and (pair? val)
858 (string? (car val))))
859
860 (define (write-entity-tag val port)
861 (if (not (cdr val))
862 (display "W/" port))
863 (write-qstring (car val) port))
864
865 (define* (parse-entity-tag-list val #:optional
866 (start 0) (end (string-length val)))
867 (let ((strong? (not (string-prefix? "W/" val 0 2 start end))))
868 (call-with-values (lambda ()
869 (parse-qstring val (if strong? start (+ start 2))
870 end #:incremental? #t))
871 (lambda (tag next)
872 (acons tag strong?
873 (let ((next (skip-whitespace val next end)))
874 (if (< next end)
875 (if (eqv? (string-ref val next) #\,)
876 (parse-entity-tag-list
877 val
878 (skip-whitespace val (1+ next) end)
879 end)
880 (bad-header-component 'entity-tag-list val))
881 '())))))))
882
883 (define (entity-tag-list? val)
884 (list-of? val entity-tag?))
885
886 (define (write-entity-tag-list val port)
887 (write-list val port write-entity-tag ", "))
888
889 ;; credentials = auth-scheme #auth-param
890 ;; auth-scheme = token
891 ;; auth-param = token "=" ( token | quoted-string )
892 ;;
893 ;; That's what the spec says. In reality the Basic scheme doesn't have
894 ;; k-v pairs, just one auth token, so we give that token as a string.
895 ;;
896 (define* (parse-credentials str #:optional (val-parser default-val-parser)
897 (start 0) (end (string-length str)))
898 (let* ((start (skip-whitespace str start end))
899 (delim (or (string-index str char-set:whitespace start end) end)))
900 (if (= start end)
901 (bad-header-component 'authorization str))
902 (let ((scheme (string->symbol
903 (string-downcase (substring str start (or delim end))))))
904 (case scheme
905 ((basic)
906 (let* ((start (skip-whitespace str delim end)))
907 (if (< start end)
908 (cons scheme (substring str start end))
909 (bad-header-component 'credentials str))))
910 (else
911 (cons scheme (parse-key-value-list str default-val-parser delim end)))))))
912
913 (define (validate-credentials val)
914 (and (pair? val) (symbol? (car val))
915 (case (car val)
916 ((basic) (string? (cdr val)))
917 (else (key-value-list? (cdr val))))))
918
919 (define (write-credentials val port)
920 (display (car val) port)
921 (if (pair? (cdr val))
922 (begin
923 (display #\space port)
924 (write-key-value-list (cdr val) port))))
925
926 ;; challenges = 1#challenge
927 ;; challenge = auth-scheme 1*SP 1#auth-param
928 ;;
929 ;; A pain to parse, as both challenges and auth params are delimited by
930 ;; commas, and qstrings can contain anything. We rely on auth params
931 ;; necessarily having "=" in them.
932 ;;
933 (define* (parse-challenge str #:optional
934 (start 0) (end (string-length str)))
935 (let* ((start (skip-whitespace str start end))
936 (sp (string-index str #\space start end))
937 (scheme (if sp
938 (string->symbol (string-downcase (substring str start sp)))
939 (bad-header-component 'challenge str))))
940 (let lp ((i sp) (out (list scheme)))
941 (if (not (< i end))
942 (values (reverse! out) end)
943 (let* ((i (skip-whitespace str i end))
944 (eq (string-index str #\= i end))
945 (comma (string-index str #\, i end))
946 (delim (min (or eq end) (or comma end)))
947 (token-end (trim-whitespace str i delim)))
948 (if (string-index str #\space i token-end)
949 (values (reverse! out) i)
950 (let ((k (string->symbol (substring str i token-end))))
951 (call-with-values
952 (lambda ()
953 (if (and eq (or (not comma) (< eq comma)))
954 (let ((i (skip-whitespace str (1+ eq) end)))
955 (if (and (< i end) (eqv? (string-ref str i) #\"))
956 (parse-qstring str i end #:incremental? #t)
957 (values (substring
958 str i
959 (trim-whitespace str i
960 (or comma end)))
961 (or comma end))))
962 (values #f delim)))
963 (lambda (v next-i)
964 (let ((i (skip-whitespace str next-i end)))
965 (if (or (= i end) (eqv? (string-ref str i) #\,))
966 (lp (1+ i) (cons (if v (cons k v) k) out))
967 (bad-header-component
968 'challenge
969 (substring str start end)))))))))))))
970
971 (define* (parse-challenges str #:optional (val-parser default-val-parser)
972 (start 0) (end (string-length str)))
973 (let lp ((i start) (ret '()))
974 (let ((i (skip-whitespace str i end)))
975 (if (< i end)
976 (call-with-values (lambda () (parse-challenge str i end))
977 (lambda (challenge i)
978 (lp i (cons challenge ret))))
979 (reverse ret)))))
980
981 (define (validate-challenges val)
982 (list-of? val (lambda (x)
983 (and (pair? x) (symbol? (car x))
984 (key-value-list? (cdr x))))))
985
986 (define (write-challenge val port)
987 (display (car val) port)
988 (display #\space port)
989 (write-key-value-list (cdr val) port))
990
991 (define (write-challenges val port)
992 (write-list val port write-challenge ", "))
993
994
995 \f
996
997 ;;;
998 ;;; Request-Line and Response-Line
999 ;;;
1000
1001 ;; Hmm.
1002 (define (bad-request message . args)
1003 (throw 'bad-request message args))
1004 (define (bad-response message . args)
1005 (throw 'bad-response message args))
1006
1007 (define *known-versions* '())
1008
1009 (define* (parse-http-version str #:optional (start 0) (end (string-length str)))
1010 "Parse an HTTP version from STR, returning it as a major–minor
1011 pair. For example, ‘HTTP/1.1’ parses as the pair of integers,
1012 ‘(1 . 1)’."
1013 (or (let lp ((known *known-versions*))
1014 (and (pair? known)
1015 (if (string= str (caar known) start end)
1016 (cdar known)
1017 (lp (cdr known)))))
1018 (let ((dot-idx (string-index str #\. start end)))
1019 (if (and (string-prefix? "HTTP/" str 0 5 start end)
1020 dot-idx
1021 (= dot-idx (string-rindex str #\. start end)))
1022 (cons (parse-non-negative-integer str (+ start 5) dot-idx)
1023 (parse-non-negative-integer str (1+ dot-idx) end))
1024 (bad-header-component 'http-version (substring str start end))))))
1025
1026 (define (write-http-version val port)
1027 "Write the given major-minor version pair to PORT."
1028 (display "HTTP/" port)
1029 (display (car val) port)
1030 (display #\. port)
1031 (display (cdr val) port))
1032
1033 (for-each
1034 (lambda (v)
1035 (set! *known-versions*
1036 (acons v (parse-http-version v 0 (string-length v))
1037 *known-versions*)))
1038 '("HTTP/1.0" "HTTP/1.1"))
1039
1040
1041 ;; Request-URI = "*" | absoluteURI | abs_path | authority
1042 ;;
1043 ;; The `authority' form is only permissible for the CONNECT method, so
1044 ;; because we don't expect people to implement CONNECT, we save
1045 ;; ourselves the trouble of that case, and disallow the CONNECT method.
1046 ;;
1047 (define* (parse-http-method str #:optional (start 0) (end (string-length str)))
1048 "Parse an HTTP method from STR. The result is an upper-case
1049 symbol, like ‘GET’."
1050 (cond
1051 ((string= str "GET" start end) 'GET)
1052 ((string= str "HEAD" start end) 'HEAD)
1053 ((string= str "POST" start end) 'POST)
1054 ((string= str "PUT" start end) 'PUT)
1055 ((string= str "DELETE" start end) 'DELETE)
1056 ((string= str "OPTIONS" start end) 'OPTIONS)
1057 ((string= str "TRACE" start end) 'TRACE)
1058 (else (bad-request "Invalid method: ~a" (substring str start end)))))
1059
1060 (define* (parse-request-uri str #:optional (start 0) (end (string-length str)))
1061 "Parse a URI from an HTTP request line. Note that URIs in requests do
1062 not have to have a scheme or host name. The result is a URI object."
1063 (cond
1064 ((= start end)
1065 (bad-request "Missing Request-URI"))
1066 ((string= str "*" start end)
1067 #f)
1068 ((eq? (string-ref str start) #\/)
1069 (let* ((q (string-index str #\? start end))
1070 (f (string-index str #\# start end))
1071 (q (and q (or (not f) (< q f)) q)))
1072 (build-uri 'http
1073 #:path (substring str start (or q f end))
1074 #:query (and q (substring str (1+ q) (or f end)))
1075 #:fragment (and f (substring str (1+ f) end)))))
1076 (else
1077 (or (string->uri (substring str start end))
1078 (bad-request "Invalid URI: ~a" (substring str start end))))))
1079
1080 (define (read-request-line port)
1081 "Read the first line of an HTTP request from PORT, returning
1082 three values: the method, the URI, and the version."
1083 (let* ((line (read-line* port))
1084 (d0 (string-index line char-set:whitespace)) ; "delimiter zero"
1085 (d1 (string-rindex line char-set:whitespace)))
1086 (if (and d0 d1 (< d0 d1))
1087 (values (parse-http-method line 0 d0)
1088 (parse-request-uri line (skip-whitespace line (1+ d0) d1) d1)
1089 (parse-http-version line (1+ d1) (string-length line)))
1090 (bad-request "Bad Request-Line: ~s" line))))
1091
1092 (define (write-uri uri port)
1093 (if (uri-host uri)
1094 (begin
1095 (display (uri-scheme uri) port)
1096 (display "://" port)
1097 (if (uri-userinfo uri)
1098 (begin
1099 (display (uri-userinfo uri) port)
1100 (display #\@ port)))
1101 (display (uri-host uri) port)
1102 (let ((p (uri-port uri)))
1103 (if (and p (not (eqv? p 80)))
1104 (begin
1105 (display #\: port)
1106 (display p port))))))
1107 (let* ((path (uri-path uri))
1108 (len (string-length path)))
1109 (cond
1110 ((and (> len 0) (not (eqv? (string-ref path 0) #\/)))
1111 (bad-request "Non-absolute URI path: ~s" path))
1112 ((and (zero? len) (not (uri-host uri)))
1113 (bad-request "Empty path and no host for URI: ~s" uri))
1114 (else
1115 (display path port))))
1116 (if (uri-query uri)
1117 (begin
1118 (display #\? port)
1119 (display (uri-query uri) port))))
1120
1121 (define (write-request-line method uri version port)
1122 "Write the first line of an HTTP request to PORT."
1123 (display method port)
1124 (display #\space port)
1125 (when (http-proxy-port? port)
1126 (let ((scheme (uri-scheme uri))
1127 (host (uri-host uri))
1128 (host-port (uri-port uri)))
1129 (when (and scheme host)
1130 (display scheme port)
1131 (display "://" port)
1132 (if (string-index host #\:)
1133 (begin (display #\[ port)
1134 (display host port)
1135 (display #\] port))
1136 (display host port))
1137 (unless ((@@ (web uri) default-port?) scheme host-port)
1138 (display #\: port)
1139 (display host-port port)))))
1140 (let ((path (uri-path uri))
1141 (query (uri-query uri)))
1142 (if (string-null? path)
1143 (display "/" port)
1144 (display path port))
1145 (if query
1146 (begin
1147 (display "?" port)
1148 (display query port))))
1149 (display #\space port)
1150 (write-http-version version port)
1151 (display "\r\n" port))
1152
1153 (define (read-response-line port)
1154 "Read the first line of an HTTP response from PORT, returning
1155 three values: the HTTP version, the response code, and the \"reason
1156 phrase\"."
1157 (let* ((line (read-line* port))
1158 (d0 (string-index line char-set:whitespace)) ; "delimiter zero"
1159 (d1 (and d0 (string-index line char-set:whitespace
1160 (skip-whitespace line d0)))))
1161 (if (and d0 d1)
1162 (values (parse-http-version line 0 d0)
1163 (parse-non-negative-integer line (skip-whitespace line d0 d1)
1164 d1)
1165 (string-trim-both line char-set:whitespace d1))
1166 (bad-response "Bad Response-Line: ~s" line))))
1167
1168 (define (write-response-line version code reason-phrase port)
1169 "Write the first line of an HTTP response to PORT."
1170 (write-http-version version port)
1171 (display #\space port)
1172 (display code port)
1173 (display #\space port)
1174 (display reason-phrase port)
1175 (display "\r\n" port))
1176
1177
1178 \f
1179
1180 ;;;
1181 ;;; Helpers for declaring headers
1182 ;;;
1183
1184 ;; emacs: (put 'declare-header! 'scheme-indent-function 1)
1185 ;; emacs: (put 'declare-opaque!-header 'scheme-indent-function 1)
1186 (define (declare-opaque-header! name)
1187 "Declares a given header as \"opaque\", meaning that its value is not
1188 treated specially, and is just returned as a plain string."
1189 (declare-header! name
1190 parse-opaque-string validate-opaque-string write-opaque-string))
1191
1192 ;; emacs: (put 'declare-date-header! 'scheme-indent-function 1)
1193 (define (declare-date-header! name)
1194 (declare-header! name
1195 parse-date date? write-date))
1196
1197 ;; emacs: (put 'declare-string-list-header! 'scheme-indent-function 1)
1198 (define (declare-string-list-header! name)
1199 (declare-header! name
1200 split-and-trim list-of-strings? write-list-of-strings))
1201
1202 ;; emacs: (put 'declare-symbol-list-header! 'scheme-indent-function 1)
1203 (define (declare-symbol-list-header! name)
1204 (declare-header! name
1205 (lambda (str)
1206 (map string->symbol (split-and-trim str)))
1207 (lambda (v)
1208 (list-of? v symbol?))
1209 (lambda (v port)
1210 (write-list v port display ", "))))
1211
1212 ;; emacs: (put 'declare-header-list-header! 'scheme-indent-function 1)
1213 (define (declare-header-list-header! name)
1214 (declare-header! name
1215 split-header-names list-of-header-names? write-header-list))
1216
1217 ;; emacs: (put 'declare-integer-header! 'scheme-indent-function 1)
1218 (define (declare-integer-header! name)
1219 (declare-header! name
1220 parse-non-negative-integer non-negative-integer? display))
1221
1222 ;; emacs: (put 'declare-uri-header! 'scheme-indent-function 1)
1223 (define (declare-uri-header! name)
1224 (declare-header! name
1225 (lambda (str) (or (string->uri str) (bad-header-component 'uri str)))
1226 (@@ (web uri) absolute-uri?)
1227 write-uri))
1228
1229 ;; emacs: (put 'declare-relative-uri-header! 'scheme-indent-function 1)
1230 (define (declare-relative-uri-header! name)
1231 (declare-header! name
1232 (lambda (str)
1233 (or ((@@ (web uri) string->uri*) str)
1234 (bad-header-component 'uri str)))
1235 uri?
1236 write-uri))
1237
1238 ;; emacs: (put 'declare-quality-list-header! 'scheme-indent-function 1)
1239 (define (declare-quality-list-header! name)
1240 (declare-header! name
1241 parse-quality-list validate-quality-list write-quality-list))
1242
1243 ;; emacs: (put 'declare-param-list-header! 'scheme-indent-function 1)
1244 (define* (declare-param-list-header! name #:optional
1245 (val-parser default-val-parser)
1246 (val-validator default-val-validator)
1247 (val-writer default-val-writer))
1248 (declare-header! name
1249 (lambda (str) (parse-param-list str val-parser))
1250 (lambda (val) (validate-param-list val val-validator))
1251 (lambda (val port) (write-param-list val port val-writer))))
1252
1253 ;; emacs: (put 'declare-key-value-list-header! 'scheme-indent-function 1)
1254 (define* (declare-key-value-list-header! name #:optional
1255 (val-parser default-val-parser)
1256 (val-validator default-val-validator)
1257 (val-writer default-val-writer))
1258 (declare-header! name
1259 (lambda (str) (parse-key-value-list str val-parser))
1260 (lambda (val) (key-value-list? val val-validator))
1261 (lambda (val port) (write-key-value-list val port val-writer))))
1262
1263 ;; emacs: (put 'declare-entity-tag-list-header! 'scheme-indent-function 1)
1264 (define (declare-entity-tag-list-header! name)
1265 (declare-header! name
1266 (lambda (str) (if (string=? str "*") '* (parse-entity-tag-list str)))
1267 (lambda (val) (or (eq? val '*) (entity-tag-list? val)))
1268 (lambda (val port)
1269 (if (eq? val '*)
1270 (display "*" port)
1271 (write-entity-tag-list val port)))))
1272
1273 ;; emacs: (put 'declare-credentials-header! 'scheme-indent-function 1)
1274 (define (declare-credentials-header! name)
1275 (declare-header! name
1276 parse-credentials validate-credentials write-credentials))
1277
1278 ;; emacs: (put 'declare-challenge-list-header! 'scheme-indent-function 1)
1279 (define (declare-challenge-list-header! name)
1280 (declare-header! name
1281 parse-challenges validate-challenges write-challenges))
1282
1283
1284 \f
1285
1286 ;;;
1287 ;;; General headers
1288 ;;;
1289
1290 ;; Cache-Control = 1#(cache-directive)
1291 ;; cache-directive = cache-request-directive | cache-response-directive
1292 ;; cache-request-directive =
1293 ;; "no-cache" ; Section 14.9.1
1294 ;; | "no-store" ; Section 14.9.2
1295 ;; | "max-age" "=" delta-seconds ; Section 14.9.3, 14.9.4
1296 ;; | "max-stale" [ "=" delta-seconds ] ; Section 14.9.3
1297 ;; | "min-fresh" "=" delta-seconds ; Section 14.9.3
1298 ;; | "no-transform" ; Section 14.9.5
1299 ;; | "only-if-cached" ; Section 14.9.4
1300 ;; | cache-extension ; Section 14.9.6
1301 ;; cache-response-directive =
1302 ;; "public" ; Section 14.9.1
1303 ;; | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
1304 ;; | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
1305 ;; | "no-store" ; Section 14.9.2
1306 ;; | "no-transform" ; Section 14.9.5
1307 ;; | "must-revalidate" ; Section 14.9.4
1308 ;; | "proxy-revalidate" ; Section 14.9.4
1309 ;; | "max-age" "=" delta-seconds ; Section 14.9.3
1310 ;; | "s-maxage" "=" delta-seconds ; Section 14.9.3
1311 ;; | cache-extension ; Section 14.9.6
1312 ;; cache-extension = token [ "=" ( token | quoted-string ) ]
1313 ;;
1314 (declare-key-value-list-header! "Cache-Control"
1315 (lambda (k v-str)
1316 (case k
1317 ((max-age min-fresh s-maxage)
1318 (parse-non-negative-integer v-str))
1319 ((max-stale)
1320 (and v-str (parse-non-negative-integer v-str)))
1321 ((private no-cache)
1322 (and v-str (split-header-names v-str)))
1323 (else v-str)))
1324 (lambda (k v)
1325 (case k
1326 ((max-age min-fresh s-maxage)
1327 (non-negative-integer? v))
1328 ((max-stale)
1329 (or (not v) (non-negative-integer? v)))
1330 ((private no-cache)
1331 (or (not v) (list-of-header-names? v)))
1332 ((no-store no-transform only-if-cache must-revalidate proxy-revalidate)
1333 (not v))
1334 (else
1335 (or (not v) (string? v)))))
1336 (lambda (k v port)
1337 (cond
1338 ((string? v) (default-val-writer k v port))
1339 ((pair? v)
1340 (display #\" port)
1341 (write-header-list v port)
1342 (display #\" port))
1343 ((integer? v)
1344 (display v port))
1345 (else
1346 (bad-header-component 'cache-control v)))))
1347
1348 ;; Connection = "Connection" ":" 1#(connection-token)
1349 ;; connection-token = token
1350 ;; e.g.
1351 ;; Connection: close, Foo-Header
1352 ;;
1353 (declare-header! "Connection"
1354 split-header-names
1355 list-of-header-names?
1356 (lambda (val port)
1357 (write-list val port
1358 (lambda (x port)
1359 (display (if (eq? x 'close)
1360 "close"
1361 (header->string x))
1362 port))
1363 ", ")))
1364
1365 ;; Date = "Date" ":" HTTP-date
1366 ;; e.g.
1367 ;; Date: Tue, 15 Nov 1994 08:12:31 GMT
1368 ;;
1369 (declare-date-header! "Date")
1370
1371 ;; Pragma = "Pragma" ":" 1#pragma-directive
1372 ;; pragma-directive = "no-cache" | extension-pragma
1373 ;; extension-pragma = token [ "=" ( token | quoted-string ) ]
1374 ;;
1375 (declare-key-value-list-header! "Pragma")
1376
1377 ;; Trailer = "Trailer" ":" 1#field-name
1378 ;;
1379 (declare-header-list-header! "Trailer")
1380
1381 ;; Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
1382 ;;
1383 (declare-param-list-header! "Transfer-Encoding")
1384
1385 ;; Upgrade = "Upgrade" ":" 1#product
1386 ;;
1387 (declare-string-list-header! "Upgrade")
1388
1389 ;; Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
1390 ;; received-protocol = [ protocol-name "/" ] protocol-version
1391 ;; protocol-name = token
1392 ;; protocol-version = token
1393 ;; received-by = ( host [ ":" port ] ) | pseudonym
1394 ;; pseudonym = token
1395 ;;
1396 (declare-header! "Via"
1397 split-and-trim
1398 list-of-strings?
1399 write-list-of-strings
1400 #:multiple? #t)
1401
1402 ;; Warning = "Warning" ":" 1#warning-value
1403 ;;
1404 ;; warning-value = warn-code SP warn-agent SP warn-text
1405 ;; [SP warn-date]
1406 ;;
1407 ;; warn-code = 3DIGIT
1408 ;; warn-agent = ( host [ ":" port ] ) | pseudonym
1409 ;; ; the name or pseudonym of the server adding
1410 ;; ; the Warning header, for use in debugging
1411 ;; warn-text = quoted-string
1412 ;; warn-date = <"> HTTP-date <">
1413 (declare-header! "Warning"
1414 (lambda (str)
1415 (let ((len (string-length str)))
1416 (let lp ((i (skip-whitespace str 0)))
1417 (let* ((idx1 (string-index str #\space i))
1418 (idx2 (string-index str #\space (1+ idx1))))
1419 (if (and idx1 idx2)
1420 (let ((code (parse-non-negative-integer str i idx1))
1421 (agent (substring str (1+ idx1) idx2)))
1422 (call-with-values
1423 (lambda () (parse-qstring str (1+ idx2) #:incremental? #t))
1424 (lambda (text i)
1425 (call-with-values
1426 (lambda ()
1427 (let ((c (and (< i len) (string-ref str i))))
1428 (case c
1429 ((#\space)
1430 ;; we have a date.
1431 (call-with-values
1432 (lambda () (parse-qstring str (1+ i)
1433 #:incremental? #t))
1434 (lambda (date i)
1435 (values text (parse-date date) i))))
1436 (else
1437 (values text #f i)))))
1438 (lambda (text date i)
1439 (let ((w (list code agent text date))
1440 (c (and (< i len) (string-ref str i))))
1441 (case c
1442 ((#f) (list w))
1443 ((#\,) (cons w (lp (skip-whitespace str (1+ i)))))
1444 (else (bad-header 'warning str))))))))))))))
1445 (lambda (val)
1446 (list-of? val
1447 (lambda (elt)
1448 (and (list? elt)
1449 (= (length elt) 4)
1450 (apply (lambda (code host text date)
1451 (and (non-negative-integer? code) (< code 1000)
1452 (string? host)
1453 (string? text)
1454 (or (not date) (date? date))))
1455 elt)))))
1456 (lambda (val port)
1457 (write-list
1458 val port
1459 (lambda (w port)
1460 (apply
1461 (lambda (code host text date)
1462 (display code port)
1463 (display #\space port)
1464 (display host port)
1465 (display #\space port)
1466 (write-qstring text port)
1467 (if date
1468 (begin
1469 (display #\space port)
1470 (write-date date port))))
1471 w))
1472 ", "))
1473 #:multiple? #t)
1474
1475
1476 \f
1477
1478 ;;;
1479 ;;; Entity headers
1480 ;;;
1481
1482 ;; Allow = #Method
1483 ;;
1484 (declare-symbol-list-header! "Allow")
1485
1486 ;; Content-Disposition = disposition-type *( ";" disposition-parm )
1487 ;; disposition-type = "attachment" | disp-extension-token
1488 ;; disposition-parm = filename-parm | disp-extension-parm
1489 ;; filename-parm = "filename" "=" quoted-string
1490 ;; disp-extension-token = token
1491 ;; disp-extension-parm = token "=" ( token | quoted-string )
1492 ;;
1493 (declare-header! "Content-Disposition"
1494 (lambda (str)
1495 (let ((disposition (parse-param-list str default-val-parser)))
1496 ;; Lazily reuse the param list parser.
1497 (unless (and (pair? disposition)
1498 (null? (cdr disposition)))
1499 (bad-header-component 'content-disposition str))
1500 (car disposition)))
1501 (lambda (val)
1502 (and (pair? val)
1503 (symbol? (car val))
1504 (list-of? (cdr val)
1505 (lambda (x)
1506 (and (pair? x) (symbol? (car x)) (string? (cdr x)))))))
1507 (lambda (val port)
1508 (write-param-list (list val) port)))
1509
1510 ;; Content-Encoding = 1#content-coding
1511 ;;
1512 (declare-symbol-list-header! "Content-Encoding")
1513
1514 ;; Content-Language = 1#language-tag
1515 ;;
1516 (declare-string-list-header! "Content-Language")
1517
1518 ;; Content-Length = 1*DIGIT
1519 ;;
1520 (declare-integer-header! "Content-Length")
1521
1522 ;; Content-Location = ( absoluteURI | relativeURI )
1523 ;;
1524 (declare-relative-uri-header! "Content-Location")
1525
1526 ;; Content-MD5 = <base64 of 128 bit MD5 digest as per RFC 1864>
1527 ;;
1528 (declare-opaque-header! "Content-MD5")
1529
1530 ;; Content-Range = content-range-spec
1531 ;; content-range-spec = byte-content-range-spec
1532 ;; byte-content-range-spec = bytes-unit SP
1533 ;; byte-range-resp-spec "/"
1534 ;; ( instance-length | "*" )
1535 ;; byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
1536 ;; | "*"
1537 ;; instance-length = 1*DIGIT
1538 ;;
1539 (declare-header! "Content-Range"
1540 (lambda (str)
1541 (let ((dash (string-index str #\-))
1542 (slash (string-index str #\/)))
1543 (if (and (string-prefix? "bytes " str) slash)
1544 (list 'bytes
1545 (cond
1546 (dash
1547 (cons
1548 (parse-non-negative-integer str 6 dash)
1549 (parse-non-negative-integer str (1+ dash) slash)))
1550 ((string= str "*" 6 slash)
1551 '*)
1552 (else
1553 (bad-header 'content-range str)))
1554 (if (string= str "*" (1+ slash))
1555 '*
1556 (parse-non-negative-integer str (1+ slash))))
1557 (bad-header 'content-range str))))
1558 (lambda (val)
1559 (and (list? val) (= (length val) 3)
1560 (symbol? (car val))
1561 (let ((x (cadr val)))
1562 (or (eq? x '*)
1563 (and (pair? x)
1564 (non-negative-integer? (car x))
1565 (non-negative-integer? (cdr x)))))
1566 (let ((x (caddr val)))
1567 (or (eq? x '*)
1568 (non-negative-integer? x)))))
1569 (lambda (val port)
1570 (display (car val) port)
1571 (display #\space port)
1572 (if (eq? (cadr val) '*)
1573 (display #\* port)
1574 (begin
1575 (display (caadr val) port)
1576 (display #\- port)
1577 (display (caadr val) port)))
1578 (if (eq? (caddr val) '*)
1579 (display #\* port)
1580 (display (caddr val) port))))
1581
1582 ;; Content-Type = media-type
1583 ;;
1584 (declare-header! "Content-Type"
1585 (lambda (str)
1586 (let ((parts (string-split str #\;)))
1587 (cons (parse-media-type (car parts))
1588 (map (lambda (x)
1589 (let ((eq (string-index x #\=)))
1590 (if (and eq (= eq (string-rindex x #\=)))
1591 (cons
1592 (string->symbol
1593 (string-trim x char-set:whitespace 0 eq))
1594 (string-trim-right x char-set:whitespace (1+ eq)))
1595 (bad-header 'content-type str))))
1596 (cdr parts)))))
1597 (lambda (val)
1598 (and (pair? val)
1599 (symbol? (car val))
1600 (list-of? (cdr val)
1601 (lambda (x)
1602 (and (pair? x) (symbol? (car x)) (string? (cdr x)))))))
1603 (lambda (val port)
1604 (display (car val) port)
1605 (if (pair? (cdr val))
1606 (begin
1607 (display ";" port)
1608 (write-list
1609 (cdr val) port
1610 (lambda (pair port)
1611 (display (car pair) port)
1612 (display #\= port)
1613 (display (cdr pair) port))
1614 ";")))))
1615
1616 ;; Expires = HTTP-date
1617 ;;
1618 (define *date-in-the-past* (parse-date "Thu, 01 Jan 1970 00:00:00 GMT"))
1619
1620 (declare-header! "Expires"
1621 (lambda (str)
1622 (if (member str '("0" "-1"))
1623 *date-in-the-past*
1624 (parse-date str)))
1625 date?
1626 write-date)
1627
1628 ;; Last-Modified = HTTP-date
1629 ;;
1630 (declare-date-header! "Last-Modified")
1631
1632
1633 \f
1634
1635 ;;;
1636 ;;; Request headers
1637 ;;;
1638
1639 ;; Accept = #( media-range [ accept-params ] )
1640 ;; media-range = ( "*/*" | ( type "/" "*" ) | ( type "/" subtype ) )
1641 ;; *( ";" parameter )
1642 ;; accept-params = ";" "q" "=" qvalue *( accept-extension )
1643 ;; accept-extension = ";" token [ "=" ( token | quoted-string ) ]
1644 ;;
1645 (declare-param-list-header! "Accept"
1646 ;; -> (type/subtype (sym-prop . str-val) ...) ...)
1647 ;;
1648 ;; with the exception of prop `q', in which case the val will be a
1649 ;; valid quality value
1650 ;;
1651 (lambda (k v)
1652 (if (eq? k 'q)
1653 (parse-quality v)
1654 v))
1655 (lambda (k v)
1656 (if (eq? k 'q)
1657 (valid-quality? v)
1658 (or (not v) (string? v))))
1659 (lambda (k v port)
1660 (if (eq? k 'q)
1661 (write-quality v port)
1662 (default-val-writer k v port))))
1663
1664 ;; Accept-Charset = 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] )
1665 ;;
1666 (declare-quality-list-header! "Accept-Charset")
1667
1668 ;; Accept-Encoding = 1#( codings [ ";" "q" "=" qvalue ] )
1669 ;; codings = ( content-coding | "*" )
1670 ;;
1671 (declare-quality-list-header! "Accept-Encoding")
1672
1673 ;; Accept-Language = 1#( language-range [ ";" "q" "=" qvalue ] )
1674 ;; language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
1675 ;;
1676 (declare-quality-list-header! "Accept-Language")
1677
1678 ;; Authorization = credentials
1679 ;; credentials = auth-scheme #auth-param
1680 ;; auth-scheme = token
1681 ;; auth-param = token "=" ( token | quoted-string )
1682 ;;
1683 (declare-credentials-header! "Authorization")
1684
1685 ;; Expect = 1#expectation
1686 ;; expectation = "100-continue" | expectation-extension
1687 ;; expectation-extension = token [ "=" ( token | quoted-string )
1688 ;; *expect-params ]
1689 ;; expect-params = ";" token [ "=" ( token | quoted-string ) ]
1690 ;;
1691 (declare-param-list-header! "Expect")
1692
1693 ;; From = mailbox
1694 ;;
1695 ;; Should be an email address; we just pass on the string as-is.
1696 ;;
1697 (declare-opaque-header! "From")
1698
1699 ;; Host = host [ ":" port ]
1700 ;;
1701 (declare-header! "Host"
1702 (lambda (str)
1703 (let* ((rbracket (string-index str #\]))
1704 (colon (string-index str #\: (or rbracket 0)))
1705 (host (cond
1706 (rbracket
1707 (unless (eqv? (string-ref str 0) #\[)
1708 (bad-header 'host str))
1709 (substring str 1 rbracket))
1710 (colon
1711 (substring str 0 colon))
1712 (else
1713 str)))
1714 (port (and colon
1715 (parse-non-negative-integer str (1+ colon)))))
1716 (cons host port)))
1717 (lambda (val)
1718 (and (pair? val)
1719 (string? (car val))
1720 (or (not (cdr val))
1721 (non-negative-integer? (cdr val)))))
1722 (lambda (val port)
1723 (if (string-index (car val) #\:)
1724 (begin
1725 (display #\[ port)
1726 (display (car val) port)
1727 (display #\] port))
1728 (display (car val) port))
1729 (if (cdr val)
1730 (begin
1731 (display #\: port)
1732 (display (cdr val) port)))))
1733
1734 ;; If-Match = ( "*" | 1#entity-tag )
1735 ;;
1736 (declare-entity-tag-list-header! "If-Match")
1737
1738 ;; If-Modified-Since = HTTP-date
1739 ;;
1740 (declare-date-header! "If-Modified-Since")
1741
1742 ;; If-None-Match = ( "*" | 1#entity-tag )
1743 ;;
1744 (declare-entity-tag-list-header! "If-None-Match")
1745
1746 ;; If-Range = ( entity-tag | HTTP-date )
1747 ;;
1748 (declare-header! "If-Range"
1749 (lambda (str)
1750 (if (or (string-prefix? "\"" str)
1751 (string-prefix? "W/" str))
1752 (parse-entity-tag str)
1753 (parse-date str)))
1754 (lambda (val)
1755 (or (date? val) (entity-tag? val)))
1756 (lambda (val port)
1757 (if (date? val)
1758 (write-date val port)
1759 (write-entity-tag val port))))
1760
1761 ;; If-Unmodified-Since = HTTP-date
1762 ;;
1763 (declare-date-header! "If-Unmodified-Since")
1764
1765 ;; Max-Forwards = 1*DIGIT
1766 ;;
1767 (declare-integer-header! "Max-Forwards")
1768
1769 ;; Proxy-Authorization = credentials
1770 ;;
1771 (declare-credentials-header! "Proxy-Authorization")
1772
1773 ;; Range = "Range" ":" ranges-specifier
1774 ;; ranges-specifier = byte-ranges-specifier
1775 ;; byte-ranges-specifier = bytes-unit "=" byte-range-set
1776 ;; byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
1777 ;; byte-range-spec = first-byte-pos "-" [last-byte-pos]
1778 ;; first-byte-pos = 1*DIGIT
1779 ;; last-byte-pos = 1*DIGIT
1780 ;; suffix-byte-range-spec = "-" suffix-length
1781 ;; suffix-length = 1*DIGIT
1782 ;;
1783 (declare-header! "Range"
1784 (lambda (str)
1785 (if (string-prefix? "bytes=" str)
1786 (cons
1787 'bytes
1788 (map (lambda (x)
1789 (let ((dash (string-index x #\-)))
1790 (cond
1791 ((not dash)
1792 (bad-header 'range str))
1793 ((zero? dash)
1794 (cons #f (parse-non-negative-integer x 1)))
1795 ((= dash (1- (string-length x)))
1796 (cons (parse-non-negative-integer x 0 dash) #f))
1797 (else
1798 (cons (parse-non-negative-integer x 0 dash)
1799 (parse-non-negative-integer x (1+ dash)))))))
1800 (string-split (substring str 6) #\,)))
1801 (bad-header 'range str)))
1802 (lambda (val)
1803 (and (pair? val)
1804 (symbol? (car val))
1805 (list-of? (cdr val)
1806 (lambda (elt)
1807 (and (pair? elt)
1808 (let ((x (car elt)) (y (cdr elt)))
1809 (and (or x y)
1810 (or (not x) (non-negative-integer? x))
1811 (or (not y) (non-negative-integer? y)))))))))
1812 (lambda (val port)
1813 (display (car val) port)
1814 (display #\= port)
1815 (write-list
1816 (cdr val) port
1817 (lambda (pair port)
1818 (if (car pair)
1819 (display (car pair) port))
1820 (display #\- port)
1821 (if (cdr pair)
1822 (display (cdr pair) port)))
1823 ",")))
1824
1825 ;; Referer = ( absoluteURI | relativeURI )
1826 ;;
1827 (declare-relative-uri-header! "Referer")
1828
1829 ;; TE = #( t-codings )
1830 ;; t-codings = "trailers" | ( transfer-extension [ accept-params ] )
1831 ;;
1832 (declare-param-list-header! "TE")
1833
1834 ;; User-Agent = 1*( product | comment )
1835 ;;
1836 (declare-opaque-header! "User-Agent")
1837
1838
1839 \f
1840
1841 ;;;
1842 ;;; Reponse headers
1843 ;;;
1844
1845 ;; Accept-Ranges = acceptable-ranges
1846 ;; acceptable-ranges = 1#range-unit | "none"
1847 ;;
1848 (declare-symbol-list-header! "Accept-Ranges")
1849
1850 ;; Age = age-value
1851 ;; age-value = delta-seconds
1852 ;;
1853 (declare-integer-header! "Age")
1854
1855 ;; ETag = entity-tag
1856 ;;
1857 (declare-header! "ETag"
1858 parse-entity-tag
1859 entity-tag?
1860 write-entity-tag)
1861
1862 ;; Location = absoluteURI
1863 ;;
1864 (declare-uri-header! "Location")
1865
1866 ;; Proxy-Authenticate = 1#challenge
1867 ;;
1868 (declare-challenge-list-header! "Proxy-Authenticate")
1869
1870 ;; Retry-After = ( HTTP-date | delta-seconds )
1871 ;;
1872 (declare-header! "Retry-After"
1873 (lambda (str)
1874 (if (and (not (string-null? str))
1875 (char-numeric? (string-ref str 0)))
1876 (parse-non-negative-integer str)
1877 (parse-date str)))
1878 (lambda (val)
1879 (or (date? val) (non-negative-integer? val)))
1880 (lambda (val port)
1881 (if (date? val)
1882 (write-date val port)
1883 (display val port))))
1884
1885 ;; Server = 1*( product | comment )
1886 ;;
1887 (declare-opaque-header! "Server")
1888
1889 ;; Vary = ( "*" | 1#field-name )
1890 ;;
1891 (declare-header! "Vary"
1892 (lambda (str)
1893 (if (equal? str "*")
1894 '*
1895 (split-header-names str)))
1896 (lambda (val)
1897 (or (eq? val '*) (list-of-header-names? val)))
1898 (lambda (val port)
1899 (if (eq? val '*)
1900 (display "*" port)
1901 (write-header-list val port))))
1902
1903 ;; WWW-Authenticate = 1#challenge
1904 ;;
1905 (declare-challenge-list-header! "WWW-Authenticate")
1906
1907
1908 ;; Chunked Responses
1909 (define (read-chunk-header port)
1910 (let* ((str (read-line port))
1911 (extension-start (string-index str (lambda (c) (or (char=? c #\;)
1912 (char=? c #\return)))))
1913 (size (string->number (if extension-start ; unnecessary?
1914 (substring str 0 extension-start)
1915 str)
1916 16)))
1917 size))
1918
1919 (define (read-chunk port)
1920 (let ((size (read-chunk-header port)))
1921 (read-chunk-body port size)))
1922
1923 (define (read-chunk-body port size)
1924 (let ((bv (get-bytevector-n port size)))
1925 (get-u8 port) ; CR
1926 (get-u8 port) ; LF
1927 bv))
1928
1929 (define* (make-chunked-input-port port #:key (keep-alive? #f))
1930 "Returns a new port which translates HTTP chunked transfer encoded
1931 data from PORT into a non-encoded format. Returns eof when it has
1932 read the final chunk from PORT. This does not necessarily mean
1933 that there is no more data on PORT. When the returned port is
1934 closed it will also close PORT, unless the KEEP-ALIVE? is true."
1935 (define (next-chunk)
1936 (read-chunk port))
1937 (define finished? #f)
1938 (define (close)
1939 (unless keep-alive?
1940 (close-port port)))
1941 (define buffer #vu8())
1942 (define buffer-size 0)
1943 (define buffer-pointer 0)
1944 (define (read! bv idx to-read)
1945 (define (loop to-read num-read)
1946 (cond ((or finished? (zero? to-read))
1947 num-read)
1948 ((<= to-read (- buffer-size buffer-pointer))
1949 (bytevector-copy! buffer buffer-pointer
1950 bv (+ idx num-read)
1951 to-read)
1952 (set! buffer-pointer (+ buffer-pointer to-read))
1953 (loop 0 (+ num-read to-read)))
1954 (else
1955 (let ((n (- buffer-size buffer-pointer)))
1956 (bytevector-copy! buffer buffer-pointer
1957 bv (+ idx num-read)
1958 n)
1959 (set! buffer (next-chunk))
1960 (set! buffer-pointer 0)
1961 (set! buffer-size (bytevector-length buffer))
1962 (set! finished? (= buffer-size 0))
1963 (loop (- to-read n)
1964 (+ num-read n))))))
1965 (loop to-read 0))
1966 (make-custom-binary-input-port "chunked input port" read! #f #f close))
1967
1968 (define* (make-chunked-output-port port #:key (keep-alive? #f))
1969 "Returns a new port which translates non-encoded data into a HTTP
1970 chunked transfer encoded data and writes this to PORT. Data
1971 written to this port is buffered until the port is flushed, at which
1972 point it is all sent as one chunk. Take care to close the port when
1973 done, as it will output the remaining data, and encode the final zero
1974 chunk. When the port is closed it will also close PORT, unless
1975 KEEP-ALIVE? is true."
1976 (define (q-for-each f q)
1977 (while (not (q-empty? q))
1978 (f (deq! q))))
1979 (define queue (make-q))
1980 (define (put-char c)
1981 (enq! queue c))
1982 (define (put-string s)
1983 (string-for-each (lambda (c) (enq! queue c))
1984 s))
1985 (define (flush)
1986 ;; It is important that we do _not_ write a chunk if the queue is
1987 ;; empty, since it will be treated as the final chunk.
1988 (unless (q-empty? queue)
1989 (let ((len (q-length queue)))
1990 (display (number->string len 16) port)
1991 (display "\r\n" port)
1992 (q-for-each (lambda (elem) (write-char elem port))
1993 queue)
1994 (display "\r\n" port))))
1995 (define (close)
1996 (flush)
1997 (display "0\r\n" port)
1998 (force-output port)
1999 (unless keep-alive?
2000 (close-port port)))
2001 (make-soft-port (vector put-char put-string flush #f close) "w"))
2002
2003 (define %http-proxy-port? (make-object-property))
2004 (define (http-proxy-port? port) (%http-proxy-port? port))
2005 (define (set-http-proxy-port?! port flag)
2006 (set! (%http-proxy-port? port) flag))