web-client.test 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. ;;;; web-client.test --- HTTP client -*- mode: scheme; coding: utf-8; -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2013 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-client)
  19. #:use-module (web client)
  20. #:use-module (web request)
  21. #:use-module (web response)
  22. #:use-module (ice-9 iconv)
  23. #:use-module (ice-9 binary-ports)
  24. #:use-module (test-suite lib))
  25. (define get-request-headers:www.gnu.org/software/guile/
  26. "GET /software/guile/ HTTP/1.1
  27. Host: www.gnu.org
  28. Connection: close
  29. ")
  30. (define get-response-headers:www.gnu.org/software/guile/
  31. "HTTP/1.1 200 OK
  32. Date: Fri, 11 Jan 2013 10:59:11 GMT
  33. Server: Apache/2.2.14
  34. Accept-Ranges: bytes
  35. Cache-Control: max-age=0
  36. Expires: Fri, 11 Jan 2013 10:59:11 GMT
  37. Vary: Accept-Encoding
  38. Content-Length: 8077
  39. Connection: close
  40. Content-Type: text/html
  41. Content-Language: en
  42. ")
  43. (define get-response-body:www.gnu.org/software/guile/
  44. "<!DOCTYPE html PUBLIC \"-//IETF//DTD HTML 2.0//EN\">
  45. <html>
  46. <head>
  47. <title>GNU Guile (About Guile)</title>
  48. <link rel=\"stylesheet\" type=\"text/css\" href=\"/gnu.css\">
  49. <link rel=\"stylesheet\" type=\"text/css\" href=\"/software/guile/guile.css\">
  50. <link rev=\"made\" href=\"mailto:bug-guile@gnu.org\">
  51. </head>
  52. <!-- If you edit these html pages directly, you're not doing yourself any
  53. favors - these pages get updated programaticly from a pair of files. Edit
  54. the files under the template directory instead -->
  55. <!-- Text black on white, unvisited links blue, visited links navy,
  56. active links red -->
  57. <body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#1f00ff\" alink=\"#ff0000\" vlink=\"#000080\">
  58. <a name=\"top\"></a>
  59. <table cellpadding=\"10\">
  60. <tr>
  61. <td>
  62. \t<a href=\"/software/guile/\">
  63. \t <img src=\"/software/guile/graphics/guile-banner.small.png\" alt=\"Guile\">
  64. \t</a>
  65. </td>
  66. <td valign=\"bottom\">
  67. \t<h4 align=\"right\">The GNU extension language</h4>
  68. \t<h4 align=\"right\">About Guile</h4>
  69. </td>
  70. </tr>
  71. </table>
  72. <br />
  73. <table border=\"0\">
  74. <!-- Table with 2 columns. One along the left (navbar) and one along the
  75. \t right (body). On the main page, the left links to anchors on the right,
  76. \t or to other pages. The left has 2 sections. Top is global navigation,
  77. \t the bottom is local nav. -->
  78. <tr>
  79. <td class=\"sidebar\">
  80. \t<table cellpadding=\"4\">
  81. \t <tr>
  82. \t <!-- Global Nav -->
  83. \t <td nowrap=\"\">
  84. \t <p><b>About Guile</b><br />
  85. \t\t<a href=\"/software/guile/guile.html\">What is Guile?</a><br />
  86. \t\t<a href=\"/software/guile/news.html\">News</a><br />
  87. \t\t<a href=\"/software/guile/community.html\">Community</a><br />
  88. \t </p>
  89. \t
  90. \t <p><b>Documentation</b><br />
  91. \t\t<a href=\"/software/guile/docs/docs.html\">Manuals</a><br />
  92. \t\t<a href=\"/software/guile/docs/faq/guile-faq.html\">FAQ's</a><br />
  93. \t </p>
  94. \t <p><b>Download</b><br />
  95. \t\t<a href=\"/software/guile/download.html#releases\">Releases</a><br />
  96. \t\t<a href=\"/software/guile/download.html#git\">Repository</a><br />
  97. \t\t<a href=\"/software/guile/download.html#snapshots\">Snapshots</a><br />
  98. \t </p>
  99. \t <p><b>Projects</b><br />
  100. \t\t<a href=\"/software/guile/gnu-guile-projects.html#Core\">Core</a><br />
  101. \t\t<a href=\"/software/guile/gnu-guile-projects.html#GUI\">GUI</a><br />
  102. \t\t<a href=\"/software/guile/gnu-guile-projects.html#File-Formats\">File Formats</a><br />
  103. \t\t<a href=\"/software/guile/gnu-guile-projects.html#Networking\">Networking</a><br />
  104. \t\t<a href=\"/software/guile/gnu-guile-projects.html#Tools\">Tools</a><br />
  105. \t\t<a href=\"/software/guile/gnu-guile-projects.html#Applications\">Applications</a><br />
  106. \t </p>
  107. \t
  108. \t <p><b>Development</b><br />
  109. \t\t<a href=\"http://savannah.gnu.org/projects/guile/\">Project summary</a><br />
  110. \t\t<a href=\"/software/guile/developers.html\">Helping out</a><br />
  111. \t\t<a href=\"/software/guile/ideas.html\">Cool ideas</a><br />
  112. \t </p>
  113. \t <p><b>Resources</b><br>
  114. \t\t<a href=\"/software/guile/resources.html#guile_resources\">Guile Resources</a><br />
  115. \t\t<a href=\"/software/guile/resources.html##scheme_resources\">Scheme Resources</a><br />
  116. \t </p>
  117. \t </td>
  118. \t </tr>
  119. \t <tr>
  120. \t <!-- Global Nav End -->
  121. \t
  122. <tr>
  123. <td>
  124. <p><a href=\"http://www.gnu.org/\">GNU Project home page</a></p>
  125. <p><a href=\"#whatisit\">What is Guile?</a></p>
  126. <p><a href=\"#get\">Getting Guile</a></p>
  127. </td>
  128. </tr>
  129. \t </tr>
  130. \t</table>
  131. </td>
  132. <td class=\"rhs-body\">
  133. \t
  134. <a name=\"whatisit\"><h3 align=\"left\">What is Guile? What can it do for you?</h3></a>
  135. <p>
  136. Guile is the <em>GNU Ubiquitous Intelligent Language for Extensions</em>,
  137. the official extension language for the
  138. <a href=\"http://www.gnu.org/\">GNU operating system</a>.
  139. </p>
  140. <p>
  141. Guile is a library designed to help programmers create flexible
  142. applications. Using Guile in an application allows the application's
  143. functionality to be <em>extended</em> by users or other programmers with
  144. plug-ins, modules, or scripts. Guile provides what might be described as
  145. \"practical software freedom,\" making it possible for users to customize an
  146. application to meet their needs without digging into the application's
  147. internals.
  148. </p>
  149. <p>
  150. There is a long list of proven applications that employ extension languages.
  151. Successful and long-lived examples of Free Software projects that use
  152. Guile are <a href=\"http://www.texmacs.org/\">TeXmacs</a>,
  153. <a href=\"http://lilypond.org/\">LilyPond</a>, and
  154. <a href=\"http://www.gnucash.org/\">GnuCash</a>.
  155. </p>
  156. <h3>Guile is a programming language</h3>
  157. <p>
  158. Guile is an interpreter and compiler for
  159. the <a href=\"http://schemers.org/\">Scheme</a> programming language, a clean
  160. and elegant dialect of Lisp. Guile is up to date with recent Scheme
  161. standards, supporting the
  162. <a href=\"http://www.schemers.org/Documents/Standards/R5RS/\">Revised<sup>5</sup></a>
  163. and most of the <a href=\"http://www.r6rs.org/\">Revised<sup>6</sup></a> language
  164. reports (including hygienic macros), as well as many
  165. <a href=\"http://srfi.schemers.org/\">SRFIs</a>. It also comes with a library
  166. of modules that offer additional features, like an HTTP server and client,
  167. XML parsing, and object-oriented programming.
  168. </p>
  169. <h3>Guile is an extension language platform</h3>
  170. <p>
  171. Guile is an efficient virtual machine that executes a portable instruction
  172. set generated by its optimizing compiler, and integrates very easily with C
  173. and C++ application code. In addition to Scheme, Guile includes compiler
  174. front-ends for
  175. <a href=\"http://www.ecma-international.org/publications/standards/Ecma-262.htm\">ECMAScript</a>
  176. and <a href=\"http://www.emacswiki.org/cgi-bin/wiki?EmacsLisp\">Emacs Lisp</a>
  177. (support for <a href=\"http://www.lua.org/\">Lua</a> is underway), which means
  178. your application can be extended in the language (or languages) most
  179. appropriate for your user base. And Guile's tools for parsing and compiling
  180. are exposed as part of its standard module set, so support for additional
  181. languages can be added without writing a single line of C.
  182. </p>
  183. <h3>Guile gives your programs more power</h3>
  184. <p>
  185. Using Guile with your program makes it more usable. Users don't
  186. need to learn the plumbing of your application to customize it; they just
  187. need to understand Guile, and the access you've provided. They can easily
  188. trade and share features by downloading and creating scripts, instead of
  189. trading complex patches and recompiling their applications. They don't need
  190. to coordinate with you or anyone else. Using Guile, your application has a
  191. full-featured scripting language right from the beginning, so you can focus
  192. on the novel and attention-getting parts of your application.
  193. </p>
  194. <a name=\"get\"><h2 align=\"center\">How do I get Guile?</h2></a>
  195. <ul>
  196. <li>The current <em>stable</em> release is
  197. <a href=\"ftp://ftp.gnu.org/gnu/guile/guile-2.0.7.tar.gz\">2.0.7</a>.
  198. </li>
  199. </ul>
  200. <p>
  201. See the <a href=\"download.html\">Download</a> page for additional ways of
  202. getting Guile.
  203. </p>
  204. </td>
  205. </tr>
  206. </table>
  207. <br />
  208. <div class=\"copyright\">
  209. <p>
  210. Please send FSF &amp; GNU inquiries &amp; questions to
  211. <a href=\"mailto:gnu@gnu.org\"><em>gnu@gnu.org</em></a>. There are also
  212. <a href=\"/home.html#ContactInfo\">other ways to contact</a> the FSF.
  213. </p>
  214. <p>
  215. Please send comments on these web pages to
  216. <a href=\"mailto:bug-guile@gnu.org\"><em>bug-guile@gnu.org</em></a>, send
  217. other questions to <a href=\"mailto:gnu@gnu.org\"><em>gnu@gnu.org</em></a>.
  218. </p>
  219. <p>
  220. Copyright (C) 2012 Free Software Foundation, Inc.
  221. </p>
  222. <p>
  223. Verbatim copying and distribution of this entire web page is
  224. permitted in any medium, provided this notice is preserved.<P>
  225. Updated:
  226. <!-- timestamp start -->
  227. $Date: 2012/11/30 00:16:15 $ $Author: civodul $
  228. <!-- timestamp end -->
  229. </p>
  230. </div>
  231. </body>
  232. </html>
  233. ")
  234. (define head-request-headers:www.gnu.org/software/guile/
  235. "HEAD /software/guile/ HTTP/1.1
  236. Host: www.gnu.org
  237. Connection: close
  238. ")
  239. (define head-response-headers:www.gnu.org/software/guile/
  240. "HTTP/1.1 200 OK
  241. Date: Fri, 11 Jan 2013 11:03:14 GMT
  242. Server: Apache/2.2.14
  243. Accept-Ranges: bytes
  244. Cache-Control: max-age=0
  245. Expires: Fri, 11 Jan 2013 11:03:14 GMT
  246. Vary: Accept-Encoding
  247. Content-Length: 8077
  248. Connection: close
  249. Content-Type: text/html
  250. Content-Language: en
  251. ")
  252. ;; Unfortunately, POST to http://www.gnu.org/software/guile/ succeeds!
  253. (define post-request-headers:www.apache.org/
  254. "POST / HTTP/1.1
  255. Host: www.apache.org
  256. Connection: close
  257. ")
  258. (define post-response-headers:www.apache.org/
  259. "HTTP/1.1 405 Method Not Allowed
  260. Date: Fri, 11 Jan 2013 11:04:34 GMT
  261. Server: Apache/2.4.3 (Unix) OpenSSL/1.0.0g
  262. Allow: TRACE
  263. Content-Length: 314
  264. Connection: close
  265. Content-Type: text/html; charset=iso-8859-1
  266. ")
  267. (define post-response-body:www.apache.org/
  268. "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">
  269. <html><head>
  270. <title>405 Method Not Allowed</title>
  271. </head><body>
  272. <h1>Method Not Allowed</h1>
  273. <p>The requested method POST is not allowed for the URL /.</p>
  274. <hr>
  275. <address>Apache/2.4.3 (Unix) OpenSSL/1.0.0g Server at www.apache.org Port 80</address>
  276. </body></html>
  277. ")
  278. (define put-request-headers:www.apache.org/
  279. "PUT / HTTP/1.1
  280. Host: www.apache.org
  281. Connection: close
  282. ")
  283. (define put-response-headers:www.apache.org/
  284. "HTTP/1.1 405 Method Not Allowed
  285. Date: Fri, 11 Jan 2013 11:04:34 GMT
  286. Server: Apache/2.4.3 (Unix) OpenSSL/1.0.0g
  287. Allow: TRACE
  288. Content-Length: 313
  289. Connection: close
  290. Content-Type: text/html; charset=iso-8859-1
  291. ")
  292. (define put-response-body:www.apache.org/
  293. "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">
  294. <html><head>
  295. <title>405 Method Not Allowed</title>
  296. </head><body>
  297. <h1>Method Not Allowed</h1>
  298. <p>The requested method PUT is not allowed for the URL /.</p>
  299. <hr>
  300. <address>Apache/2.4.3 (Unix) OpenSSL/1.0.0g Server at www.apache.org Port 80</address>
  301. </body></html>
  302. ")
  303. (define delete-request-headers:www.apache.org/
  304. "DELETE / HTTP/1.1
  305. Host: www.apache.org
  306. Connection: close
  307. ")
  308. (define delete-response-headers:www.apache.org/
  309. "HTTP/1.1 405 Method Not Allowed
  310. Date: Fri, 11 Jan 2013 11:07:19 GMT
  311. Server: Apache/2.4.3 (Unix) OpenSSL/1.0.0g
  312. Allow: TRACE
  313. Content-Length: 316
  314. Connection: close
  315. Content-Type: text/html; charset=iso-8859-1
  316. ")
  317. (define delete-response-body:www.apache.org/
  318. "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">
  319. <html><head>
  320. <title>405 Method Not Allowed</title>
  321. </head><body>
  322. <h1>Method Not Allowed</h1>
  323. <p>The requested method DELETE is not allowed for the URL /.</p>
  324. <hr>
  325. <address>Apache/2.4.3 (Unix) OpenSSL/1.0.0g Server at www.apache.org Port 80</address>
  326. </body></html>
  327. ")
  328. (define options-request-headers:www.apache.org/
  329. "OPTIONS / HTTP/1.1
  330. Host: www.apache.org
  331. Connection: close
  332. ")
  333. (define options-response-headers:www.apache.org/
  334. "HTTP/1.1 200 OK
  335. Date: Fri, 11 Jan 2013 11:08:31 GMT
  336. Server: Apache/2.4.3 (Unix) OpenSSL/1.0.0g
  337. Allow: OPTIONS,GET,HEAD,POST,TRACE
  338. Cache-Control: max-age=3600
  339. Expires: Fri, 11 Jan 2013 12:08:31 GMT
  340. Content-Length: 0
  341. Connection: close
  342. Content-Type: text/html; charset=utf-8
  343. ")
  344. ;; This depends on the exact request that we send. I copied this off
  345. ;; the console with an "nc" session, so it doesn't include the CR bytes.
  346. ;; But that's OK -- we just have to decode the body as an HTTP request
  347. ;; and check that it's the same.
  348. (define trace-request-headers:www.apache.org/
  349. "TRACE / HTTP/1.1\r
  350. Host: www.apache.org\r
  351. Connection: close\r
  352. \r
  353. ")
  354. (define trace-response-headers:www.apache.org/
  355. "HTTP/1.1 200 OK\r
  356. Date: Fri, 11 Jan 2013 12:36:13 GMT\r
  357. Server: Apache/2.4.3 (Unix) OpenSSL/1.0.0g\r
  358. Connection: close\r
  359. Transfer-Encoding: chunked\r
  360. Content-Type: message/http\r
  361. \r
  362. ")
  363. (define trace-response-body:www.apache.org/
  364. "3d\r
  365. TRACE / HTTP/1.1\r
  366. Host: www.apache.org\r
  367. Connection: close\r
  368. \r
  369. \r
  370. 0\r
  371. \r
  372. ")
  373. (define (requests-equal? r1 r2)
  374. (and (equal? (request-method r1) (request-method r2))
  375. (equal? (request-uri r1) (request-uri r2))
  376. (equal? (request-version r1) (request-version r2))
  377. (equal? (request-headers r1) (request-headers r2))))
  378. (define (responses-equal? r1 r2)
  379. (and (equal? (response-code r1) (response-code r2))
  380. (equal? (response-version r1) (response-version r2))
  381. (equal? (response-headers r1) (response-headers r2))))
  382. (define* (run-with-http-transcript
  383. expected-request expected-request-body request-body-encoding
  384. response response-body response-body-encoding
  385. proc)
  386. (let ((reading? #f)
  387. (writing? #t)
  388. (response-port (open-input-string response))
  389. (response-body-port (open-bytevector-input-port
  390. (string->bytevector response-body
  391. response-body-encoding))))
  392. (call-with-values (lambda () (open-bytevector-output-port))
  393. (lambda (request-port get-bytevector)
  394. (define (put-char c)
  395. (unless writing?
  396. (error "Port closed for writing"))
  397. (put-u8 request-port (char->integer c)))
  398. (define (put-string s)
  399. (string-for-each put-char s)
  400. (set! writing? #f)
  401. (set! reading? #t)
  402. (let* ((p (open-bytevector-input-port (get-bytevector)))
  403. (actual-request (read-request p))
  404. (actual-body (read-request-body actual-request)))
  405. (pass-if "requests equal"
  406. (requests-equal? actual-request
  407. (call-with-input-string expected-request
  408. read-request)))
  409. (pass-if "request bodies equal"
  410. (equal? (or actual-body #vu8())
  411. (string->bytevector expected-request-body
  412. request-body-encoding)))))
  413. (define (get-char)
  414. (unless reading?
  415. (error "Port closed for reading"))
  416. (let ((c (read-char response-port)))
  417. (if (char? c)
  418. c
  419. (let ((u8 (get-u8 response-body-port)))
  420. (if (eof-object? u8)
  421. u8
  422. (integer->char u8))))))
  423. (define (close)
  424. (when writing?
  425. (unless (eof-object? (get-u8 response-body-port))
  426. (error "Failed to consume all of body"))))
  427. (let ((soft-port (make-soft-port
  428. (vector put-char put-string #f get-char close)
  429. "rw")))
  430. ;; Arrange it so that the only time our put-char/put-string
  431. ;; functions are called is during force-output.
  432. (setvbuf soft-port 'block 10000)
  433. (proc soft-port))))))
  434. (define* (check-transaction method uri
  435. request-headers request-body request-body-encoding
  436. response-headers response-body response-body-encoding
  437. proc
  438. #:key (response-body-comparison response-body))
  439. (with-test-prefix (string-append method " " uri)
  440. (run-with-http-transcript
  441. request-headers request-body request-body-encoding
  442. response-headers response-body response-body-encoding
  443. (lambda (port)
  444. (call-with-values (lambda ()
  445. (proc uri #:port port))
  446. (lambda (response body)
  447. (pass-if "response equal"
  448. (responses-equal?
  449. response
  450. (call-with-input-string response-headers read-response)))
  451. (pass-if "response body equal"
  452. (equal? (or body "") response-body-comparison))))))))
  453. (check-transaction
  454. "GET" "http://www.gnu.org/software/guile/"
  455. get-request-headers:www.gnu.org/software/guile/ "" "iso-8859-1"
  456. get-response-headers:www.gnu.org/software/guile/
  457. get-response-body:www.gnu.org/software/guile/ "iso-8859-1"
  458. http-get)
  459. (check-transaction
  460. "HEAD" "http://www.gnu.org/software/guile/"
  461. head-request-headers:www.gnu.org/software/guile/ "" "iso-8859-1"
  462. head-response-headers:www.gnu.org/software/guile/ "" "iso-8859-1"
  463. http-head)
  464. (check-transaction
  465. "POST" "http://www.apache.org/"
  466. post-request-headers:www.apache.org/ "" "iso-8859-1"
  467. post-response-headers:www.apache.org/
  468. post-response-body:www.apache.org/ "iso-8859-1"
  469. http-post)
  470. (check-transaction
  471. "PUT" "http://www.apache.org/"
  472. put-request-headers:www.apache.org/ "" "iso-8859-1"
  473. put-response-headers:www.apache.org/
  474. put-response-body:www.apache.org/ "iso-8859-1"
  475. http-put)
  476. (check-transaction
  477. "DELETE" "http://www.apache.org/"
  478. delete-request-headers:www.apache.org/ "" "iso-8859-1"
  479. delete-response-headers:www.apache.org/
  480. delete-response-body:www.apache.org/ "iso-8859-1"
  481. http-delete)
  482. (check-transaction
  483. "OPTIONS" "http://www.apache.org/"
  484. options-request-headers:www.apache.org/ "" "utf-8"
  485. options-response-headers:www.apache.org/ "" "utf-8"
  486. http-options)
  487. (check-transaction
  488. "TRACE" "http://www.apache.org/"
  489. trace-request-headers:www.apache.org/ "" "iso-8859-1"
  490. trace-response-headers:www.apache.org/
  491. trace-response-body:www.apache.org/ "iso-8859-1"
  492. http-trace
  493. #:response-body-comparison
  494. ;; The body will be message/http, which is logically a sequence of
  495. ;; bytes, not characters. It happens that iso-8859-1 can encode our
  496. ;; body and is compatible with the headers as well.
  497. (string->bytevector trace-request-headers:www.apache.org/
  498. "iso-8859-1"))