doc: Use a preferred naming convention in SRFI-9 examples.
[bpt/guile.git] / test-suite / tests / web-http.test
CommitLineData
440840c1
AW
1;;;; web-uri.test --- URI library -*- mode: scheme; coding: utf-8; -*-
2;;;;
be1be3e5 3;;;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
440840c1
AW
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 02110-1301 USA
18
19
20(define-module (test-suite web-http)
21 #:use-module (web uri)
22 #:use-module (web http)
312e79f8 23 #:use-module (rnrs io ports)
440840c1
AW
24 #:use-module (ice-9 regex)
25 #:use-module (ice-9 control)
26 #:use-module (srfi srfi-19)
27 #:use-module (test-suite lib))
28
29
30(define-syntax pass-if-named-exception
31 (syntax-rules ()
32 ((_ name k pat exp)
33 (pass-if name
34 (catch 'k
35 (lambda () exp (error "expected exception" 'k))
36 (lambda (k message args)
37 (if (string-match pat message)
38 #t
39 (error "unexpected exception" message args))))))))
40
41(define-syntax pass-if-parse
42 (syntax-rules ()
43 ((_ sym str val)
44 (pass-if (format #f "~a: ~s -> ~s" 'sym str val)
4855c634
DH
45 (and (equal? (parse-header 'sym str)
46 val)
47 (valid-header? 'sym val))))))
440840c1 48
61fe8eaf
AW
49(define-syntax pass-if-round-trip
50 (syntax-rules ()
51 ((_ str)
52 (pass-if (format #f "~s round trip" str)
53 (equal? (call-with-output-string
54 (lambda (port)
55 (call-with-values
56 (lambda () (read-header (open-input-string str)))
57 (lambda (sym val)
58 (write-header sym val port)))))
59 str)))))
60
440840c1
AW
61(define-syntax pass-if-any-error
62 (syntax-rules ()
63 ((_ sym str)
64 (pass-if (format #f "~a: ~s -> any error" 'sym str)
65 (% (catch #t
66 (lambda ()
be1be3e5 67 (parse-header 'sym str)
440840c1
AW
68 (abort (lambda () (error "expected exception"))))
69 (lambda (k . args)
70 #t))
71 (lambda (k thunk)
72 (thunk)))))))
73
74(define-syntax pass-if-parse-error
75 (syntax-rules ()
76 ((_ sym str expected-component)
77 (pass-if (format #f "~a: ~s -> ~a error" 'sym str 'expected-component)
78 (catch 'bad-header
79 (lambda ()
be1be3e5 80 (parse-header 'sym str)
440840c1
AW
81 (error "expected exception" 'expected-component))
82 (lambda (k component arg)
83 (if (or (not 'expected-component)
84 (eq? 'expected-component component))
85 #t
86 (error "unexpected exception" component arg))))))))
87
2e08ff38
DH
88(define-syntax pass-if-read-request-line
89 (syntax-rules ()
90 ((_ str expected-method expected-uri expected-version)
91 (pass-if str
92 (equal? (call-with-values
93 (lambda ()
94 (read-request-line (open-input-string
95 (string-append str "\r\n"))))
96 list)
97 (list 'expected-method
98 expected-uri
99 'expected-version))))))
100
101(define-syntax pass-if-write-request-line
102 (syntax-rules ()
103 ((_ expected-str method uri version)
104 (pass-if expected-str
105 (equal? (string-append expected-str "\r\n")
106 (call-with-output-string
107 (lambda (port)
108 (write-request-line 'method uri 'version port))))))))
109
110(define-syntax pass-if-read-response-line
111 (syntax-rules ()
112 ((_ str expected-version expected-code expected-phrase)
113 (pass-if str
114 (equal? (call-with-values
115 (lambda ()
116 (read-response-line (open-input-string
117 (string-append str "\r\n"))))
118 list)
119 (list 'expected-version
120 expected-code
121 expected-phrase))))))
122
123(define-syntax pass-if-write-response-line
124 (syntax-rules ()
125 ((_ expected-str version code phrase)
126 (pass-if expected-str
127 (equal? (string-append expected-str "\r\n")
128 (call-with-output-string
129 (lambda (port)
130 (write-response-line 'version code phrase port))))))))
131
132(with-test-prefix "read-request-line"
133 (pass-if-read-request-line "GET / HTTP/1.1"
134 GET
135 (build-uri 'http
136 #:path "/")
137 (1 . 1))
138 (pass-if-read-request-line "GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1"
139 GET
140 (build-uri 'http
141 #:host "www.w3.org"
142 #:path "/pub/WWW/TheProject.html")
143 (1 . 1))
144 (pass-if-read-request-line "GET /pub/WWW/TheProject.html HTTP/1.1"
145 GET
146 (build-uri 'http
147 #:path "/pub/WWW/TheProject.html")
148 (1 . 1))
149 (pass-if-read-request-line "HEAD /etc/hosts?foo=bar HTTP/1.1"
150 HEAD
151 (build-uri 'http
152 #:path "/etc/hosts"
153 #:query "foo=bar")
154 (1 . 1)))
155
156(with-test-prefix "write-request-line"
157 (pass-if-write-request-line "GET / HTTP/1.1"
158 GET
159 (build-uri 'http
160 #:path "/")
161 (1 . 1))
162 ;;; FIXME: Test fails due to scheme, host always being removed.
163 ;;; However, it should be supported to request these be present, and
164 ;;; that is possible with absolute/relative URI support.
165 ;; (pass-if-write-request-line "GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1"
166 ;; GET
167 ;; (build-uri 'http
168 ;; #:host "www.w3.org"
169 ;; #:path "/pub/WWW/TheProject.html")
170 ;; (1 . 1))
171 (pass-if-write-request-line "GET /pub/WWW/TheProject.html HTTP/1.1"
172 GET
173 (build-uri 'http
174 #:path "/pub/WWW/TheProject.html")
175 (1 . 1))
176 (pass-if-write-request-line "HEAD /etc/hosts?foo=bar HTTP/1.1"
177 HEAD
178 (build-uri 'http
179 #:path "/etc/hosts"
180 #:query "foo=bar")
181 (1 . 1)))
182
183(with-test-prefix "read-response-line"
184 (pass-if-read-response-line "HTTP/1.0 404 Not Found"
185 (1 . 0) 404 "Not Found")
186 (pass-if-read-response-line "HTTP/1.1 200 OK"
187 (1 . 1) 200 "OK"))
188
189(with-test-prefix "write-response-line"
190 (pass-if-write-response-line "HTTP/1.0 404 Not Found"
191 (1 . 0) 404 "Not Found")
192 (pass-if-write-response-line "HTTP/1.1 200 OK"
193 (1 . 1) 200 "OK"))
194
440840c1
AW
195(with-test-prefix "general headers"
196
197 (pass-if-parse cache-control "no-transform" '(no-transform))
0acc595b 198 (pass-if-parse cache-control "no-transform,foo" '(no-transform foo))
25731543
AW
199 (pass-if-parse cache-control "no-cache" '(no-cache))
200 (pass-if-parse cache-control "no-cache=\"Authorization, Date\""
201 '((no-cache . (authorization date))))
202 (pass-if-parse cache-control "private=\"Foo\""
be1be3e5 203 '((private . (foo))))
440840c1 204 (pass-if-parse cache-control "no-cache,max-age=10"
25731543 205 '(no-cache (max-age . 10)))
321770b2
DH
206 (pass-if-parse cache-control "max-stale" '(max-stale))
207 (pass-if-parse cache-control "max-stale=10" '((max-stale . 10)))
61fe8eaf
AW
208 (pass-if-round-trip "Cache-Control: acme-cache-extension\r\n")
209 (pass-if-round-trip "Cache-Control: acme-cache-extension=20\r\n")
210 (pass-if-round-trip "Cache-Control: acme-cache-extension=100 quux\r\n")
211 (pass-if-round-trip "Cache-Control: acme-cache-extension=\"100, quux\"\r\n")
440840c1 212
94f16a5b
AW
213 (pass-if-parse connection "close" '(close))
214 (pass-if-parse connection "Content-Encoding" '(content-encoding))
440840c1
AW
215
216 (pass-if-parse date "Tue, 15 Nov 1994 08:12:31 GMT"
217 (string->date "Tue, 15 Nov 1994 08:12:31 +0000"
218 "~a, ~d ~b ~Y ~H:~M:~S ~z"))
cb7bcfca
IP
219 (pass-if-parse date "Wed, 7 Sep 2011 11:25:00 GMT"
220 (string->date "Wed, 7 Sep 2011 11:25:00 +0000"
221 "~a,~e ~b ~Y ~H:~M:~S ~z"))
440840c1
AW
222 (pass-if-parse-error date "Tue, 15 Nov 1994 08:12:31 EST" date)
223 (pass-if-any-error date "Tue, 15 Qux 1994 08:12:31 EST")
224
225 (pass-if-parse pragma "no-cache" '(no-cache))
0acc595b 226 (pass-if-parse pragma "no-cache, foo" '(no-cache foo))
440840c1 227
be1be3e5
AW
228 (pass-if-parse trailer "foo, bar" '(foo bar))
229 (pass-if-parse trailer "connection, bar" '(connection bar))
440840c1 230
0acc595b 231 (pass-if-parse transfer-encoding "foo, chunked" '((foo) (chunked)))
440840c1
AW
232
233 (pass-if-parse upgrade "qux" '("qux"))
234
235 (pass-if-parse via "xyzzy" '("xyzzy"))
236
237 (pass-if-parse warning "123 foo \"core breach imminent\""
238 '((123 "foo" "core breach imminent" #f)))
239 (pass-if-parse
240 warning
241 "123 foo \"core breach imminent\" \"Tue, 15 Nov 1994 08:12:31 GMT\""
242 `((123 "foo" "core breach imminent"
243 ,(string->date "Tue, 15 Nov 1994 08:12:31 +0000"
244 "~a, ~d ~b ~Y ~H:~M:~S ~z")))))
245
246(with-test-prefix "entity headers"
94f16a5b
AW
247 (pass-if-parse allow "foo, bar" '(foo bar))
248 (pass-if-parse content-encoding "qux, baz" '(qux baz))
440840c1
AW
249 (pass-if-parse content-language "qux, baz" '("qux" "baz"))
250 (pass-if-parse content-length "100" 100)
251 (pass-if-parse content-length "0" 0)
252 (pass-if-parse content-length "010" 10)
253 (pass-if-parse content-location "http://foo/"
254 (build-uri 'http #:host "foo" #:path "/"))
255 (pass-if-parse content-range "bytes 10-20/*" '(bytes (10 . 20) *))
256 (pass-if-parse content-range "bytes */*" '(bytes * *))
257 (pass-if-parse content-range "bytes */30" '(bytes * 30))
0acc595b
AW
258 (pass-if-parse content-type "foo/bar" '(foo/bar))
259 (pass-if-parse content-type "foo/bar; baz=qux" '(foo/bar (baz . "qux")))
440840c1
AW
260 (pass-if-parse expires "Tue, 15 Nov 1994 08:12:31 GMT"
261 (string->date "Tue, 15 Nov 1994 08:12:31 +0000"
262 "~a, ~d ~b ~Y ~H:~M:~S ~z"))
263 (pass-if-parse last-modified "Tue, 15 Nov 1994 08:12:31 GMT"
264 (string->date "Tue, 15 Nov 1994 08:12:31 +0000"
265 "~a, ~d ~b ~Y ~H:~M:~S ~z")))
266
440840c1
AW
267(with-test-prefix "request headers"
268 (pass-if-parse accept "text/*;q=0.3, text/html;q=0.7, text/html;level=1"
0acc595b
AW
269 '((text/* (q . 300))
270 (text/html (q . 700))
271 (text/html (level . "1"))))
440840c1
AW
272 (pass-if-parse accept-charset "iso-8859-5, unicode-1-1;q=0.8"
273 '((1000 . "iso-8859-5") (800 . "unicode-1-1")))
274 (pass-if-parse accept-encoding "gzip;q=1.0, identity; q=0.5, *;q=0"
275 '((1000 . "gzip")
276 (500 . "identity")
277 (0 . "*")))
278 (pass-if-parse accept-language "da, en-gb;q=0.8, en;q=0.7"
279 '((1000 . "da") (800 . "en-gb") (700 . "en")))
9eed1010
AW
280 ;; Allow nonstandard .2 to mean 0.2
281 (pass-if-parse accept-language "en-gb;q=.2" '((200 . "en-gb")))
ecfb7167
AW
282 (pass-if-parse authorization "Basic foooo" '(basic . "foooo"))
283 (pass-if-parse authorization "Digest foooo" '(digest foooo))
284 (pass-if-parse authorization "Digest foo=bar,baz=qux"
285 '(digest (foo . "bar") (baz . "qux")))
0acc595b 286 (pass-if-parse expect "100-continue, foo" '((100-continue) (foo)))
440840c1
AW
287 (pass-if-parse from "foo@bar" "foo@bar")
288 (pass-if-parse host "qux" '("qux" . #f))
289 (pass-if-parse host "qux:80" '("qux" . 80))
b1c46fd3
DH
290 (pass-if-parse host "[2001:db8::1]" '("2001:db8::1" . #f))
291 (pass-if-parse host "[2001:db8::1]:80" '("2001:db8::1" . 80))
292 (pass-if-parse host "[::ffff:192.0.2.1]" '("::ffff:192.0.2.1" . #f))
293 (pass-if-round-trip "Host: [2001:db8::1]\r\n")
440840c1
AW
294 (pass-if-parse if-match "\"xyzzy\", W/\"qux\""
295 '(("xyzzy" . #t) ("qux" . #f)))
296 (pass-if-parse if-match "*" '*)
297 (pass-if-parse if-modified-since "Tue, 15 Nov 1994 08:12:31 GMT"
298 (string->date "Tue, 15 Nov 1994 08:12:31 +0000"
299 "~a, ~d ~b ~Y ~H:~M:~S ~z"))
300 (pass-if-parse if-none-match "\"xyzzy\", W/\"qux\""
301 '(("xyzzy" . #t) ("qux" . #f)))
302 (pass-if-parse if-none-match "*" '*)
303 (pass-if-parse if-range "\"foo\"" '("foo" . #t))
304 (pass-if-parse if-range "Tue, 15 Nov 1994 08:12:31 GMT"
305 (string->date "Tue, 15 Nov 1994 08:12:31 +0000"
306 "~a, ~d ~b ~Y ~H:~M:~S ~z"))
307 (pass-if-parse if-unmodified-since "Tue, 15 Nov 1994 08:12:31 GMT"
308 (string->date "Tue, 15 Nov 1994 08:12:31 +0000"
309 "~a, ~d ~b ~Y ~H:~M:~S ~z"))
310 (pass-if-parse max-forwards "10" 10)
311 (pass-if-parse max-forwards "00" 0)
ecfb7167
AW
312 (pass-if-parse proxy-authorization "Basic foooo" '(basic . "foooo"))
313 (pass-if-parse proxy-authorization "Digest foooo" '(digest foooo))
314 (pass-if-parse proxy-authorization "Digest foo=bar,baz=qux"
315 '(digest (foo . "bar") (baz . "qux")))
440840c1
AW
316 (pass-if-parse range "bytes=10-20" '(bytes (10 . 20)))
317 (pass-if-parse range "bytes=10-" '(bytes (10 . #f)))
318 (pass-if-parse range "bytes=-20" '(bytes (#f . 20)))
319 (pass-if-parse range "bytes=-20,-30" '(bytes (#f . 20) (#f . 30)))
320 (pass-if-parse referer "http://foo/bar?baz"
321 (build-uri 'http #:host "foo" #:path "/bar" #:query "baz"))
322 (pass-if-parse te "trailers" '((trailers)))
0acc595b 323 (pass-if-parse te "trailers,foo" '((trailers) (foo)))
440840c1
AW
324 (pass-if-parse user-agent "guile" "guile"))
325
326
327;; Response headers
328;;
329(with-test-prefix "response headers"
94f16a5b 330 (pass-if-parse accept-ranges "foo,bar" '(foo bar))
440840c1
AW
331 (pass-if-parse age "30" 30)
332 (pass-if-parse etag "\"foo\"" '("foo" . #t))
333 (pass-if-parse etag "W/\"foo\"" '("foo" . #f))
334 (pass-if-parse location "http://other-place"
335 (build-uri 'http #:host "other-place"))
ecfb7167
AW
336 (pass-if-parse proxy-authenticate "Basic realm=\"guile\""
337 '((basic (realm . "guile"))))
440840c1
AW
338 (pass-if-parse retry-after "Tue, 15 Nov 1994 08:12:31 GMT"
339 (string->date "Tue, 15 Nov 1994 08:12:31 +0000"
340 "~a, ~d ~b ~Y ~H:~M:~S ~z"))
341 (pass-if-parse retry-after "20" 20)
342 (pass-if-parse server "guile!" "guile!")
343 (pass-if-parse vary "*" '*)
be1be3e5 344 (pass-if-parse vary "foo, bar" '(foo bar))
ecfb7167
AW
345 (pass-if-parse www-authenticate "Basic realm=\"guile\""
346 '((basic (realm . "guile")))))
312e79f8
IP
347
348(with-test-prefix "chunked encoding"
349 (let* ((s "5\r\nFirst\r\nA\r\n line\n Sec\r\n8\r\nond line\r\n0\r\n")
350 (p (make-chunked-input-port (open-input-string s))))
351 (pass-if (equal? "First line\n Second line"
352 (get-string-all p)))
353 (pass-if (port-eof? (make-chunked-input-port (open-input-string "0\r\n")))))
354 (pass-if
355 (equal? (call-with-output-string
356 (lambda (out-raw)
357 (let ((out-chunked (make-chunked-output-port out-raw
358 #:keep-alive? #t)))
359 (display "First chunk" out-chunked)
360 (force-output out-chunked)
361 (display "Second chunk" out-chunked)
362 (force-output out-chunked)
363 (display "Third chunk" out-chunked)
364 (close-port out-chunked))))
365 "b\r\nFirst chunk\r\nc\r\nSecond chunk\r\nb\r\nThird chunk\r\n0\r\n")))