request.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. ;;; HTTP request objects
  2. ;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
  3. ;; This library is free software; you can redistribute it and/or
  4. ;; modify it under the terms of the GNU Lesser General Public
  5. ;; License as published by the Free Software Foundation; either
  6. ;; version 3 of the License, or (at your option) any later version.
  7. ;;
  8. ;; This library is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;; Lesser General Public License for more details.
  12. ;;
  13. ;; You should have received a copy of the GNU Lesser General Public
  14. ;; License along with this library; if not, write to the Free Software
  15. ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  16. ;; 02110-1301 USA
  17. ;;; Code:
  18. (define-module (web request)
  19. #:use-module (rnrs bytevectors)
  20. #:use-module (ice-9 binary-ports)
  21. #:use-module (ice-9 rdelim)
  22. #:use-module (srfi srfi-9)
  23. #:use-module (web uri)
  24. #:use-module (web http)
  25. #:export (request?
  26. request-method
  27. request-uri
  28. request-version
  29. request-headers
  30. request-meta
  31. request-port
  32. read-request
  33. build-request
  34. write-request
  35. read-request-body
  36. write-request-body
  37. ;; General headers
  38. ;;
  39. request-cache-control
  40. request-connection
  41. request-date
  42. request-pragma
  43. request-trailer
  44. request-transfer-encoding
  45. request-upgrade
  46. request-via
  47. request-warning
  48. ;; Entity headers
  49. ;;
  50. request-allow
  51. request-content-encoding
  52. request-content-language
  53. request-content-length
  54. request-content-location
  55. request-content-md5
  56. request-content-range
  57. request-content-type
  58. request-expires
  59. request-last-modified
  60. ;; Request headers
  61. ;;
  62. request-accept
  63. request-accept-charset
  64. request-accept-encoding
  65. request-accept-language
  66. request-authorization
  67. request-expect
  68. request-from
  69. request-host
  70. request-if-match
  71. request-if-modified-since
  72. request-if-none-match
  73. request-if-range
  74. request-if-unmodified-since
  75. request-max-forwards
  76. request-proxy-authorization
  77. request-range
  78. request-referer
  79. request-te
  80. request-user-agent
  81. ;; Misc
  82. request-absolute-uri))
  83. ;;; {Character Encodings, Strings, and Bytevectors}
  84. ;;;
  85. ;;; Requests are read from over the wire, and as such have to be treated
  86. ;;; very carefully.
  87. ;;;
  88. ;;; The header portion of the message is defined to be in a subset of
  89. ;;; ASCII, and may be processed either byte-wise (using bytevectors and
  90. ;;; binary I/O) or as characters in a single-byte ASCII-compatible
  91. ;;; encoding.
  92. ;;;
  93. ;;; We choose the latter, processing as strings in the latin-1
  94. ;;; encoding. This allows us to use all the read-delimited machinery,
  95. ;;; character sets, and regular expressions, shared substrings, etc.
  96. ;;;
  97. ;;; The characters in the header values may themselves encode other
  98. ;;; bytes or characters -- basically each header has its own parser. We
  99. ;;; leave that as a header-specific topic.
  100. ;;;
  101. ;;; The body is present if the content-length header is present. Its
  102. ;;; format and, if textual, encoding is determined by the headers, but
  103. ;;; its length is encoded in bytes. So we just slurp that number of
  104. ;;; characters in latin-1, knowing that the number of characters
  105. ;;; corresponds to the number of bytes, and then convert to a
  106. ;;; bytevector, perhaps for later decoding.
  107. ;;;
  108. (define-record-type <request>
  109. (make-request method uri version headers meta port)
  110. request?
  111. (method request-method)
  112. (uri request-uri)
  113. (version request-version)
  114. (headers request-headers)
  115. (meta request-meta)
  116. (port request-port))
  117. (define (bad-request message . args)
  118. (throw 'bad-request message args))
  119. (define (non-negative-integer? n)
  120. (and (number? n) (>= n 0) (exact? n) (integer? n)))
  121. (define (validate-headers headers)
  122. (if (pair? headers)
  123. (let ((h (car headers)))
  124. (if (pair? h)
  125. (let ((k (car h)) (v (cdr h)))
  126. (if (valid-header? k v)
  127. (validate-headers (cdr headers))
  128. (bad-request "Bad value for header ~a: ~s" k v)))
  129. (bad-request "Header not a pair: ~a" h)))
  130. (if (not (null? headers))
  131. (bad-request "Headers not a list: ~a" headers))))
  132. (define* (build-request uri #:key (method 'GET) (version '(1 . 1))
  133. (headers '()) port (meta '())
  134. (validate-headers? #t))
  135. "Construct an HTTP request object. If @var{validate-headers?} is true,
  136. the headers are each run through their respective validators."
  137. (let ((needs-host? (and (equal? version '(1 . 1))
  138. (not (assq-ref headers 'host)))))
  139. (cond
  140. ((not (and (pair? version)
  141. (non-negative-integer? (car version))
  142. (non-negative-integer? (cdr version))))
  143. (bad-request "Bad version: ~a" version))
  144. ((not (uri? uri))
  145. (bad-request "Bad uri: ~a" uri))
  146. ((and (not port) (memq method '(POST PUT)))
  147. (bad-request "Missing port for message ~a" method))
  148. ((not (list? meta))
  149. (bad-request "Bad metadata alist" meta))
  150. ((and needs-host? (not (uri-host uri)))
  151. (bad-request "HTTP/1.1 request without Host header and no host in URI: ~a"
  152. uri))
  153. (else
  154. (if validate-headers?
  155. (validate-headers headers))))
  156. (make-request method uri version
  157. (if needs-host?
  158. (acons 'host (cons (uri-host uri) (uri-port uri))
  159. headers)
  160. headers)
  161. meta port)))
  162. (define* (read-request port #:optional (meta '()))
  163. "Read an HTTP request from @var{port}, optionally attaching the given
  164. metadata, @var{meta}.
  165. As a side effect, sets the encoding on @var{port} to
  166. ISO-8859-1 (latin-1), so that reading one character reads one byte. See
  167. the discussion of character sets in \"HTTP Requests\" in the manual, for
  168. more information."
  169. (set-port-encoding! port "ISO-8859-1")
  170. (call-with-values (lambda () (read-request-line port))
  171. (lambda (method uri version)
  172. (make-request method uri version (read-headers port) meta port))))
  173. ;; FIXME: really return a new request?
  174. (define (write-request r port)
  175. "Write the given HTTP request to @var{port}.
  176. Returns a new request, whose @code{request-port} will continue writing
  177. on @var{port}, perhaps using some transfer encoding."
  178. (write-request-line (request-method r) (request-uri r)
  179. (request-version r) port)
  180. (write-headers (request-headers r) port)
  181. (display "\r\n" port)
  182. (if (eq? port (request-port r))
  183. r
  184. (make-request (request-method r) (request-uri r) (request-version r)
  185. (request-headers r) (request-meta r) port)))
  186. (define (read-request-body r)
  187. "Reads the request body from @var{r}, as a bytevector. Returns
  188. @code{#f} if there was no request body."
  189. (let ((nbytes (request-content-length r)))
  190. (and nbytes
  191. (let ((bv (get-bytevector-n (request-port r) nbytes)))
  192. (if (= (bytevector-length bv) nbytes)
  193. bv
  194. (bad-request "EOF while reading request body: ~a bytes of ~a"
  195. (bytevector-length bv) nbytes))))))
  196. (define (write-request-body r bv)
  197. "Write @var{body}, a bytevector, to the port corresponding to the HTTP
  198. request @var{r}."
  199. (put-bytevector (request-port r) bv))
  200. (define-syntax define-request-accessor
  201. (lambda (x)
  202. (syntax-case x ()
  203. ((_ field)
  204. #'(define-request-accessor field #f))
  205. ((_ field def) (identifier? #'field)
  206. #`(define* (#,(datum->syntax
  207. #'field
  208. (symbol-append 'request- (syntax->datum #'field)))
  209. request
  210. #:optional (default def))
  211. (cond
  212. ((assq 'field (request-headers request)) => cdr)
  213. (else default)))))))
  214. ;; General headers
  215. ;;
  216. (define-request-accessor cache-control '())
  217. (define-request-accessor connection '())
  218. (define-request-accessor date #f)
  219. (define-request-accessor pragma '())
  220. (define-request-accessor trailer '())
  221. (define-request-accessor transfer-encoding '())
  222. (define-request-accessor upgrade '())
  223. (define-request-accessor via '())
  224. (define-request-accessor warning '())
  225. ;; Entity headers
  226. ;;
  227. (define-request-accessor allow '())
  228. (define-request-accessor content-encoding '())
  229. (define-request-accessor content-language '())
  230. (define-request-accessor content-length #f)
  231. (define-request-accessor content-location #f)
  232. (define-request-accessor content-md5 #f)
  233. (define-request-accessor content-range #f)
  234. (define-request-accessor content-type #f)
  235. (define-request-accessor expires #f)
  236. (define-request-accessor last-modified #f)
  237. ;; Request headers
  238. ;;
  239. (define-request-accessor accept '())
  240. (define-request-accessor accept-charset '())
  241. (define-request-accessor accept-encoding '())
  242. (define-request-accessor accept-language '())
  243. (define-request-accessor authorization #f)
  244. (define-request-accessor expect '())
  245. (define-request-accessor from #f)
  246. (define-request-accessor host #f)
  247. ;; Absence of an if-directive appears to be different from `*'.
  248. (define-request-accessor if-match #f)
  249. (define-request-accessor if-modified-since #f)
  250. (define-request-accessor if-none-match #f)
  251. (define-request-accessor if-range #f)
  252. (define-request-accessor if-unmodified-since #f)
  253. (define-request-accessor max-forwards #f)
  254. (define-request-accessor proxy-authorization #f)
  255. (define-request-accessor range #f)
  256. (define-request-accessor referer #f)
  257. (define-request-accessor te '())
  258. (define-request-accessor user-agent #f)
  259. ;; Misc accessors
  260. (define* (request-absolute-uri r #:optional default-host default-port)
  261. (let ((uri (request-uri r)))
  262. (if (uri-host uri)
  263. uri
  264. (let ((host
  265. (or (request-host r)
  266. (if default-host
  267. (cons default-host default-port)
  268. (bad-request
  269. "URI not absolute, no Host header, and no default: ~s"
  270. uri)))))
  271. (build-uri (uri-scheme uri)
  272. #:host (car host)
  273. #:port (cdr host)
  274. #:path (uri-path uri)
  275. #:query (uri-query uri)
  276. #:fragment (uri-fragment uri))))))