main.scm 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. (library (example-web-server (0 0 1))
  2. (export main)
  3. (import
  4. (except (rnrs base) let-values)
  5. (only (guile) lambda* λ error when display sleep)
  6. ;; Guile modules
  7. ;; alist->hash-table
  8. (prefix (ice-9 hash-table) ice9-hash-table:)
  9. ;; Guile exception handling
  10. (ice-9 exceptions)
  11. (ice-9 session)
  12. ;; for bytevector operations
  13. (ice-9 binary-ports)
  14. ;; SRFIs
  15. ;; hash tables
  16. (prefix (srfi srfi-69) srfi-69:)
  17. ;; receive form
  18. (prefix (srfi srfi-8) srfi-8:)
  19. ;; let-values
  20. (prefix (srfi srfi-11) srfi-11:)
  21. ;; list utils
  22. (prefix (srfi srfi-1) srfi-1:)
  23. ;; web server, concurrent
  24. (fibers web server)
  25. ;; standard web library
  26. (web request)
  27. (web response)
  28. (web uri)
  29. (sxml simple)
  30. ;; custom modules
  31. (prefix (handlers) handlers:)
  32. (middleware)
  33. (response-utils)
  34. (request-utils)
  35. (path-handling)
  36. (web-path-handling)
  37. (file-reader)
  38. (mime-types)
  39. (prefix (logging) log:)))
  40. ;;;
  41. ;;; SERVER
  42. ;;;
  43. ;; Here we define the routes and other server specific
  44. ;; stuff.
  45. ;; A routes-config is a hash that contains associations
  46. ;; between route parts and handlers.
  47. (define routes-config
  48. (srfi-69:alist->hash-table
  49. ;; Using (quote ...) would not evaluate the handlers in
  50. ;; the list so we need quasiquote unquote.
  51. `((() . ,handlers:index)
  52. (("debug") . ,handlers:debug))))
  53. (define make-routes-dispatcher
  54. (lambda* (routes-config #:key (default-handler handlers:debug))
  55. "make-routes-dispatcher returns a procedure, which, for
  56. each request, looks up the appropriate handler inside the
  57. given routes-config. As a fallback, a default-handler is
  58. given. In this case it is the debug-handler, which will
  59. render all headers."
  60. ;; NOTE: make-routes-dispatcher itself is not
  61. ;; responsible for answering to requests. The Guile web
  62. ;; server leaves the implementation details completely
  63. ;; to us and thus offers maximum flexibility in this
  64. ;; matter. We made the decision ourselves, that we want
  65. ;; to look at the request URI parts, to determin the
  66. ;; appropriate handler.
  67. (λ (request body)
  68. (log:debug "-----------------------------------------------")
  69. (log:debug "(request-path-components request):" (request-path-components request))
  70. (let* ([req-path-comp (request-path-components request)]
  71. [req-path (if (null? req-path-comp)
  72. "/"
  73. (apply path-join req-path-comp))])
  74. (log:debug "request path is:" req-path)
  75. (cond
  76. ;; NOTE/TODO: Perhaps we have to translate the
  77. ;; request path to a file system path first.
  78. [(static-asset-path? req-path)
  79. (log:debug "request path is a static asset path:" req-path)
  80. ;; Check, whether the static asset route is OK to
  81. ;; access. If static asset is OK to access, then
  82. ;; serve it.
  83. (cond
  84. ;; All security hinges on
  85. ;; safe/existing/static-asset-path?, so it better
  86. ;; be secure!
  87. [(safe/existing/static-asset-path? req-path)
  88. (respond-static-asset req-path)]
  89. ;; If the path is not safe, refuse, by answering
  90. ;; with a 404 HTTP status code.
  91. [else
  92. (log:debug "using 404 handler for" req-path-comp)
  93. (handlers:not-found-404 request body)])]
  94. [else
  95. ;; Here we can have sequential actions. The first
  96. ;; action in this example is a logging
  97. ;; middleware. We could make a middleware return a
  98. ;; result, which is then handed to the next
  99. ;; middleware which might in turn manipulate the
  100. ;; result of the first one or create a new result
  101. ;; or whatever else we want to implement.
  102. (log:debug "not a static asset path" req-path-comp)
  103. (logging-middleware request body)
  104. ;; Only after logging the real request handling
  105. ;; begins. First we get the appropriate handler
  106. ;; and then we hand it the request.
  107. (let* ([route-parts (request-path-components request)]
  108. [handler
  109. (srfi-69:hash-table-ref routes-config
  110. route-parts
  111. ;; SRFI-69 wants a
  112. ;; thunk, which is
  113. ;; more flexible
  114. ;; than a simple
  115. ;; default value.
  116. (λ () default-handler))])
  117. ;; Hand the handler the request and the body of
  118. ;; the request.
  119. (handler request body))])))))
  120. (define main
  121. (λ ()
  122. (log:debug "Starting the web server ...")
  123. ;; Start the server. The run-server procedure expects to
  124. ;; be given a procedure, which will dispatch requests to
  125. ;; whatever is responsible for handling the
  126. ;; requests. Theoretically one could implement all
  127. ;; inside this dispatcher, but that would be less clean.
  128. (run-server
  129. (make-routes-dispatcher routes-config #:default-handler handlers:debug))
  130. (log:debug "Stopped web server.")))
  131. (main)