server.scm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. ;;; Web server
  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. ;;; Commentary:
  18. ;;;
  19. ;;; (web server) is a generic web server interface, along with a main
  20. ;;; loop implementation for web servers controlled by Guile.
  21. ;;;
  22. ;;; The lowest layer is the <server-impl> object, which defines a set of
  23. ;;; hooks to open a server, read a request from a client, write a
  24. ;;; response to a client, and close a server. These hooks -- open,
  25. ;;; read, write, and close, respectively -- are bound together in a
  26. ;;; <server-impl> object. Procedures in this module take a
  27. ;;; <server-impl> object, if needed.
  28. ;;;
  29. ;;; A <server-impl> may also be looked up by name. If you pass the
  30. ;;; `http' symbol to `run-server', Guile looks for a variable named
  31. ;;; `http' in the `(web server http)' module, which should be bound to a
  32. ;;; <server-impl> object. Such a binding is made by instantiation of
  33. ;;; the `define-server-impl' syntax. In this way the run-server loop can
  34. ;;; automatically load other backends if available.
  35. ;;;
  36. ;;; The life cycle of a server goes as follows:
  37. ;;;
  38. ;;; * The `open' hook is called, to open the server. `open' takes 0 or
  39. ;;; more arguments, depending on the backend, and returns an opaque
  40. ;;; server socket object, or signals an error.
  41. ;;;
  42. ;;; * The `read' hook is called, to read a request from a new client.
  43. ;;; The `read' hook takes one arguments, the server socket. It
  44. ;;; should return three values: an opaque client socket, the
  45. ;;; request, and the request body. The request should be a
  46. ;;; `<request>' object, from `(web request)'. The body should be a
  47. ;;; string or a bytevector, or `#f' if there is no body.
  48. ;;;
  49. ;;; If the read failed, the `read' hook may return #f for the client
  50. ;;; socket, request, and body.
  51. ;;;
  52. ;;; * A user-provided handler procedure is called, with the request
  53. ;;; and body as its arguments. The handler should return two
  54. ;;; values: the response, as a `<response>' record from `(web
  55. ;;; response)', and the response body as a string, bytevector, or
  56. ;;; `#f' if not present. We also allow the reponse to be simply an
  57. ;;; alist of headers, in which case a default response object is
  58. ;;; constructed with those headers.
  59. ;;;
  60. ;;; * The `write' hook is called with three arguments: the client
  61. ;;; socket, the response, and the body. The `write' hook returns no
  62. ;;; values.
  63. ;;;
  64. ;;; * At this point the request handling is complete. For a loop, we
  65. ;;; loop back and try to read a new request.
  66. ;;;
  67. ;;; * If the user interrupts the loop, the `close' hook is called on
  68. ;;; the server socket.
  69. ;;;
  70. ;;; Code:
  71. (define-module (web server)
  72. #:use-module (srfi srfi-9)
  73. #:use-module (rnrs bytevectors)
  74. #:use-module (ice-9 binary-ports)
  75. #:use-module (web request)
  76. #:use-module (web response)
  77. #:use-module (system repl error-handling)
  78. #:use-module (ice-9 control)
  79. #:export (define-server-impl
  80. lookup-server-impl
  81. open-server
  82. read-client
  83. handle-request
  84. sanitize-response
  85. write-client
  86. close-server
  87. serve-one-client
  88. run-server))
  89. (define *timer* (gettimeofday))
  90. (define (print-elapsed who)
  91. (let ((t (gettimeofday)))
  92. (pk who (+ (* (- (car t) (car *timer*)) 1000000)
  93. (- (cdr t) (cdr *timer*))))
  94. (set! *timer* t)))
  95. (eval-when (expand)
  96. (define *time-debug?* #f))
  97. (define-syntax debug-elapsed
  98. (lambda (x)
  99. (syntax-case x ()
  100. ((_ who)
  101. (if *time-debug?*
  102. #'(print-elapsed who)
  103. #'*unspecified*)))))
  104. (define-record-type server-impl
  105. (make-server-impl name open read write close)
  106. server-impl?
  107. (name server-impl-name)
  108. (open server-impl-open)
  109. (read server-impl-read)
  110. (write server-impl-write)
  111. (close server-impl-close))
  112. (define-syntax-rule (define-server-impl name open read write close)
  113. (define name
  114. (make-server-impl 'name open read write close)))
  115. (define (lookup-server-impl impl)
  116. "Look up a server implementation. If @var{impl} is a server
  117. implementation already, it is returned directly. If it is a symbol, the
  118. binding named @var{impl} in the @code{(web server @var{impl})} module is
  119. looked up. Otherwise an error is signaled.
  120. Currently a server implementation is a somewhat opaque type, useful only
  121. for passing to other procedures in this module, like
  122. @code{read-client}."
  123. (cond
  124. ((server-impl? impl) impl)
  125. ((symbol? impl)
  126. (let ((impl (module-ref (resolve-module `(web server ,impl)) impl)))
  127. (if (server-impl? impl)
  128. impl
  129. (error "expected a server impl in module" `(web server ,impl)))))
  130. (else
  131. (error "expected a server-impl or a symbol" impl))))
  132. ;; -> server
  133. (define (open-server impl open-params)
  134. "Open a server for the given implementation. Returns one value, the
  135. new server object. The implementation's @code{open} procedure is
  136. applied to @var{open-params}, which should be a list."
  137. (apply (server-impl-open impl) open-params))
  138. ;; -> (client request body | #f #f #f)
  139. (define (read-client impl server)
  140. "Read a new client from @var{server}, by applying the implementation's
  141. @code{read} procedure to the server. If successful, returns three
  142. values: an object corresponding to the client, a request object, and the
  143. request body. If any exception occurs, returns @code{#f} for all three
  144. values."
  145. (call-with-error-handling
  146. (lambda ()
  147. ((server-impl-read impl) server))
  148. #:pass-keys '(quit interrupt)
  149. #:on-error (if (batch-mode?) 'pass 'debug)
  150. #:post-error
  151. (lambda (k . args)
  152. (warn "Error while accepting client" k args)
  153. (values #f #f #f))))
  154. ;; like call-with-output-string, but actually closes the port (doh)
  155. (define (call-with-output-string* proc)
  156. (let ((port (open-output-string)))
  157. (proc port)
  158. (let ((str (get-output-string port)))
  159. (close-port port)
  160. str)))
  161. (define (call-with-output-bytevector* proc)
  162. (call-with-values
  163. (lambda ()
  164. (open-bytevector-output-port))
  165. (lambda (port get-bytevector)
  166. (proc port)
  167. (let ((bv (get-bytevector)))
  168. (close-port port)
  169. bv))))
  170. (define (call-with-encoded-output-string charset proc)
  171. (if (string-ci=? charset "utf-8")
  172. ;; I don't know why, but this appears to be faster; at least for
  173. ;; examples/debug-sxml.scm (1464 reqs/s versus 850 reqs/s).
  174. (string->utf8 (call-with-output-string* proc))
  175. (call-with-output-bytevector*
  176. (lambda (port)
  177. (set-port-encoding! port charset)
  178. (proc port)))))
  179. (define (encode-string str charset)
  180. (if (string-ci=? charset "utf-8")
  181. (string->utf8 str)
  182. (call-with-encoded-output-string charset
  183. (lambda (port)
  184. (display str port)))))
  185. (define (extend-response r k v . additional)
  186. (let ((r (build-response #:version (response-version r)
  187. #:code (response-code r)
  188. #:headers
  189. (assoc-set! (copy-tree (response-headers r))
  190. k v)
  191. #:port (response-port r))))
  192. (if (null? additional)
  193. r
  194. (apply extend-response r additional))))
  195. ;; -> response body
  196. (define (sanitize-response request response body)
  197. "\"Sanitize\" the given response and body, making them appropriate for
  198. the given request.
  199. As a convenience to web handler authors, @var{response} may be given as
  200. an alist of headers, in which case it is used to construct a default
  201. response. Ensures that the response version corresponds to the request
  202. version. If @var{body} is a string, encodes the string to a bytevector,
  203. in an encoding appropriate for @var{response}. Adds a
  204. @code{content-length} and @code{content-type} header, as necessary.
  205. If @var{body} is a procedure, it is called with a port as an argument,
  206. and the output collected as a bytevector. In the future we might try to
  207. instead use a compressing, chunk-encoded port, and call this procedure
  208. later, in the write-client procedure. Authors are advised not to rely
  209. on the procedure being called at any particular time."
  210. (cond
  211. ((list? response)
  212. (sanitize-response request
  213. (build-response #:version (request-version request)
  214. #:headers response)
  215. body))
  216. ((not (equal? (request-version request) (response-version response)))
  217. (sanitize-response request
  218. (adapt-response-version response
  219. (request-version request))
  220. body))
  221. ((not body)
  222. (values response #vu8()))
  223. ((string? body)
  224. (let* ((type (response-content-type response
  225. '(text/plain)))
  226. (declared-charset (assq-ref (cdr type) 'charset))
  227. (charset (or declared-charset "utf-8")))
  228. (sanitize-response
  229. request
  230. (if declared-charset
  231. response
  232. (extend-response response 'content-type
  233. `(,@type (charset . ,charset))))
  234. (encode-string body charset))))
  235. ((procedure? body)
  236. (let* ((type (response-content-type response
  237. '(text/plain)))
  238. (declared-charset (assq-ref (cdr type) 'charset))
  239. (charset (or declared-charset "utf-8")))
  240. (sanitize-response
  241. request
  242. (if declared-charset
  243. response
  244. (extend-response response 'content-type
  245. `(,@type (charset . ,charset))))
  246. (call-with-encoded-output-string charset body))))
  247. ((bytevector? body)
  248. ;; check length; assert type; add other required fields?
  249. (values (let ((rlen (response-content-length response))
  250. (blen (bytevector-length body)))
  251. (cond
  252. (rlen (if (= rlen blen)
  253. response
  254. (error "bad content-length" rlen blen)))
  255. ((zero? blen) response)
  256. (else (extend-response response 'content-length blen))))
  257. body))
  258. (else
  259. (error "unexpected body type"))))
  260. ;; -> response body state
  261. (define (handle-request handler request body state)
  262. "Handle a given request, returning the response and body.
  263. The response and response body are produced by calling the given
  264. @var{handler} with @var{request} and @var{body} as arguments.
  265. The elements of @var{state} are also passed to @var{handler} as
  266. arguments, and may be returned as additional values. The new
  267. @var{state}, collected from the @var{handler}'s return values, is then
  268. returned as a list. The idea is that a server loop receives a handler
  269. from the user, along with whatever state values the user is interested
  270. in, allowing the user's handler to explicitly manage its state."
  271. (call-with-error-handling
  272. (lambda ()
  273. (call-with-values (lambda ()
  274. (with-stack-and-prompt
  275. (lambda ()
  276. (apply handler request body state))))
  277. (lambda (response body . state)
  278. (call-with-values (lambda ()
  279. (debug-elapsed 'handler)
  280. (sanitize-response request response body))
  281. (lambda (response body)
  282. (debug-elapsed 'sanitize)
  283. (values response body state))))))
  284. #:pass-keys '(quit interrupt)
  285. #:on-error (if (batch-mode?) 'pass 'debug)
  286. #:post-error
  287. (lambda (k . args)
  288. (warn "Error handling request" k args)
  289. (values (build-response #:code 500) #f state))))
  290. ;; -> unspecified values
  291. (define (write-client impl server client response body)
  292. "Write an HTTP response and body to @var{client}. If the server and
  293. client support persistent connections, it is the implementation's
  294. responsibility to keep track of the client thereafter, presumably by
  295. attaching it to the @var{server} argument somehow."
  296. (call-with-error-handling
  297. (lambda ()
  298. ((server-impl-write impl) server client response body))
  299. #:pass-keys '(quit interrupt)
  300. #:on-error (if (batch-mode?) 'pass 'debug)
  301. #:post-error
  302. (lambda (k . args)
  303. (warn "Error while writing response" k args)
  304. (values))))
  305. ;; -> unspecified values
  306. (define (close-server impl server)
  307. "Release resources allocated by a previous invocation of
  308. @code{open-server}."
  309. ((server-impl-close impl) server))
  310. (define call-with-sigint
  311. (if (not (provided? 'posix))
  312. (lambda (thunk handler-thunk) (thunk))
  313. (lambda (thunk handler-thunk)
  314. (let ((handler #f))
  315. (catch 'interrupt
  316. (lambda ()
  317. (dynamic-wind
  318. (lambda ()
  319. (set! handler
  320. (sigaction SIGINT (lambda (sig) (throw 'interrupt)))))
  321. thunk
  322. (lambda ()
  323. (if handler
  324. ;; restore Scheme handler, SIG_IGN or SIG_DFL.
  325. (sigaction SIGINT (car handler) (cdr handler))
  326. ;; restore original C handler.
  327. (sigaction SIGINT #f)))))
  328. (lambda (k . _) (handler-thunk)))))))
  329. (define (with-stack-and-prompt thunk)
  330. (call-with-prompt (default-prompt-tag)
  331. (lambda () (start-stack #t (thunk)))
  332. (lambda (k proc)
  333. (with-stack-and-prompt (lambda () (proc k))))))
  334. ;; -> new-state
  335. (define (serve-one-client handler impl server state)
  336. "Read one request from @var{server}, call @var{handler} on the request
  337. and body, and write the response to the client. Returns the new state
  338. produced by the handler procedure."
  339. (debug-elapsed 'serve-again)
  340. (call-with-values
  341. (lambda ()
  342. (read-client impl server))
  343. (lambda (client request body)
  344. (debug-elapsed 'read-client)
  345. (if client
  346. (call-with-values
  347. (lambda ()
  348. (handle-request handler request body state))
  349. (lambda (response body state)
  350. (debug-elapsed 'handle-request)
  351. (write-client impl server client response body)
  352. (debug-elapsed 'write-client)
  353. state))
  354. state))))
  355. (define* (run-server handler #:optional (impl 'http) (open-params '())
  356. . state)
  357. "Run Guile's built-in web server.
  358. @var{handler} should be a procedure that takes two or more arguments,
  359. the HTTP request and request body, and returns two or more values, the
  360. response and response body.
  361. For example, here is a simple \"Hello, World!\" server:
  362. @example
  363. (define (handler request body)
  364. (values '((content-type . (text/plain)))
  365. \"Hello, World!\"))
  366. (run-server handler)
  367. @end example
  368. The response and body will be run through @code{sanitize-response}
  369. before sending back to the client.
  370. Additional arguments to @var{handler} are taken from
  371. @var{state}. Additional return values are accumulated into a new
  372. @var{state}, which will be used for subsequent requests. In this way a
  373. handler can explicitly manage its state.
  374. The default server implementation is @code{http}, which accepts
  375. @var{open-params} like @code{(#:port 8081)}, among others. See \"Web
  376. Server\" in the manual, for more information."
  377. (let* ((impl (lookup-server-impl impl))
  378. (server (open-server impl open-params)))
  379. (call-with-sigint
  380. (lambda ()
  381. (let lp ((state state))
  382. (lp (serve-one-client handler impl server state))))
  383. (lambda ()
  384. (close-server impl server)
  385. (values)))))