main.scm 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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. (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. (templates)))
  41. ;;;
  42. ;;; SERVER
  43. ;;;
  44. ;; Here we define the routes and other server specific
  45. ;; stuff.
  46. ;; A routes-config is a hash that contains associations
  47. ;; between route parts and handlers.
  48. (define routes-config
  49. (srfi-69:alist->hash-table
  50. ;; Using (quote ...) would not evaluate the handlers in
  51. ;; the list so we need quasiquote unquote.
  52. `((("hello" "world") . ,hello-world-handler)
  53. (("debug") . ,debug-handler))))
  54. (define make-routes-dispatcher
  55. (lambda* (routes-config #:key (default-handler debug-handler))
  56. "make-routes-dispatcher returns a procedure, which, for
  57. each request, looks up the appropriate handler inside the
  58. given routes-config. As a fallback, a default-handler is
  59. given. In this case it is the debug-handler, which will
  60. render all headers."
  61. ;; NOTE: make-routes-dispatcher itself is not
  62. ;; responsible for answering to requests. The Guile web
  63. ;; server leaves the implementation details completely
  64. ;; to us and thus offers maximum flexibility in this
  65. ;; matter. We made the decision ourselves, that we want
  66. ;; to look at the request URI parts, to determin the
  67. ;; appropriate handler.
  68. (λ (request body)
  69. (log:debug "-----------------------------------------------")
  70. (log:debug "(request-path-components request):" (request-path-components request))
  71. (let* ([req-path-comp (request-path-components request)]
  72. [req-path (if (null? req-path-comp)
  73. "/"
  74. (apply path-join req-path-comp))])
  75. (log:debug "request path is:" req-path)
  76. (cond
  77. ;; NOTE/TODO: Perhaps we have to translate the
  78. ;; request path to a file system path first.
  79. [(static-asset-path? req-path)
  80. (log:debug "request path is a static asset path:" req-path)
  81. ;; Check, whether the static asset route is OK to
  82. ;; access. If static asset is OK to access, then
  83. ;; serve it.
  84. (cond
  85. ;; All security hinges on
  86. ;; safe/existing/static-asset-path?, so it better
  87. ;; be secure!
  88. [(safe/existing/static-asset-path? req-path)
  89. (respond-static-asset req-path)]
  90. ;; If the path is not safe, refuse, by answering
  91. ;; with a 404 HTTP status code.
  92. [else
  93. (log:debug "using 404 handler for" req-path-comp)
  94. (not-found-404-handler request body)])]
  95. [else
  96. ;; Here we can have sequential actions. The first
  97. ;; action in this example is a logging
  98. ;; middleware. We could make a middleware return a
  99. ;; result, which is then handed to the next
  100. ;; middleware which might in turn manipulate the
  101. ;; result of the first one or create a new result
  102. ;; or whatever else we want to implement.
  103. (log:debug "not a static asset path" req-path-comp)
  104. (logging-middleware request body)
  105. ;; Only after logging the real request handling
  106. ;; begins. First we get the appropriate handler
  107. ;; and then we hand it the request.
  108. (let* ([route-parts (request-path-components request)]
  109. [handler
  110. (srfi-69:hash-table-ref routes-config
  111. route-parts
  112. ;; SRFI-69 wants a
  113. ;; thunk, which is
  114. ;; more flexible
  115. ;; than a simple
  116. ;; default value.
  117. (λ () default-handler))])
  118. ;; Hand the handler the request and the body of
  119. ;; the request.
  120. (handler request body))])))))
  121. (define main
  122. (λ ()
  123. (log:debug "Starting the web server ...")
  124. ;; Start the server. The run-server procedure expects to
  125. ;; be given a procedure, which will dispatch requests to
  126. ;; whatever is responsible for handling the
  127. ;; requests. Theoretically one could implement all
  128. ;; inside this dispatcher, but that would be less clean.
  129. (run-server
  130. (make-routes-dispatcher routes-config #:default-handler debug-handler))
  131. (log:debug "Stopped web server.")))
  132. (main)