response-utils.scm 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. (library (response-utils)
  2. (export respond
  3. respond-static-asset
  4. make-respond-static-asset-handler)
  5. (import
  6. (except (rnrs base) let-values)
  7. (only (guile) lambda* λ error when display sleep)
  8. ;; Guile modules
  9. ;; alist->hash-table
  10. (prefix (ice-9 hash-table) ice9-hash-table:)
  11. ;; Guile exception handling
  12. (ice-9 exceptions)
  13. (ice-9 session)
  14. ;; for bytevector operations
  15. (ice-9 binary-ports)
  16. ;; SRFIs
  17. ;; hash tables
  18. (prefix (srfi srfi-69) srfi-69:)
  19. ;; receive form
  20. (prefix (srfi srfi-8) srfi-8:)
  21. ;; let-values
  22. (prefix (srfi srfi-11) srfi-11:)
  23. ;; list utils
  24. (prefix (srfi srfi-1) srfi-1:)
  25. ;; web server, concurrent
  26. (fibers web server)
  27. ;; standard web library
  28. (web request)
  29. (web response)
  30. (web uri)
  31. (sxml simple)
  32. ;; custom modules
  33. (path-handling)
  34. (web-path-handling)
  35. (file-reader)
  36. (mime-types)
  37. (prefix (logging) log:)
  38. (templates)))
  39. (define respond
  40. (lambda* (#:optional body
  41. #:key
  42. (status 200)
  43. (title "This is my title!")
  44. (doctype "<!DOCTYPE html>\n")
  45. (content-type-params '((charset . "utf-8")))
  46. (content-type 'text/html)
  47. ;; Usually we have no exra headers by default.
  48. (extra-headers '())
  49. ;; If a body is provided use its templatized
  50. ;; form. and returns its last argument, if
  51. ;; previous arguments are #t.
  52. (sxml (and body (templatize title body))))
  53. "Respond to a request with the given SXML body. The SXML
  54. is put into the HTML template, which adds html, head, title,
  55. and body tag."
  56. ;; as before, answer in two parts, headers and body
  57. (values (build-response #:code status
  58. ;; headers are an alist
  59. #:headers
  60. `((content-type . (,content-type ,@content-type-params))
  61. ,@extra-headers))
  62. ;; Instead of returning the body as a string,
  63. ;; respond can be given a procedure, which will
  64. ;; be called by the web server to write out the
  65. ;; response to the client. This procedure gets
  66. ;; an output port as an argument. So you have 2
  67. ;; options: return string or return procedure
  68. ;; which takes a port.
  69. (λ (port)
  70. (when doctype (display doctype port))
  71. (cond
  72. [sxml
  73. (sxml->xml sxml port)]
  74. [else
  75. (sxml->xml '(p "no HTML body in response") port)])))))
  76. (define respond-static-asset
  77. (lambda* (static-asset-path
  78. #:key
  79. ;; By default assume the asset to exist in the file system.
  80. (status 200)
  81. (content-type-params '())
  82. (extra-headers '()))
  83. "Serve a static asset."
  84. (log:debug "serving a static asset for path:" static-asset-path)
  85. (let* ([file-ext (file-extension static-asset-path)]
  86. [mime-type (srfi-69:hash-table-ref file-extension-mime-types file-ext)]
  87. [content-type mime-type])
  88. (log:debug "responding with MIME type:" mime-type)
  89. (values (build-response
  90. #:code status
  91. #:headers
  92. `((content-type . (,content-type ,@content-type-params))
  93. ,@extra-headers))
  94. (λ (port)
  95. (let ([static-asset-data (read-file-to-bytevector static-asset-path)])
  96. (cond
  97. [static-asset-data
  98. (put-bytevector port static-asset-data)]
  99. [else
  100. (raise-exception
  101. (make-exception
  102. (make-non-continuable-error)
  103. (make-exception-with-message "no data read from file")
  104. (make-exception-with-irritants (list static-asset-path))
  105. (make-exception-with-origin 'respond-static-asset)))])))))))
  106. ;; (define make-respond-static-asset-handler
  107. ;; ;; take a static asset path
  108. ;; (λ (static-asset-path)
  109. ;; (log:debug "creating static asset handler, which takes 2 arguments")
  110. ;; ;; and return a procedure, which takes 2 arguments
  111. ;; (λ (request body)
  112. ;; (log:debug "inside static asset response handler")
  113. ;; ;; TODO: do we need to check for existence of the asset at the point?
  114. ;; (srfi-11:let-values
  115. ;; ([(resp-headers resp-body)
  116. ;; ;; TODO: does there need to be a lambda here?
  117. ;; (respond-static-asset static-asset-path)])
  118. ;; ;; return 2 values
  119. ;; (values resp-headers resp-body)))))