summaryrefslogtreecommitdiff
path: root/module
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2017-02-08 08:45:42 +0100
committerAndy Wingo <wingo@pobox.com>2017-02-08 15:09:14 +0100
commit96b994b6f815747ce2548123cc996d8132bd4781 (patch)
tree076ef471a99e2777333d1c5d337584702cc257a7 /module
parent8c50060ae94c60ec5b0f6d506bb5a8205a18d4bb (diff)
Beginnings of suspendable HTTP
* module/web/http.scm: Use put-string and other routines from (ice-9 textual-ports) in preference to `display'. The goal is for these operations to be suspendable.
Diffstat (limited to 'module')
-rw-r--r--module/web/http.scm280
1 files changed, 146 insertions, 134 deletions
diff --git a/module/web/http.scm b/module/web/http.scm
index 57c209599..c3fbf6f41 100644
--- a/module/web/http.scm
+++ b/module/web/http.scm
@@ -37,6 +37,7 @@
#:use-module (ice-9 match)
#:use-module (ice-9 q)
#:use-module (ice-9 binary-ports)
+ #:use-module (ice-9 textual-ports)
#:use-module (rnrs bytevectors)
#:use-module (web uri)
#:export (string->header
@@ -73,6 +74,12 @@
set-http-proxy-port?!))
+(define (put-symbol port sym)
+ (put-string port (symbol->string sym)))
+
+(define (put-non-negative-integer port i)
+ (put-string port (number->string i)))
+
(define (string->header name)
"Parse NAME to a symbolic header name."
(string->symbol (string-downcase name)))
@@ -205,10 +212,10 @@ header with name SYM."
(define (write-header sym val port)
"Write the given header name and value to PORT, using the writer
from ‘header-writer’."
- (display (header->string sym) port)
- (display ": " port)
+ (put-string port (header->string sym))
+ (put-string port ": ")
((header-writer sym) val port)
- (display "\r\n" port))
+ (put-string port "\r\n"))
(define (read-headers port)
"Read the headers of an HTTP message from PORT, returning them
@@ -263,7 +270,7 @@ as an ordered alist."
(define (validate-opaque-string val)
(string? val))
(define (write-opaque-string val port)
- (display val port))
+ (put-string port val))
(define separators-without-slash
(string->char-set "[^][()<>@,;:\\\"?= \t]"))
@@ -312,7 +319,7 @@ as an ordered alist."
(define (write-header-list val port)
(write-list val port
(lambda (x port)
- (display (header->string x) port))
+ (put-string port (header->string x)))
", "))
(define (collect-escaped-string from start len escapes)
@@ -359,17 +366,17 @@ as an ordered alist."
(match items
(() (values))
((item . items)
- (display delim port)
+ (put-string port delim)
(write-item item port)
(lp items)))))))
(define (write-qstring str port)
- (display #\" port)
+ (put-char port #\")
(if (string-index str #\")
;; optimize me
(write-list (string-split str #\") port display "\\\"")
- (display str port))
- (display #\" port))
+ (put-string port str))
+ (put-char port #\"))
(define* (parse-quality str #:optional (start 0) (end (string-length str)))
(define (char->decimal c)
@@ -422,11 +429,11 @@ as an ordered alist."
(define (write-quality q port)
(define (digit->char d)
(integer->char (+ (char->integer #\0) d)))
- (display (digit->char (modulo (quotient q 1000) 10)) port)
- (display #\. port)
- (display (digit->char (modulo (quotient q 100) 10)) port)
- (display (digit->char (modulo (quotient q 10) 10)) port)
- (display (digit->char (modulo q 10)) port))
+ (put-char port (digit->char (modulo (quotient q 1000) 10)))
+ (put-char port #\.)
+ (put-char port (digit->char (modulo (quotient q 100) 10)))
+ (put-char port (digit->char (modulo (quotient q 10) 10)))
+ (put-char port (digit->char (modulo q 10))))
(define (list-of? val pred)
(match val
@@ -457,9 +464,9 @@ as an ordered alist."
(lambda (x port)
(let ((q (car x))
(str (cdr x)))
- (display str port)
+ (put-string port str)
(when (< q 1000)
- (display ";q=" port)
+ (put-string port ";q=")
(write-quality q port))))
","))
@@ -492,7 +499,7 @@ as an ordered alist."
(string-index val #\,)
(string-index val #\"))
(write-qstring val port)
- (display val port)))
+ (put-string port val)))
(define* (parse-key-value-list str #:optional
(val-parser default-val-parser)
@@ -542,13 +549,13 @@ as an ordered alist."
(lambda (x port)
(match x
((k . #f)
- (display (symbol->string k) port))
+ (put-symbol port k))
((k . v)
- (display (symbol->string k) port)
- (display #\= port)
+ (put-symbol port k)
+ (put-char port #\=)
(val-writer k v port))
(k
- (display (symbol->string k) port))))
+ (put-symbol port k))))
delim))
;; param-component = token [ "=" (token | quoted-string) ] \
@@ -837,33 +844,33 @@ as an ordered alist."
(define zero (char->integer #\0))
(let lp ((tens (expt 10 (1- digits))))
(when (> tens 0)
- (display (integer->char (+ zero (modulo (truncate/ n tens) 10)))
- port)
+ (put-char port
+ (integer->char (+ zero (modulo (truncate/ n tens) 10))))
(lp (floor/ tens 10)))))
(let ((date (if (zero? (date-zone-offset date))
date
(time-tai->date (date->time-tai date) 0))))
- (display (case (date-week-day date)
- ((0) "Sun, ") ((1) "Mon, ") ((2) "Tue, ")
- ((3) "Wed, ") ((4) "Thu, ") ((5) "Fri, ")
- ((6) "Sat, ") (else (error "bad date" date)))
- port)
+ (put-string port
+ (case (date-week-day date)
+ ((0) "Sun, ") ((1) "Mon, ") ((2) "Tue, ")
+ ((3) "Wed, ") ((4) "Thu, ") ((5) "Fri, ")
+ ((6) "Sat, ") (else (error "bad date" date))))
(display-digits (date-day date) 2 port)
- (display (case (date-month date)
- ((1) " Jan ") ((2) " Feb ") ((3) " Mar ")
- ((4) " Apr ") ((5) " May ") ((6) " Jun ")
- ((7) " Jul ") ((8) " Aug ") ((9) " Sep ")
- ((10) " Oct ") ((11) " Nov ") ((12) " Dec ")
- (else (error "bad date" date)))
- port)
+ (put-string port
+ (case (date-month date)
+ ((1) " Jan ") ((2) " Feb ") ((3) " Mar ")
+ ((4) " Apr ") ((5) " May ") ((6) " Jun ")
+ ((7) " Jul ") ((8) " Aug ") ((9) " Sep ")
+ ((10) " Oct ") ((11) " Nov ") ((12) " Dec ")
+ (else (error "bad date" date))))
(display-digits (date-year date) 4 port)
- (display #\space port)
+ (put-char port #\space)
(display-digits (date-hour date) 2 port)
- (display #\: port)
+ (put-char port #\:)
(display-digits (date-minute date) 2 port)
- (display #\: port)
+ (put-char port #\:)
(display-digits (date-second date) 2 port)
- (display " GMT" port)))
+ (put-string port " GMT")))
;; Following https://tools.ietf.org/html/rfc7232#section-2.3, an entity
;; tag should really be a qstring. However there are a number of
@@ -899,7 +906,7 @@ as an ordered alist."
(define (write-entity-tag val port)
(match val
((tag . strong?)
- (unless strong? (display "W/" port))
+ (unless strong? (put-string port "W/"))
(write-qstring tag port))))
(define* (parse-entity-tag-list val #:optional
@@ -955,11 +962,14 @@ as an ordered alist."
(_ #f)))
(define (write-credentials val port)
- (display (car val) port)
- (display #\space port)
- (case (car val)
- ((basic) (display (cdr val) port))
- (else (write-key-value-list (cdr val) port))))
+ (match val
+ (('basic . cred)
+ (put-string port "basic ")
+ (put-string port cred))
+ ((scheme . params)
+ (put-symbol port scheme)
+ (put-char port #\space)
+ (write-key-value-list params port))))
;; challenges = 1#challenge
;; challenge = auth-scheme 1*SP 1#auth-param
@@ -1021,9 +1031,11 @@ as an ordered alist."
(_ #f)))
(define (write-challenge val port)
- (display (car val) port)
- (display #\space port)
- (write-key-value-list (cdr val) port))
+ (match val
+ ((scheme . params)
+ (put-symbol port scheme)
+ (put-char port #\space)
+ (write-key-value-list params port))))
(define (write-challenges val port)
(write-list val port write-challenge ", "))
@@ -1065,10 +1077,10 @@ pair. For example, ‘HTTP/1.1’ parses as the pair of integers,
(define (write-http-version val port)
"Write the given major-minor version pair to PORT."
- (display "HTTP/" port)
- (display (car val) port)
- (display #\. port)
- (display (cdr val) port))
+ (put-string port "HTTP/")
+ (put-non-negative-integer port (car val))
+ (put-char port #\.)
+ (put-non-negative-integer port (cdr val)))
(for-each
(lambda (v)
@@ -1132,17 +1144,17 @@ three values: the method, the URI, and the version."
(define (write-uri uri port)
(when (uri-host uri)
(when (uri-scheme uri)
- (display (uri-scheme uri) port)
- (display #\: port))
- (display "//" port)
+ (put-symbol port (uri-scheme uri))
+ (put-char port #\:))
+ (put-string port "//")
(when (uri-userinfo uri)
- (display (uri-userinfo uri) port)
- (display #\@ port))
- (display (uri-host uri) port)
+ (put-string port (uri-userinfo uri))
+ (put-char port #\@))
+ (put-string port (uri-host uri))
(let ((p (uri-port uri)))
(when (and p (not (eqv? p 80)))
- (display #\: port)
- (display p port))))
+ (put-char port #\:)
+ (put-non-negative-integer port p))))
(let* ((path (uri-path uri))
(len (string-length path)))
(cond
@@ -1151,43 +1163,43 @@ three values: the method, the URI, and the version."
((and (zero? len) (not (uri-host uri)))
(bad-request "Empty path and no host for URI: ~s" uri))
(else
- (display path port))))
+ (put-string port path))))
(when (uri-query uri)
- (display #\? port)
- (display (uri-query uri) port)))
+ (put-char port #\?)
+ (put-string port (uri-query uri))))
(define (write-request-line method uri version port)
"Write the first line of an HTTP request to PORT."
- (display method port)
- (display #\space port)
+ (put-symbol port method)
+ (put-char port #\space)
(when (http-proxy-port? port)
(let ((scheme (uri-scheme uri))
(host (uri-host uri))
(host-port (uri-port uri)))
(when (and scheme host)
- (display scheme port)
- (display "://" port)
+ (put-symbol port scheme)
+ (put-string port "://")
(cond
- ((string-index host #\:)
- (display #\[ port)
- (display host port)
- (display #\] port))
+ ((host string-index #\:)
+ (put-char #\[ port)
+ (put-string port host
+ (put-char port #\])))
(else
- (display host port)))
+ (put-string port host)))
(unless ((@@ (web uri) default-port?) scheme host-port)
- (display #\: port)
- (display host-port port)))))
+ (put-char port #\:)
+ (put-non-negative-integer port host-port)))))
(let ((path (uri-path uri))
(query (uri-query uri)))
(if (string-null? path)
- (display "/" port)
- (display path port))
+ (put-string port "/")
+ (put-string port path))
(when query
- (display "?" port)
- (display query port)))
- (display #\space port)
+ (put-string port "?")
+ (put-string port query)))
+ (put-char port #\space)
(write-http-version version port)
- (display "\r\n" port))
+ (put-string port "\r\n"))
(define (read-response-line port)
"Read the first line of an HTTP response from PORT, returning three
@@ -1207,11 +1219,11 @@ values: the HTTP version, the response code, and the (possibly empty)
(define (write-response-line version code reason-phrase port)
"Write the first line of an HTTP response to PORT."
(write-http-version version port)
- (display #\space port)
- (display code port)
- (display #\space port)
- (display reason-phrase port)
- (display "\r\n" port))
+ (put-char port #\space)
+ (put-non-negative-integer port code)
+ (put-char port #\space)
+ (put-string port reason-phrase)
+ (put-string port "\r\n"))
@@ -1306,7 +1318,7 @@ treated specially, and is just returned as a plain string."
(lambda (val) (or (eq? val '*) (entity-tag-list? val)))
(lambda (val port)
(if (eq? val '*)
- (display "*" port)
+ (put-string port "*")
(write-entity-tag-list val port)))))
;; emacs: (put 'declare-credentials-header! 'scheme-indent-function 1)
@@ -1376,11 +1388,11 @@ treated specially, and is just returned as a plain string."
(cond
((string? v) (default-val-writer k v port))
((pair? v)
- (display #\" port)
+ (put-char port #\")
(write-header-list v port)
- (display #\" port))
+ (put-char port #\"))
((integer? v)
- (display v port))
+ (put-non-negative-integer port v))
(else
(bad-header-component 'cache-control v)))))
@@ -1395,10 +1407,10 @@ treated specially, and is just returned as a plain string."
(lambda (val port)
(write-list val port
(lambda (x port)
- (display (if (eq? x 'close)
- "close"
- (header->string x))
- port))
+ (put-string port
+ (if (eq? x 'close)
+ "close"
+ (header->string x))))
", ")))
;; Date = "Date" ":" HTTP-date
@@ -1497,16 +1509,16 @@ treated specially, and is just returned as a plain string."
(lambda (w port)
(match w
((code host text date)
- (display code port)
- (display #\space port)
- (display host port)
- (display #\space port)
+ (put-non-negative-integer port code)
+ (put-char port #\space)
+ (put-string port host)
+ (put-char port #\space)
(write-qstring text port)
(when date
- (display #\space port)
- (display #\" port)
+ (put-char port #\space)
+ (put-char port #\")
(write-date date port)
- (display #\" port)))))
+ (put-char port #\")))))
", "))
#:multiple? #t)
@@ -1599,19 +1611,19 @@ treated specially, and is just returned as a plain string."
(lambda (val port)
(match val
((unit range instance-length)
- (display unit port)
- (display #\space port)
+ (put-symbol port unit)
+ (put-char port #\space)
(match range
('*
- (display #\* port))
+ (put-char port #\*))
((start . end)
- (display start port)
- (display #\- port)
- (display end port)))
- (display #\/ port)
+ (put-non-negative-integer port start)
+ (put-char port #\-)
+ (put-non-negative-integer port end)))
+ (put-char port #\/)
(match instance-length
- ('* (display #\* port))
- (len (display len port)))))))
+ ('* (put-char port #\*))
+ (len (put-non-negative-integer port len)))))))
;; Content-Type = media-type
;;
@@ -1635,19 +1647,19 @@ treated specially, and is just returned as a plain string."
(lambda (val port)
(match val
((type . args)
- (display type port)
+ (put-symbol port type)
(match args
(() (values))
(args
- (display ";" port)
+ (put-string port ";")
(write-list
args port
(lambda (pair port)
(match pair
((k . v)
- (display k port)
- (display #\= port)
- (display v port))))
+ (put-symbol port k)
+ (put-char port #\=)
+ (put-string port v))))
";")))))))
;; Expires = HTTP-date
@@ -1760,14 +1772,14 @@ treated specially, and is just returned as a plain string."
((host-name . host-port)
(cond
((string-index host-name #\:)
- (display #\[ port)
- (display host-name port)
- (display #\] port))
+ (put-char port #\[)
+ (put-string port host-name)
+ (put-char port #\]))
(else
- (display host-name port)))
+ (put-string port host-name)))
(when host-port
- (display #\: port)
- (display host-port port))))))
+ (put-char port #\:)
+ (put-non-negative-integer port host-port))))))
;; If-Match = ( "*" | 1#entity-tag )
;;
@@ -1848,16 +1860,16 @@ treated specially, and is just returned as a plain string."
(lambda (val port)
(match val
((unit . ranges)
- (display unit port)
- (display #\= port)
+ (put-symbol port unit)
+ (put-char port #\=)
(write-list
ranges port
(lambda (range port)
(match range
((start . end)
- (when start (display start port))
- (display #\- port)
- (when end (display end port)))))
+ (when start (put-non-negative-integer port start))
+ (put-char port #\-)
+ (when end (put-non-negative-integer port end)))))
",")))))
;; Referer = URI-reference
@@ -1922,7 +1934,7 @@ treated specially, and is just returned as a plain string."
(lambda (val port)
(if (date? val)
(write-date val port)
- (display val port))))
+ (put-non-negative-integer port val))))
;; Server = 1*( product | comment )
;;
@@ -1939,7 +1951,7 @@ treated specially, and is just returned as a plain string."
(or (eq? val '*) (list-of-header-names? val)))
(lambda (val port)
(if (eq? val '*)
- (display "*" port)
+ (put-string port "*")
(write-header-list val port))))
;; WWW-Authenticate = 1#challenge
@@ -2027,9 +2039,9 @@ KEEP-ALIVE? is true."
(while (not (q-empty? q))
(f (deq! q))))
(define queue (make-q))
- (define (put-char c)
+ (define (%put-char c)
(enq! queue c))
- (define (put-string s)
+ (define (%put-string s)
(string-for-each (lambda (c) (enq! queue c))
s))
(define (flush)
@@ -2037,18 +2049,18 @@ KEEP-ALIVE? is true."
;; empty, since it will be treated as the final chunk.
(unless (q-empty? queue)
(let ((len (q-length queue)))
- (display (number->string len 16) port)
- (display "\r\n" port)
+ (put-string port (number->string len 16))
+ (put-string port "\r\n")
(q-for-each (lambda (elem) (write-char elem port))
queue)
- (display "\r\n" port))))
+ (put-string port "\r\n"))))
(define (close)
(flush)
- (display "0\r\n" port)
+ (put-string port "0\r\n")
(force-output port)
(unless keep-alive?
(close-port port)))
- (let ((ret (make-soft-port (vector put-char put-string flush #f close) "w")))
+ (let ((ret (make-soft-port (vector %put-char %put-string flush #f close) "w")))
(setvbuf ret 'block buffering)
ret))