itsi 0.1.20 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -8
  3. data/Cargo.lock +2 -2
  4. data/LICENSE.txt +698 -0
  5. data/README.md +15 -4
  6. data/Rakefile +9 -5
  7. data/crates/itsi_acme/.gitignore +4 -0
  8. data/crates/itsi_acme/Cargo.toml +86 -0
  9. data/crates/itsi_acme/LICENSE-APACHE +201 -0
  10. data/crates/itsi_acme/LICENSE-MIT +23 -0
  11. data/crates/itsi_acme/README.md +9 -0
  12. data/crates/itsi_acme/examples/high_level.rs +63 -0
  13. data/crates/itsi_acme/examples/high_level_warp.rs +52 -0
  14. data/crates/itsi_acme/examples/low_level.rs +87 -0
  15. data/crates/itsi_acme/examples/low_level_axum.rs +66 -0
  16. data/crates/itsi_acme/src/acceptor.rs +81 -0
  17. data/crates/itsi_acme/src/acme.rs +354 -0
  18. data/crates/itsi_acme/src/axum.rs +86 -0
  19. data/crates/itsi_acme/src/cache.rs +39 -0
  20. data/crates/itsi_acme/src/caches/boxed.rs +80 -0
  21. data/crates/itsi_acme/src/caches/composite.rs +69 -0
  22. data/crates/itsi_acme/src/caches/dir.rs +106 -0
  23. data/crates/itsi_acme/src/caches/mod.rs +11 -0
  24. data/crates/itsi_acme/src/caches/no.rs +78 -0
  25. data/crates/itsi_acme/src/caches/test.rs +136 -0
  26. data/crates/itsi_acme/src/config.rs +172 -0
  27. data/crates/itsi_acme/src/https_helper.rs +69 -0
  28. data/crates/itsi_acme/src/incoming.rs +142 -0
  29. data/crates/itsi_acme/src/jose.rs +161 -0
  30. data/crates/itsi_acme/src/lib.rs +142 -0
  31. data/crates/itsi_acme/src/resolver.rs +59 -0
  32. data/crates/itsi_acme/src/state.rs +424 -0
  33. data/crates/itsi_rb_helpers/src/lib.rs +4 -3
  34. data/crates/itsi_scheduler/Cargo.toml +1 -1
  35. data/crates/itsi_scheduler/src/itsi_scheduler.rs +8 -2
  36. data/crates/itsi_scheduler/src/lib.rs +1 -0
  37. data/crates/itsi_server/Cargo.toml +1 -1
  38. data/crates/itsi_server/src/lib.rs +2 -1
  39. data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +18 -1
  40. data/crates/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +11 -3
  41. data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +122 -63
  42. data/crates/itsi_server/src/ruby_types/itsi_server.rs +2 -0
  43. data/crates/itsi_server/src/server/binds/bind.rs +3 -0
  44. data/crates/itsi_server/src/server/binds/listener.rs +12 -5
  45. data/crates/itsi_server/src/server/binds/tls.rs +13 -5
  46. data/crates/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +12 -5
  47. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +8 -1
  48. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +9 -1
  49. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +48 -43
  50. data/crates/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +11 -2
  51. data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +39 -12
  52. data/crates/itsi_server/src/server/middleware_stack/middlewares/cors.rs +36 -27
  53. data/crates/itsi_server/src/server/middleware_stack/middlewares/csp.rs +25 -11
  54. data/crates/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +12 -3
  55. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +74 -72
  56. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +15 -1
  57. data/crates/itsi_server/src/server/middleware_stack/middlewares/etag.rs +11 -8
  58. data/crates/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +19 -11
  59. data/crates/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +5 -5
  60. data/crates/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +2 -2
  61. data/crates/itsi_server/src/server/middleware_stack/middlewares/mod.rs +11 -5
  62. data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +17 -20
  63. data/crates/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +19 -8
  64. data/crates/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +16 -37
  65. data/crates/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +22 -12
  66. data/crates/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +26 -11
  67. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +7 -1
  68. data/crates/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +14 -4
  69. data/crates/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +19 -0
  70. data/crates/itsi_server/src/server/middleware_stack/mod.rs +49 -13
  71. data/crates/itsi_server/src/server/mod.rs +1 -0
  72. data/crates/itsi_server/src/server/redirect_type.rs +26 -0
  73. data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +22 -16
  74. data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +49 -12
  75. data/crates/itsi_server/src/server/signal.rs +1 -0
  76. data/crates/itsi_server/src/server/size_limited_incoming.rs +6 -0
  77. data/crates/itsi_server/src/server/thread_worker.rs +5 -1
  78. data/crates/itsi_server/src/services/itsi_http_service.rs +20 -2
  79. data/crates/itsi_server/src/services/rate_limiter.rs +15 -4
  80. data/crates/itsi_server/src/services/static_file_server.rs +33 -19
  81. data/crates/itsi_tracing/src/lib.rs +42 -22
  82. data/docs/content/_index.md +1 -2
  83. data/docs/content/acknowledgements/_index.md +5 -2
  84. data/docs/content/configuration/_index.md +8 -5
  85. data/docs/content/contact/_index.md +8 -1
  86. data/docs/content/faqs/_index.md +5 -3
  87. data/docs/content/features/_index.md +56 -50
  88. data/docs/content/getting_started/_index.md +8 -5
  89. data/docs/content/getting_started/local_development.md +68 -8
  90. data/docs/content/getting_started/logging.md +16 -9
  91. data/docs/content/getting_started/running_itsi_in_production.md +5 -3
  92. data/docs/content/getting_started/signals.md +38 -0
  93. data/docs/content/itsi_scheduler/_index.md +8 -7
  94. data/docs/content/utilities/_index.md +13 -0
  95. data/docs/content/utilities/config_file_testing.md +17 -0
  96. data/docs/content/utilities/passfile_generator.md +41 -0
  97. data/docs/content/utilities/route_testing.md +27 -0
  98. data/docs/content/utilities/secrets_management.md +30 -0
  99. data/docs/hugo.yaml +1 -1
  100. data/fairytale.txt +3 -4
  101. data/gems/scheduler/Cargo.lock +1 -1
  102. data/gems/scheduler/README.md +4 -5
  103. data/gems/scheduler/Rakefile +0 -4
  104. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  105. data/gems/scheduler/lib/itsi/scheduler.rb +9 -4
  106. data/gems/scheduler/test/test_active_record.rb +12 -7
  107. data/gems/server/Cargo.lock +1 -1
  108. data/gems/server/Rakefile +0 -4
  109. data/gems/server/exe/itsi +13 -2
  110. data/gems/server/lib/itsi/http_request/response_status_shortcodes.rb +2 -0
  111. data/gems/server/lib/itsi/http_request.rb +40 -9
  112. data/gems/server/lib/itsi/http_response.rb +2 -1
  113. data/gems/server/lib/itsi/passfile.rb +0 -1
  114. data/gems/server/lib/itsi/server/config/config_helpers.rb +20 -8
  115. data/gems/server/lib/itsi/server/config/dsl.rb +20 -435
  116. data/gems/server/lib/itsi/server/config/known_paths.rb +4 -1
  117. data/gems/server/lib/itsi/server/config/middleware/_index.md +6 -4
  118. data/gems/server/lib/itsi/server/config/middleware/allow_list.md +46 -0
  119. data/gems/server/lib/itsi/server/config/middleware/allow_list.rb +42 -0
  120. data/gems/server/lib/itsi/server/config/middleware/auth_api_key.md +90 -0
  121. data/gems/server/lib/itsi/server/config/middleware/auth_api_key.rb +51 -0
  122. data/gems/server/lib/itsi/server/config/middleware/auth_basic.md +45 -0
  123. data/gems/server/lib/itsi/server/config/middleware/auth_basic.rb +44 -0
  124. data/gems/server/lib/itsi/server/config/middleware/auth_jwt.md +82 -0
  125. data/gems/server/lib/itsi/server/config/middleware/auth_jwt.rb +38 -0
  126. data/gems/server/lib/itsi/server/config/middleware/cache_control.md +78 -0
  127. data/gems/server/lib/itsi/server/config/middleware/cache_control.rb +45 -0
  128. data/gems/server/lib/itsi/server/config/middleware/cidr_to_regex.rb +50 -0
  129. data/gems/server/lib/itsi/server/config/middleware/compression.md +50 -0
  130. data/gems/server/lib/itsi/server/config/middleware/compression.rb +37 -0
  131. data/gems/server/lib/itsi/server/config/middleware/cors.md +93 -0
  132. data/gems/server/lib/itsi/server/config/middleware/cors.rb +32 -0
  133. data/gems/server/lib/itsi/server/config/middleware/csp.md +37 -0
  134. data/gems/server/lib/itsi/server/config/middleware/csp.rb +44 -0
  135. data/gems/server/lib/itsi/server/config/middleware/deny_list.md +45 -0
  136. data/gems/server/lib/itsi/server/config/middleware/deny_list.rb +42 -0
  137. data/gems/server/lib/itsi/server/config/middleware/endpoint/_index.md +159 -0
  138. data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.md +186 -0
  139. data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.rb +33 -0
  140. data/gems/server/lib/itsi/server/config/middleware/endpoint/delete.md +12 -0
  141. data/gems/server/lib/itsi/server/config/middleware/endpoint/delete.rb +42 -0
  142. data/gems/server/lib/itsi/server/config/middleware/endpoint/endpoint.rb +99 -0
  143. data/gems/server/lib/itsi/server/config/middleware/endpoint/get.md +12 -0
  144. data/gems/server/lib/itsi/server/config/middleware/endpoint/get.rb +42 -0
  145. data/gems/server/lib/itsi/server/config/middleware/endpoint/http_request.md +44 -0
  146. data/gems/server/lib/itsi/server/config/middleware/endpoint/http_response.md +39 -0
  147. data/gems/server/lib/itsi/server/config/middleware/endpoint/patch.md +12 -0
  148. data/gems/server/lib/itsi/server/config/middleware/endpoint/patch.rb +42 -0
  149. data/gems/server/lib/itsi/server/config/middleware/endpoint/post.md +12 -0
  150. data/gems/server/lib/itsi/server/config/middleware/endpoint/post.rb +42 -0
  151. data/gems/server/lib/itsi/server/config/middleware/endpoint/put.md +12 -0
  152. data/gems/server/lib/itsi/server/config/middleware/endpoint/put.rb +42 -0
  153. data/gems/server/lib/itsi/server/config/middleware/endpoint/schemas.md +122 -0
  154. data/gems/server/lib/itsi/server/config/middleware/error_response.md +61 -0
  155. data/gems/server/lib/itsi/server/config/middleware/error_response.rb +36 -0
  156. data/gems/server/lib/itsi/server/config/middleware/etag.md +59 -0
  157. data/gems/server/lib/itsi/server/config/middleware/etag.rb +27 -0
  158. data/gems/server/lib/itsi/server/config/middleware/grpc.md +172 -0
  159. data/gems/server/lib/itsi/server/config/middleware/grpc.rb +54 -0
  160. data/gems/server/lib/itsi/server/config/middleware/intrusion_protection.md +124 -0
  161. data/gems/server/lib/itsi/server/config/middleware/intrusion_protection.rb +61 -0
  162. data/gems/server/lib/itsi/server/config/middleware/location.md +107 -0
  163. data/gems/server/lib/itsi/server/config/middleware/location.rb +99 -0
  164. data/gems/server/lib/itsi/server/config/middleware/log_requests.md +13 -11
  165. data/gems/server/lib/itsi/server/config/middleware/log_requests.rb +1 -3
  166. data/gems/server/lib/itsi/server/config/middleware/max_body.md +18 -0
  167. data/gems/server/lib/itsi/server/config/middleware/max_body.rb +21 -0
  168. data/gems/server/lib/itsi/server/config/middleware/proxy.md +62 -0
  169. data/gems/server/lib/itsi/server/config/middleware/proxy.rb +41 -0
  170. data/gems/server/lib/itsi/server/config/middleware/rackup_file.md +54 -0
  171. data/gems/server/lib/itsi/server/config/middleware/rackup_file.rb +44 -0
  172. data/gems/server/lib/itsi/server/config/middleware/rate_limit.md +126 -0
  173. data/gems/server/lib/itsi/server/config/middleware/rate_limit.rb +34 -0
  174. data/gems/server/lib/itsi/server/config/middleware/rate_limit_store.rb +25 -0
  175. data/gems/server/lib/itsi/server/config/middleware/redirect.md +55 -0
  176. data/gems/server/lib/itsi/server/config/middleware/redirect.rb +25 -0
  177. data/gems/server/lib/itsi/server/config/middleware/request_headers.md +34 -0
  178. data/gems/server/lib/itsi/server/config/middleware/request_headers.rb +24 -0
  179. data/gems/server/lib/itsi/server/config/middleware/response_headers.md +33 -0
  180. data/gems/server/lib/itsi/server/config/middleware/response_headers.rb +25 -0
  181. data/gems/server/lib/itsi/server/config/middleware/run.md +60 -0
  182. data/gems/server/lib/itsi/server/config/middleware/run.rb +43 -0
  183. data/gems/server/lib/itsi/server/config/middleware/static_assets.md +73 -0
  184. data/gems/server/lib/itsi/server/config/middleware/static_assets.rb +87 -0
  185. data/gems/server/lib/itsi/server/config/middleware/static_response.md +44 -0
  186. data/gems/server/lib/itsi/server/config/middleware/static_response.rb +29 -0
  187. data/gems/server/lib/itsi/server/config/middleware/string_rewrite.md +67 -0
  188. data/gems/server/lib/itsi/server/config/middleware/token_source.rb +32 -0
  189. data/gems/server/lib/itsi/server/config/middleware.rb +4 -0
  190. data/gems/server/lib/itsi/server/config/option.rb +5 -0
  191. data/gems/server/lib/itsi/server/config/options/_index.md +3 -2
  192. data/gems/server/lib/itsi/server/config/options/auto_reload_config.md +13 -0
  193. data/gems/server/lib/itsi/server/config/options/auto_reload_config.rb +41 -0
  194. data/gems/server/lib/itsi/server/config/options/bind.md +71 -0
  195. data/gems/server/lib/itsi/server/config/options/bind.rb +26 -0
  196. data/gems/server/lib/itsi/server/config/options/certificates.md +65 -0
  197. data/gems/server/lib/itsi/server/config/options/daemonize.md +14 -0
  198. data/gems/server/lib/itsi/server/config/options/daemonize.rb +19 -0
  199. data/gems/server/lib/itsi/server/config/options/fiber_scheduler.md +1 -2
  200. data/gems/server/lib/itsi/server/config/options/fiber_scheduler.rb +6 -3
  201. data/gems/server/lib/itsi/server/config/options/header_read_timeout.md +17 -0
  202. data/gems/server/lib/itsi/server/config/options/header_read_timeout.rb +19 -0
  203. data/gems/server/lib/itsi/server/config/options/hooks/_index.md +11 -0
  204. data/gems/server/lib/itsi/server/config/options/hooks/after_fork.md +13 -0
  205. data/gems/server/lib/itsi/server/config/options/hooks/after_fork.rb +28 -0
  206. data/gems/server/lib/itsi/server/config/options/hooks/after_memory_limit_reached.md +14 -0
  207. data/gems/server/lib/itsi/server/config/options/hooks/after_memory_limit_reached.rb +28 -0
  208. data/gems/server/lib/itsi/server/config/options/hooks/after_start.md +12 -0
  209. data/gems/server/lib/itsi/server/config/options/hooks/after_start.rb +28 -0
  210. data/gems/server/lib/itsi/server/config/options/hooks/before_fork.md +13 -0
  211. data/gems/server/lib/itsi/server/config/options/hooks/before_fork.rb +28 -0
  212. data/gems/server/lib/itsi/server/config/options/hooks/before_restart.md +12 -0
  213. data/gems/server/lib/itsi/server/config/options/hooks/before_restart.rb +28 -0
  214. data/gems/server/lib/itsi/server/config/options/hooks/before_shutdown.md +12 -0
  215. data/gems/server/lib/itsi/server/config/options/hooks/before_shutdown.rb +28 -0
  216. data/gems/server/lib/itsi/server/config/options/include.md +20 -0
  217. data/gems/server/lib/itsi/server/config/options/include.rb +36 -0
  218. data/gems/server/lib/itsi/server/config/options/listen_backlog.md +11 -0
  219. data/gems/server/lib/itsi/server/config/options/listen_backlog.rb +19 -0
  220. data/gems/server/lib/itsi/server/config/options/log_format.md +18 -0
  221. data/gems/server/lib/itsi/server/config/options/log_format.rb +19 -0
  222. data/gems/server/lib/itsi/server/config/options/log_level.md +34 -0
  223. data/gems/server/lib/itsi/server/config/options/log_level.rb +20 -0
  224. data/gems/server/lib/itsi/server/config/options/log_target.md +38 -0
  225. data/gems/server/lib/itsi/server/config/options/log_target.rb +19 -0
  226. data/gems/server/lib/itsi/server/config/options/log_target_filters.md +17 -0
  227. data/gems/server/lib/itsi/server/config/options/log_target_filters.rb +19 -0
  228. data/gems/server/lib/itsi/server/config/options/multithreaded_reactor.md +27 -0
  229. data/gems/server/lib/itsi/server/config/options/multithreaded_reactor.rb +24 -0
  230. data/gems/server/lib/itsi/server/config/options/nodelay.md +16 -0
  231. data/gems/server/lib/itsi/server/config/options/nodelay.rb +19 -0
  232. data/gems/server/lib/itsi/server/config/options/oob_gc_responses_threshold.md +19 -0
  233. data/gems/server/lib/itsi/server/config/options/oob_gc_responses_threshold.rb +18 -0
  234. data/gems/server/lib/itsi/server/config/options/pin_worker_cores.md +17 -0
  235. data/gems/server/lib/itsi/server/config/options/pin_worker_cores.rb +19 -0
  236. data/gems/server/lib/itsi/server/config/options/preload.md +21 -0
  237. data/gems/server/lib/itsi/server/config/options/preload.rb +18 -0
  238. data/gems/server/lib/itsi/server/config/options/recv_buffer_size.md +15 -0
  239. data/gems/server/lib/itsi/server/config/options/recv_buffer_size.rb +19 -0
  240. data/gems/server/lib/itsi/server/config/options/redirect_http_to_https.md +21 -0
  241. data/gems/server/lib/itsi/server/config/options/redirect_http_to_https.rb +30 -0
  242. data/gems/server/lib/itsi/server/config/options/request_timeout.md +23 -0
  243. data/gems/server/lib/itsi/server/config/options/request_timeout.rb +19 -0
  244. data/gems/server/lib/itsi/server/config/options/reuse_address.md +16 -0
  245. data/gems/server/lib/itsi/server/config/options/reuse_address.rb +19 -0
  246. data/gems/server/lib/itsi/server/config/options/reuse_port.md +16 -0
  247. data/gems/server/lib/itsi/server/config/options/reuse_port.rb +19 -0
  248. data/gems/server/lib/itsi/server/config/options/scheduler_threads.md +34 -0
  249. data/gems/server/lib/itsi/server/config/options/scheduler_threads.rb +17 -0
  250. data/gems/server/lib/itsi/server/config/options/shutdown_timeout.md +17 -0
  251. data/gems/server/lib/itsi/server/config/options/shutdown_timeout.rb +19 -0
  252. data/gems/server/lib/itsi/server/config/options/stream_body.md +32 -0
  253. data/gems/server/lib/itsi/server/config/options/stream_body.rb +18 -0
  254. data/gems/server/lib/itsi/server/config/options/threads.md +7 -2
  255. data/gems/server/lib/itsi/server/config/options/threads.rb +1 -1
  256. data/gems/server/lib/itsi/server/config/options/watch.md +16 -0
  257. data/gems/server/lib/itsi/server/config/options/watch.rb +28 -0
  258. data/gems/server/lib/itsi/server/config/options/worker_memory_limit.md +22 -0
  259. data/gems/server/lib/itsi/server/config/options/worker_memory_limit.rb +18 -0
  260. data/gems/server/lib/itsi/server/config/options/workers.md +1 -2
  261. data/gems/server/lib/itsi/server/config/options/workers.rb +1 -1
  262. data/gems/server/lib/itsi/server/config/typed_struct.rb +59 -20
  263. data/gems/server/lib/itsi/server/config.rb +77 -48
  264. data/gems/server/lib/itsi/server/default_config/Itsi.rb +3 -3
  265. data/gems/server/lib/itsi/server/grpc/grpc_call.rb +1 -1
  266. data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +11 -4
  267. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +3 -3
  268. data/gems/server/lib/itsi/server/route_tester.rb +58 -8
  269. data/gems/server/lib/itsi/server/signal_trap.rb +1 -1
  270. data/gems/server/lib/itsi/server/typed_handlers/param_parser.rb +14 -18
  271. data/gems/server/lib/itsi/server/typed_handlers/source_parser.rb +5 -4
  272. data/gems/server/lib/itsi/server/typed_handlers.rb +12 -4
  273. data/gems/server/lib/itsi/server/version.rb +1 -1
  274. data/gems/server/lib/itsi/server.rb +98 -14
  275. data/gems/server/lib/ruby_lsp/itsi/addon.rb +20 -18
  276. data/gems/server/test/helpers/test_helper.rb +89 -29
  277. data/gems/server/test/middleware/allow_list.rb +128 -0
  278. data/gems/server/test/middleware/auth_api_key.rb +141 -0
  279. data/gems/server/test/middleware/auth_basic.rb +91 -0
  280. data/gems/server/test/middleware/auth_jwt.rb +214 -0
  281. data/gems/server/test/middleware/cache_control.rb +82 -0
  282. data/gems/server/test/middleware/cidr_to_regex.rb +46 -0
  283. data/gems/server/test/middleware/compression.rb +89 -0
  284. data/gems/server/test/middleware/cors.rb +113 -0
  285. data/gems/server/test/middleware/csp.rb +62 -0
  286. data/gems/server/test/middleware/deny_list.rb +131 -0
  287. data/gems/server/test/middleware/endpoint.rb +300 -0
  288. data/gems/server/test/middleware/etag.rb +75 -0
  289. data/gems/server/test/middleware/grpc/grpc.rb +158 -0
  290. data/gems/server/test/middleware/grpc/test_service.proto +32 -0
  291. data/gems/server/test/middleware/grpc/test_service_impl.rb +28 -0
  292. data/gems/server/test/middleware/grpc/test_service_pb.rb +18 -0
  293. data/gems/server/test/middleware/grpc/test_service_services_pb.rb +30 -0
  294. data/gems/server/test/middleware/header_interpolation.rb +35 -0
  295. data/gems/server/test/middleware/intrusion_protection.rb +259 -0
  296. data/gems/server/test/middleware/location.rb +220 -0
  297. data/gems/server/test/middleware/max_body.rb +20 -0
  298. data/gems/server/test/middleware/proxy.rb +415 -0
  299. data/gems/server/test/middleware/rate_limit.rb +211 -0
  300. data/gems/server/test/middleware/redirect.rb +85 -0
  301. data/gems/server/test/middleware/request_headers.rb +50 -0
  302. data/gems/server/test/middleware/response_headers.rb +50 -0
  303. data/gems/server/test/middleware/static_assets.rb +374 -0
  304. data/gems/server/test/middleware/static_response.rb +56 -0
  305. data/gems/server/test/middleware/string_rewrite.rb +112 -0
  306. data/gems/server/test/options/bind.rb +47 -0
  307. data/gems/server/test/options/header_read_timeout.rb +23 -0
  308. data/gems/server/test/options/test_request_timeout.rb +16 -0
  309. data/gems/server/test/options/test_workers.rb +2 -4
  310. data/gems/server/test/{test_itsi_server.rb → rack/test_rack_server.rb} +2 -2
  311. data/grpc_test/Itsi.rb +11 -0
  312. data/grpc_test/echo.proto +14 -0
  313. data/grpc_test/echo_pb.rb +16 -0
  314. data/grpc_test/echo_service_impl.rb +8 -0
  315. data/grpc_test/echo_services_pb.rb +22 -0
  316. data/lib/itsi/version.rb +1 -1
  317. data/tasks.txt +15 -72
  318. metadata +210 -7
  319. data/gems/server/lib/itsi/server/default_config/Itsi-rackup.rb +0 -119
