web-http.test 17 KB

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