main.scm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. (import
  2. (except (rnrs base) let-values)
  3. (only (guile) lambda* λ error when display sleep)
  4. ;; Guile modules
  5. ;; alist->hash-table
  6. (prefix (ice-9 hash-table) ice9-hash-table:)
  7. ;; Guile exception handling
  8. (ice-9 exceptions)
  9. (ice-9 session)
  10. ;; for bytevector operations
  11. (ice-9 binary-ports)
  12. ;; SRFIs
  13. ;; hash tables
  14. (prefix (srfi srfi-69) srfi-69:)
  15. ;; receive form
  16. (prefix (srfi srfi-8) srfi-8:)
  17. ;; let-values
  18. (prefix (srfi srfi-11) srfi-11:)
  19. ;; list utils
  20. (prefix (srfi srfi-1) srfi-1:)
  21. ;; web server, concurrent
  22. (fibers web server)
  23. ;; standard web library
  24. (web request)
  25. (web response)
  26. (web uri)
  27. (sxml simple)
  28. ;; custom modules
  29. ;; model
  30. (model handler-config)
  31. ;; handlers
  32. (handlers index)
  33. (handlers schedule)
  34. (handlers resources)
  35. (handlers about)
  36. (handlers static-asset)
  37. (handlers debug)
  38. (handlers not-found-404)
  39. (middleware middleware)
  40. (lib utils response-utils)
  41. (lib utils request-utils)
  42. (lib utils list-utils)
  43. ;; (path-handling)
  44. (lib web-location-handling)
  45. (file-reader)
  46. (lib mime-types)
  47. (prefix (logging) log:)
  48. (templates helpers)
  49. (prefix (fslib) fslib:))
  50. ;;;
  51. ;;; SERVER
  52. ;;;
  53. ;; Here we define the routes and other server specific
  54. ;; stuff.
  55. ;; A routes-config is a hash that contains associations
  56. ;; between route parts and handlers.
  57. (define routes-config
  58. (srfi-69:alist->hash-table
  59. (list (cons '() (make-handler-config index-handler))
  60. (cons '("home") (make-handler-config index-handler))
  61. (cons '("schedule") (make-handler-config schedule-handler))
  62. (cons '("resources") (make-handler-config resources-handler))
  63. (cons '("about") (make-handler-config about-handler))
  64. (cons '("debug") (make-handler-config debug-handler))
  65. (cons '("static")
  66. (make-handler-config static-asset-handler
  67. #:handles-children #t)))))
  68. (define make-routes-dispatcher
  69. (lambda* (routes-config #:key (default-handler debug-handler))
  70. "make-routes-dispatcher returns a procedure, which, for
  71. each request, looks up the appropriate handler inside the
  72. given routes-config. As a fallback, a default-handler is
  73. given. In this case it is the debug-handler, which will
  74. render all headers."
  75. ;; NOTE: make-routes-dispatcher itself is not
  76. ;; responsible for answering to requests. The Guile web
  77. ;; server leaves the implementation details completely
  78. ;; to us and thus offers maximum flexibility in this
  79. ;; matter. We made the decision ourselves, that we want
  80. ;; to look at the request URI parts, to determin the
  81. ;; appropriate handler.
  82. (λ (request body)
  83. ;; Here we can have sequential actions. The first action in this example
  84. ;; is a logging middleware. We could make a middleware return a result,
  85. ;; which is then handed to the next middleware which might in turn
  86. ;; manipulate the result of the first one or create a new result or
  87. ;; whatever else we want to implement.
  88. (logging-middleware request body)
  89. (define req-path-components (request-path-components request))
  90. (log:debug "request path components:" req-path-components)
  91. ;; Try to find the request path in the routes. If it is not in the routes,
  92. ;; try to find each prefix of the request path in the routes. If a prefix
  93. ;; is in the routes, check, whether the handler of that route is
  94. ;; configured to match all requests starting with that request path
  95. ;; prefix.
  96. (let ([handler-conf
  97. (srfi-69:hash-table-ref routes-config
  98. req-path-components
  99. (λ () #f))])
  100. (cond
  101. [handler-conf
  102. ;; The request path has been found in the routes. Let the request be
  103. ;; handled by the corresponding handler.
  104. (log:debug "handler found for" req-path-components)
  105. ((handler-config-handler handler-conf) request body)]
  106. [else
  107. ;; Try to find request path prefixes in the routes. Reverse the prefixes
  108. ;; list, to check for longest prefixes first.
  109. (let ([req-path-prefixes (list-prefixes-long-to-short req-path-components)])
  110. (let ([prefix-request-path-handler
  111. (srfi-1:fold (λ (req-path-prefix acc)
  112. ;; Try to get a handler config for the request path
  113. ;; prefix.
  114. (let ([maybe-handler-conf
  115. (srfi-69:hash-table-ref routes-config
  116. req-path-prefix
  117. (λ () #f))])
  118. ;; If already a handler has been found, use
  119. ;; that one. This ensures to use the handler
  120. ;; for the longest matched prefix.
  121. (or acc
  122. ;; To actually match the request path, the
  123. ;; handler config must specify, that the handler
  124. ;; matches children of the route it is set for.
  125. (and (handler-config? maybe-handler-conf)
  126. (handler-config-handles-children? maybe-handler-conf)
  127. (handler-config-handler maybe-handler-conf)))))
  128. #f
  129. req-path-prefixes)])
  130. (cond
  131. [prefix-request-path-handler
  132. (log:debug "prefix handler found for" req-path-components)
  133. (prefix-request-path-handler request body)]
  134. ;; If even a handler for a prefix of the request path could not be
  135. ;; found, answer with a 404 response.
  136. [else
  137. (log:debug "no handler found for" req-path-components)
  138. (not-found-404-handler request body)])))])))))
  139. (define main
  140. (λ ()
  141. (log:debug "Starting the web server ...")
  142. ;; Start the server. The run-server procedure expects to
  143. ;; be given a procedure, which will dispatch requests to
  144. ;; whatever is responsible for handling the
  145. ;; requests. Theoretically one could implement all
  146. ;; inside this dispatcher, but that would be less clean.
  147. (run-server
  148. (make-routes-dispatcher routes-config #:default-handler debug-handler))
  149. (log:debug "Stopped web server.")))
  150. (main)