@@ -18,7 +18,7 @@ use std::{
18
18
  collections::{HashMap, HashSet},
19
19
  sync::OnceLock,
20
20
  };
21
- use tracing::{debug, error};
21
+ use tracing::debug;
22
22
 
23
23
  #[derive(Debug, Clone, Deserialize)]
24
24
  pub struct AuthJwt {
@@ -32,6 +32,10 @@ pub struct AuthJwt {
32
32
  pub audiences: Option<HashSet<String>>,
33
33
  pub subjects: Option<HashSet<String>>,
34
34
  pub issuers: Option<HashSet<String>>,
35
+ #[serde(skip_deserializing)]
36
+ pub audience_vec: OnceLock<Option<Vec<String>>>,
37
+ #[serde(skip_deserializing)]
38
+ pub issuer_vec: OnceLock<Option<Vec<String>>>,
35
39
  pub leeway: Option<u64>,
36
40
  #[serde(default = "unauthorized_error_response")]
37
41
  pub error_response: ErrorResponse,
@@ -91,7 +95,7 @@ impl JwtAlgorithm {
91
95
  /// Given a base64-encoded key string, decode and construct a jsonwebtoken::DecodingKey.
92
96
  pub fn key_from(&self, base64: &str) -> itsi_error::Result<DecodingKey> {
93
97
  match self {
94
- // For HMAC algorithms, use the secret directly.
98
+ // For HMAC algorithms, expect a base64 encoded secret.
95
99
  JwtAlgorithm::Hs256 | JwtAlgorithm::Hs384 | JwtAlgorithm::Hs512 => {
96
100
  Ok(DecodingKey::from_secret(
97
101
  &general_purpose::STANDARD
@@ -118,15 +122,16 @@ impl JwtAlgorithm {
118
122
 
119
123
  #[derive(Debug, Deserialize)]
120
124
  #[serde(untagged)]
125
+ #[allow(dead_code)]
121
126
  enum Audience {
122
127
  Single(String),
123
128
  Multiple(Vec<String>),
124
129
  }
125
130
 
126
131
  #[derive(Debug, Deserialize)]
132
+ #[allow(dead_code)]
127
133
  struct Claims {
128
134
  // Here we assume the token includes an expiration.
129
- #[allow(dead_code)]
130
135
  exp: usize,
131
136
  // The audience claim may be a single string or an array.
132
137
  aud: Option<Audience>,
@@ -171,6 +176,17 @@ impl MiddlewareLayer for AuthJwt {
171
176
  self.keys
172
177
  .set(keys)
173
178
  .map_err(|_| ItsiError::new("Failed to set keys"))?;
179
+
180
+ if let Some(audiences) = self.audiences.as_ref() {
181
+ self.audience_vec
182
+ .set(Some(audiences.iter().cloned().collect::<Vec<_>>()))
183
+ .ok();
184
+ }
185
+ if let Some(issuers) = self.issuers.as_ref() {
186
+ self.issuer_vec
187
+ .set(Some(issuers.iter().cloned().collect::<Vec<_>>()))
188
+ .ok();
189
+ }
174
190
  Ok(())
175
191
  }
176
192
 
@@ -219,8 +235,17 @@ impl MiddlewareLayer for AuthJwt {
219
235
  ));
220
236
  }
221
237
  let token_str = token_str.unwrap();
222
- let header =
223
- decode_header(token_str).map_err(|_| ItsiError::new("Invalid token header"))?;
238
+ let header = match decode_header(token_str) {
239
+ Ok(header) => header,
240
+ Err(_) => {
241
+ debug!(target: "middleware::auth_jwt", "JWT decoding failed");
242
+ return Ok(Either::Right(
243
+ self.error_response
244
+ .to_http_response(req.accept().into())
245
+ .await,
246
+ ));
247
+ }
248
+ };
224
249
 
225
250
  let alg: JwtAlgorithm = header.alg.into();
226
251
 
@@ -256,12 +281,28 @@ impl MiddlewareLayer for AuthJwt {
256
281
  validation.leeway = leeway;
257
282
  }
258
283
 
284
+ if let Some(Some(auds)) = &self.audience_vec.get() {
285
+ validation.set_audience(auds);
286
+ validation.required_spec_claims.insert("aud".to_owned());
287
+ } else {
288
+ validation.validate_aud = false;
289
+ }
290
+
291
+ if let Some(Some(issuers)) = &self.issuer_vec.get() {
292
+ validation.set_issuer(issuers);
293
+ validation.required_spec_claims.insert("iss".to_owned());
294
+ }
295
+
296
+ if self.subjects.is_some() {
297
+ validation.required_spec_claims.insert("sub".to_owned());
298
+ }
299
+
259
300
  let token_data: Option<TokenData<Claims>> =
260
301
  keys.iter()
261
302
  .find_map(|key| match decode::<Claims>(token_str, key, &validation) {
262
303
  Ok(data) => Some(data),
263
304
  Err(e) => {
264
- error!("Token validation failed: {:?}", e);
305
+ debug!("Token validation failed: {:?}", e);
265
306
  None
266
307
  }
267
308
  });
@@ -278,30 +319,11 @@ impl MiddlewareLayer for AuthJwt {
278
319
 
279
320
  let claims = token_data.claims;
280
321
 
281
- if let Some(expected_audiences) = &self.audiences {
282
- if let Some(aud) = &claims.aud {
283
- let token_auds: HashSet<String> = match aud {
284
- Audience::Single(s) => [s.clone()].into_iter().collect(),
285
- Audience::Multiple(v) => v.iter().cloned().collect(),
286
- };
287
- if expected_audiences.is_disjoint(&token_auds) {
288
- debug!(
289
- "AUD check failed, token_auds: {:?}, expected_audiences: {:?}",
290
- token_auds, expected_audiences
291
- );
292
- return Ok(Either::Right(
293
- self.error_response
294
- .to_http_response(req.accept().into())
295
- .await,
296
- ));
297
- }
298
- }
299
- }
300
-
301
322
  if let Some(expected_subjects) = &self.subjects {
302
323
  if let Some(sub) = &claims.sub {
303
324
  if !expected_subjects.contains(sub) {
304
325
  debug!(
326
+ target: "middleware::auth_jwt",
305
327
  "SUB check failed, token_sub: {:?}, expected_subjects: {:?}",
306
328
  sub, expected_subjects
307
329
  );
@@ -314,23 +336,6 @@ impl MiddlewareLayer for AuthJwt {
314
336
  }
315
337
  }
316
338
 
317
- // Verify expected issuer.
318
- if let Some(expected_issuers) = &self.issuers {
319
- if let Some(iss) = &claims.iss {
320
- if !expected_issuers.contains(iss) {
321
- debug!(
322
- "ISS check failed, token_iss: {:?}, expected_issuers: {:?}",
323
- iss, expected_issuers
324
- );
325
- return Ok(Either::Right(
326
- self.error_response
327
- .to_http_response(req.accept().into())
328
- .await,
329
- ));
330
- }
331
- }
332
- }
333
-
334
339
  Ok(Either::Left(req))
335
340
  }
336
341
  }
@@ -8,8 +8,9 @@ use http::{HeaderName, HeaderValue};
8
8
  use magnus::error::Result;
9
9
  use serde::Deserialize;
10
10
  use std::{collections::HashMap, sync::OnceLock};
11
+ use tracing::debug;
11
12
 
12
- #[derive(Debug, Clone, Deserialize)]
13
+ #[derive(Debug, Deserialize)]
13
14
  pub struct CacheControl {
14
15
  #[serde(default)]
15
16
  pub max_age: Option<u64>,
@@ -87,6 +88,7 @@ impl MiddlewareLayer for CacheControl {
87
88
  // Set the Cache-Control header if we have directives
88
89
  if !directives.is_empty() {
89
90
  let cache_control_value = directives.join(", ");
91
+ debug!(target: "middleware::cache_control", "Built cache-control directive {}", cache_control_value);
90
92
  self.cache_control_str.set(cache_control_value).unwrap();
91
93
  }
92
94
 
@@ -97,20 +99,27 @@ impl MiddlewareLayer for CacheControl {
97
99
  // Skip for statuses where caching doesn't make sense
98
100
  let status = resp.status().as_u16();
99
101
  if matches!(status, 401 | 403 | 500..=599) {
102
+ debug!(target: "middleware::cache_control", "Skipping cache-control for status {}", status);
100
103
  return resp;
101
104
  }
102
105
 
103
106
  // Set the Cache-Control header if we have directives
104
107
  if let Some(cache_control_value) = self.cache_control_str.get() {
105
108
  if let Ok(value) = HeaderValue::from_str(cache_control_value) {
109
+ debug!(target: "middleware::cache_control", "Setting cache-control header to {}", cache_control_value);
106
110
  resp.headers_mut().insert("Cache-Control", value);
111
+ } else {
112
+ debug!(target: "middleware::cache_control", "Failed to parse cache-control value {}", cache_control_value);
107
113
  }
114
+ } else {
115
+ debug!(target: "middleware::cache_control", "No cache-control value provided");
108
116
  }
109
117
 
110
118
  // Set Expires header based on max-age if present
111
119
  if let Some(max_age) = self.max_age {
112
120
  // Set the Expires header based on max-age
113
121
  // Use a helper to format the HTTP date correctly
122
+ debug!(target: "middleware::cache_control", "Setting expires header to {}", max_age);
114
123
  let expires = chrono::Utc::now() + chrono::Duration::seconds(max_age as i64);
115
124
  let expires_str = expires.format("%a, %d %b %Y %H:%M:%S GMT").to_string();
116
125
  if let Ok(value) = HeaderValue::from_str(&expires_str) {
@@ -118,7 +127,6 @@ impl MiddlewareLayer for CacheControl {
118
127
  }
119
128
  }
120
129
 
121
- // Set Vary header
122
130
  if !self.vary.is_empty() {
123
131
  let vary_value = self.vary.join(", ");
124
132
  if let Ok(value) = HeaderValue::from_str(&vary_value) {
@@ -130,6 +138,7 @@ impl MiddlewareLayer for CacheControl {
130
138
  for (name, value) in &self.additional_headers {
131
139
  if let Ok(header_value) = HeaderValue::from_str(value) {
132
140
  if let Ok(header_name) = name.parse::<HeaderName>() {
141
+ debug!(target: "middleware::cache_control", "Setting custom header {} to {:?}", header_name, header_value);
133
142
  resp.headers_mut().insert(header_name, header_value);
134
143
  }
135
144
  }
@@ -25,6 +25,7 @@ use std::convert::Infallible;
25
25
  use tokio::io::{AsyncRead, AsyncReadExt, BufReader};
26
26
  use tokio_stream::StreamExt;
27
27
  use tokio_util::io::{ReaderStream, StreamReader};
28
+ use tracing::debug;
28
29
  #[derive(Debug, Clone, Serialize, Deserialize)]
29
30
  pub struct Compression {
30
31
  min_size: usize,
@@ -40,8 +41,8 @@ enum CompressionLevel {
40
41
  Fastest,
41
42
  #[serde(rename(deserialize = "best"))]
42
43
  Best,
43
- #[serde(rename(deserialize = "default"))]
44
- Default,
44
+ #[serde(rename(deserialize = "balanced"))]
45
+ Balanced,
45
46
  #[serde(rename(deserialize = "precise"))]
46
47
  Precise(i32),
47
48
  }
@@ -51,7 +52,7 @@ impl CompressionLevel {
51
52
  match self {
52
53
  CompressionLevel::Fastest => Level::Fastest,
53
54
  CompressionLevel::Best => Level::Best,
54
- CompressionLevel::Default => Level::Default,
55
+ CompressionLevel::Balanced => Level::Default,
55
56
  CompressionLevel::Precise(level) => Level::Precise(*level),
56
57
  }
57
58
  }
@@ -61,14 +62,14 @@ impl CompressionLevel {
61
62
  pub enum CompressionAlgorithm {
62
63
  #[serde(rename(deserialize = "gzip"))]
63
64
  Gzip,
64
- #[serde(rename(deserialize = "brotli"))]
65
+ #[serde(rename(deserialize = "br"))]
65
66
  Brotli,
66
67
  #[serde(rename(deserialize = "deflate"))]
67
68
  Deflate,
68
69
  #[serde(rename(deserialize = "zstd"))]
69
70
  Zstd,
70
- #[serde(rename(deserialize = "none"))]
71
- None,
71
+ #[serde(rename(deserialize = "identity"))]
72
+ Identity,
72
73
  }
73
74
 
74
75
  impl CompressionAlgorithm {
@@ -78,7 +79,7 @@ impl CompressionAlgorithm {
78
79
  CompressionAlgorithm::Brotli => "br",
79
80
  CompressionAlgorithm::Deflate => "deflate",
80
81
  CompressionAlgorithm::Zstd => "zstd",
81
- CompressionAlgorithm::None => "none",
82
+ CompressionAlgorithm::Identity => "identity",
82
83
  }
83
84
  }
84
85
 
@@ -99,6 +100,8 @@ enum MimeType {
99
100
  Audio,
100
101
  #[serde(rename(deserialize = "video"))]
101
102
  Video,
103
+ #[serde(rename(deserialize = "font"))]
104
+ Font,
102
105
  #[serde(rename(deserialize = "other"))]
103
106
  Other(String),
104
107
  #[serde(rename(deserialize = "all"))]
@@ -113,6 +116,7 @@ impl MimeType {
113
116
  MimeType::Application => header_contains(content_encodings, "application/*"),
114
117
  MimeType::Audio => header_contains(content_encodings, "audio/*"),
115
118
  MimeType::Video => header_contains(content_encodings, "video/*"),
119
+ MimeType::Font => header_contains(content_encodings, "font/*"),
116
120
  MimeType::Other(v) => header_contains(content_encodings, v),
117
121
  MimeType::All => header_contains(content_encodings, "*"),
118
122
  }
@@ -162,16 +166,28 @@ impl MiddlewareLayer for Compression {
162
166
  .iter()
163
167
  .any(|mt| mt.matches(&resp.headers().get_all(CONTENT_TYPE)))
164
168
  {
169
+ debug!(
170
+ target: "middleware::compress",
171
+ "Mime type not supported for compression"
172
+ );
165
173
  return resp;
166
174
  }
167
175
 
168
176
  // Don't compress streams unless compress streams is enabled.
169
177
  if body_size.is_none() && !self.compress_streams {
178
+ debug!(
179
+ target: "middleware::compress",
180
+ "Stream compression disabled"
181
+ );
170
182
  return resp;
171
183
  }
172
184
 
173
185
  // Don't compress too small bodies
174
186
  if body_size.is_some_and(|s| s < self.min_size as u64) {
187
+ debug!(
188
+ target: "middleware::compress",
189
+ "Body size too small for compression"
190
+ );
175
191
  return resp;
176
192
  }
177
193
 
@@ -181,6 +197,10 @@ impl MiddlewareLayer for Compression {
181
197
  for encoding in encodings.split(',').map(str::trim) {
182
198
  let encoding = encoding.split(';').next().unwrap_or(encoding).trim();
183
199
  if self.algorithms.iter().any(|algo| algo.as_str() == encoding) {
200
+ debug!(
201
+ target: "middleware::compress",
202
+ "Body already compressed with supported algorithm"
203
+ );
184
204
  return resp;
185
205
  }
186
206
  }
@@ -195,10 +215,14 @@ impl MiddlewareLayer for Compression {
195
215
  Some("br") => CompressionAlgorithm::Brotli,
196
216
  Some("deflate") => CompressionAlgorithm::Deflate,
197
217
  Some("zstd") => CompressionAlgorithm::Zstd,
198
- _ => CompressionAlgorithm::None,
218
+ _ => CompressionAlgorithm::Identity,
199
219
  };
200
220
 
201
- if matches!(compression_method, CompressionAlgorithm::None) {
221
+ debug!(
222
+ target: "middleware::compress",
223
+ "Selected compression method: {:?}", compression_method
224
+ );
225
+ if matches!(compression_method, CompressionAlgorithm::Identity) {
202
226
  return resp;
203
227
  }
204
228
 
@@ -250,7 +274,7 @@ impl MiddlewareLayer for Compression {
250
274
  encoder.read_to_end(&mut buf).await.unwrap();
251
275
  buf
252
276
  }
253
- CompressionAlgorithm::None => unreachable!(),
277
+ CompressionAlgorithm::Identity => unreachable!(),
254
278
  };
255
279
  BoxBody::new(Full::new(Bytes::from(compressed_bytes)))
256
280
  } else {
@@ -276,13 +300,16 @@ impl MiddlewareLayer for Compression {
276
300
  reader,
277
301
  self.level.to_async_compression_level(),
278
302
  )),
279
- CompressionAlgorithm::None => unreachable!(),
303
+ CompressionAlgorithm::Identity => unreachable!(),
280
304
  }
281
305
  };
282
306
 
283
307
  update_content_encoding(&mut parts, compression_method.header_value());
284
308
  parts.headers.remove(CONTENT_LENGTH);
285
-
309
+ debug!(
310
+ target: "middleware::compress",
311
+ "Response compressed"
312
+ );
286
313
  Response::from_parts(parts, new_body)
287
314
  }
288
315
  }
@@ -10,14 +10,15 @@ use http_body_util::{combinators::BoxBody, Empty};
10
10
  use itsi_error::ItsiError;
11
11
  use magnus::error::Result;
12
12
  use serde::Deserialize;
13
+ use tracing::debug;
13
14
 
14
15
  #[derive(Debug, Clone, Deserialize)]
15
16
  pub struct Cors {
16
- pub allowed_origins: Vec<String>,
17
- pub allowed_methods: Vec<HttpMethod>,
18
- pub allowed_headers: Vec<String>,
19
- pub exposed_headers: Vec<String>,
17
+ pub allow_origins: Vec<String>,
18
+ pub allow_methods: Vec<HttpMethod>,
19
+ pub allow_headers: Vec<String>,
20
20
  pub allow_credentials: bool,
21
+ pub expose_headers: Vec<String>,
21
22
  pub max_age: Option<u64>,
22
23
  }
23
24
 
@@ -74,6 +75,7 @@ impl Cors {
74
75
 
75
76
  if origin.is_empty() {
76
77
  // When credentials are allowed, you cannot return "*".
78
+ debug!(target: "middleware::cors", "Origin empty {}", origin);
77
79
  if !self.allow_credentials {
78
80
  headers.insert(
79
81
  "Access-Control-Allow-Origin",
@@ -84,13 +86,13 @@ impl Cors {
84
86
  }
85
87
 
86
88
  // Only return a header if the origin is allowed.
87
- if self.allowed_origins.iter().any(|o| o == origin || o == "*") {
89
+ if self.allow_origins.iter().any(|o| o == origin || o == "*") {
88
90
  // If credentials are allowed, we must echo back the exact origin.
89
91
  let value = if self.allow_credentials {
90
92
  origin
91
93
  } else {
92
94
  // If not, and if "*" is allowed, you can still use "*".
93
- if self.allowed_origins.iter().any(|o| o == "*") {
95
+ if self.allow_origins.iter().any(|o| o == "*") {
94
96
  "*"
95
97
  } else {
96
98
  origin
@@ -102,10 +104,10 @@ impl Cors {
102
104
  );
103
105
  }
104
106
 
105
- if !self.allowed_methods.is_empty() {
107
+ if !self.allow_methods.is_empty() {
106
108
  headers.insert(
107
109
  "Access-Control-Allow-Methods",
108
- self.allowed_methods
110
+ self.allow_methods
109
111
  .iter()
110
112
  .map(HttpMethod::to_str)
111
113
  .collect::<Vec<&str>>()
@@ -114,10 +116,10 @@ impl Cors {
114
116
  .map_err(ItsiError::new)?,
115
117
  );
116
118
  }
117
- if !self.allowed_headers.is_empty() {
119
+ if !self.allow_headers.is_empty() {
118
120
  headers.insert(
119
121
  "Access-Control-Allow-Headers",
120
- self.allowed_headers
122
+ self.allow_headers
121
123
  .join(", ")
122
124
  .parse()
123
125
  .map_err(ItsiError::new)?,
@@ -135,10 +137,10 @@ impl Cors {
135
137
  max_age.to_string().parse().map_err(ItsiError::new)?,
136
138
  );
137
139
  }
138
- if !self.exposed_headers.is_empty() {
140
+ if !self.expose_headers.is_empty() {
139
141
  headers.insert(
140
142
  "Access-Control-Expose-Headers",
141
- self.exposed_headers
143
+ self.expose_headers
142
144
  .join(", ")
143
145
  .parse()
144
146
  .map_err(ItsiError::new)?,
@@ -159,27 +161,31 @@ impl Cors {
159
161
 
160
162
  let origin = match origin {
161
163
  Some(o) if !o.is_empty() => o,
162
- _ => return Ok(headers), // Missing Origin – preflight fails
164
+ _ => {
165
+ debug!(target: "middleware::cors", "Missing Origin – preflight fails");
166
+ return Ok(headers);
167
+ }
163
168
  };
164
169
 
165
170
  if !self
166
- .allowed_origins
171
+ .allow_origins
167
172
  .iter()
168
173
  .any(|allowed| allowed == "*" || allowed == origin)
169
174
  {
175
+ debug!(target: "middleware::cors", "Origin not allowed");
170
176
  return Ok(headers);
171
177
  }
172
178
 
173
179
  let request_method = match req_method {
174
180
  Some(m) if !m.is_empty() => m,
175
- _ => return Ok(headers), // Missing request method – preflight fails
181
+ _ => {
182
+ debug!(target: "middleware::cors", "Missing request method – preflight fails");
183
+ return Ok(headers);
184
+ }
176
185
  };
177
186
 
178
- if !self
179
- .allowed_methods
180
- .iter()
181
- .any(|m| m.matches(request_method))
182
- {
187
+ if !self.allow_methods.iter().any(|m| m.matches(request_method)) {
188
+ debug!(target: "middleware::cors", "Method not allowed");
183
189
  return Ok(headers);
184
190
  }
185
191
 
@@ -191,10 +197,11 @@ impl Cors {
191
197
  .collect();
192
198
  for header in req_headers_list {
193
199
  if !self
194
- .allowed_headers
200
+ .allow_headers
195
201
  .iter()
196
202
  .any(|allowed| allowed.eq_ignore_ascii_case(header))
197
203
  {
204
+ debug!(target: "middleware::cors", "Header not allowed {}", header);
198
205
  return Ok(headers);
199
206
  }
200
207
  }
@@ -203,7 +210,7 @@ impl Cors {
203
210
  headers.insert("Access-Control-Allow-Origin", origin.parse().unwrap());
204
211
  headers.insert(
205
212
  "Access-Control-Allow-Methods",
206
- self.allowed_methods
213
+ self.allow_methods
207
214
  .iter()
208
215
  .map(HttpMethod::to_str)
209
216
  .collect::<Vec<&str>>()
@@ -213,7 +220,7 @@ impl Cors {
213
220
  );
214
221
  headers.insert(
215
222
  "Access-Control-Allow-Headers",
216
- self.allowed_headers
223
+ self.allow_headers
217
224
  .join(", ")
218
225
  .parse()
219
226
  .map_err(ItsiError::new)?,
@@ -230,10 +237,10 @@ impl Cors {
230
237
  max_age.to_string().parse().map_err(ItsiError::new)?,
231
238
  );
232
239
  }
233
- if !self.exposed_headers.is_empty() {
240
+ if !self.expose_headers.is_empty() {
234
241
  headers.insert(
235
242
  "Access-Control-Expose-Headers",
236
- self.exposed_headers
243
+ self.expose_headers
237
244
  .join(", ")
238
245
  .parse()
239
246
  .map_err(ItsiError::new)?,
@@ -257,11 +264,12 @@ impl MiddlewareLayer for Cors {
257
264
  context: &mut HttpRequestContext,
258
265
  ) -> Result<either::Either<HttpRequest, HttpResponse>> {
259
266
  let origin = req.header("Origin");
267
+ debug!(target: "middleware::cors", "Origin: {:?}", origin);
260
268
  if req.method() == Method::OPTIONS {
261
269
  let ac_request_method = req.header("Access-Control-Request-Method");
262
270
  let ac_request_headers = req.header("Access-Control-Request-Headers");
263
271
  let headers = self.preflight_headers(origin, ac_request_method, ac_request_headers)?;
264
-
272
+ debug!(target: "middleware::cors", "Preflight Headers: {:?}", headers);
265
273
  let mut response_builder = Response::builder().status(204);
266
274
  *response_builder.headers_mut().unwrap() = headers;
267
275
  let response = response_builder
@@ -273,14 +281,15 @@ impl MiddlewareLayer for Cors {
273
281
  Ok(either::Either::Left(req))
274
282
  }
275
283
 
276
- // The after hook can be used to inject CORS headers into non-preflight responses.
277
284
  async fn after(
278
285
  &self,
279
286
  mut resp: HttpResponse,
280
287
  context: &mut HttpRequestContext,
281
288
  ) -> HttpResponse {
282
289
  if let Some(Some(origin)) = context.origin.get() {
290
+ debug!(target: "middleware::cors", "fetching cors headers for origin {}", origin);
283
291
  if let Ok(cors_headers) = self.cors_headers(origin) {
292
+ debug!(target: "middleware::cors", "Cors Headers: {:?}", cors_headers);
284
293
  for (key, value) in cors_headers.iter() {
285
294
  resp.headers_mut().insert(key.clone(), value.clone());
286
295
  }