itsi 0.1.20 → 0.2.3

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 (323) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -8
  3. data/Cargo.lock +29 -30
  4. data/LICENSE.txt +698 -0
  5. data/README.md +16 -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 +148 -66
  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 +2 -3
  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/directory_listing.jpg +0 -0
  87. data/docs/content/error_page.jpg +0 -0
  88. data/docs/content/faqs/_index.md +5 -3
  89. data/docs/content/features/_index.md +56 -50
  90. data/docs/content/getting_started/_index.md +8 -5
  91. data/docs/content/getting_started/local_development.md +76 -9
  92. data/docs/content/getting_started/logging.md +15 -9
  93. data/docs/content/getting_started/running_itsi_in_production.md +5 -3
  94. data/docs/content/getting_started/signals.md +37 -0
  95. data/docs/content/itsi_scheduler/_index.md +8 -7
  96. data/docs/content/utilities/_index.md +13 -0
  97. data/docs/content/utilities/config_file_testing.md +17 -0
  98. data/docs/content/utilities/passfile_generator.md +41 -0
  99. data/docs/content/utilities/route_testing.md +27 -0
  100. data/docs/content/utilities/secrets_management.md +30 -0
  101. data/docs/hugo.yaml +4 -1
  102. data/fairytale.txt +3 -4
  103. data/gems/scheduler/Cargo.lock +74 -17
  104. data/gems/scheduler/README.md +4 -5
  105. data/gems/scheduler/Rakefile +0 -4
  106. data/gems/scheduler/itsi-scheduler.gemspec +2 -2
  107. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  108. data/gems/scheduler/lib/itsi/scheduler.rb +9 -4
  109. data/gems/scheduler/test/test_active_record.rb +12 -7
  110. data/gems/server/Cargo.lock +28 -29
  111. data/gems/server/Rakefile +0 -4
  112. data/gems/server/exe/itsi +13 -2
  113. data/gems/server/itsi-server.gemspec +2 -2
  114. data/gems/server/lib/itsi/http_request/response_status_shortcodes.rb +2 -0
  115. data/gems/server/lib/itsi/http_request.rb +58 -30
  116. data/gems/server/lib/itsi/http_response.rb +10 -7
  117. data/gems/server/lib/itsi/passfile.rb +6 -7
  118. data/gems/server/lib/itsi/server/config/config_helpers.rb +41 -29
  119. data/gems/server/lib/itsi/server/config/dsl.rb +22 -442
  120. data/gems/server/lib/itsi/server/config/known_paths.rb +14 -7
  121. data/gems/server/lib/itsi/server/config/middleware/_index.md +6 -4
  122. data/gems/server/lib/itsi/server/config/middleware/allow_list.md +46 -0
  123. data/gems/server/lib/itsi/server/config/middleware/allow_list.rb +42 -0
  124. data/gems/server/lib/itsi/server/config/middleware/auth_api_key.md +90 -0
  125. data/gems/server/lib/itsi/server/config/middleware/auth_api_key.rb +51 -0
  126. data/gems/server/lib/itsi/server/config/middleware/auth_basic.md +45 -0
  127. data/gems/server/lib/itsi/server/config/middleware/auth_basic.rb +44 -0
  128. data/gems/server/lib/itsi/server/config/middleware/auth_jwt.md +82 -0
  129. data/gems/server/lib/itsi/server/config/middleware/auth_jwt.rb +38 -0
  130. data/gems/server/lib/itsi/server/config/middleware/cache_control.md +78 -0
  131. data/gems/server/lib/itsi/server/config/middleware/cache_control.rb +45 -0
  132. data/gems/server/lib/itsi/server/config/middleware/cidr_to_regex.rb +50 -0
  133. data/gems/server/lib/itsi/server/config/middleware/compression.md +50 -0
  134. data/gems/server/lib/itsi/server/config/middleware/compression.rb +37 -0
  135. data/gems/server/lib/itsi/server/config/middleware/cors.md +93 -0
  136. data/gems/server/lib/itsi/server/config/middleware/cors.rb +32 -0
  137. data/gems/server/lib/itsi/server/config/middleware/csp.md +37 -0
  138. data/gems/server/lib/itsi/server/config/middleware/csp.rb +44 -0
  139. data/gems/server/lib/itsi/server/config/middleware/deny_list.md +45 -0
  140. data/gems/server/lib/itsi/server/config/middleware/deny_list.rb +42 -0
  141. data/gems/server/lib/itsi/server/config/middleware/endpoint/_index.md +159 -0
  142. data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.md +186 -0
  143. data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.rb +33 -0
  144. data/gems/server/lib/itsi/server/config/middleware/endpoint/delete.md +12 -0
  145. data/gems/server/lib/itsi/server/config/middleware/endpoint/delete.rb +42 -0
  146. data/gems/server/lib/itsi/server/config/middleware/endpoint/endpoint.rb +99 -0
  147. data/gems/server/lib/itsi/server/config/middleware/endpoint/get.md +12 -0
  148. data/gems/server/lib/itsi/server/config/middleware/endpoint/get.rb +42 -0
  149. data/gems/server/lib/itsi/server/config/middleware/endpoint/http_request.md +44 -0
  150. data/gems/server/lib/itsi/server/config/middleware/endpoint/http_response.md +39 -0
  151. data/gems/server/lib/itsi/server/config/middleware/endpoint/patch.md +12 -0
  152. data/gems/server/lib/itsi/server/config/middleware/endpoint/patch.rb +42 -0
  153. data/gems/server/lib/itsi/server/config/middleware/endpoint/post.md +12 -0
  154. data/gems/server/lib/itsi/server/config/middleware/endpoint/post.rb +42 -0
  155. data/gems/server/lib/itsi/server/config/middleware/endpoint/put.md +12 -0
  156. data/gems/server/lib/itsi/server/config/middleware/endpoint/put.rb +42 -0
  157. data/gems/server/lib/itsi/server/config/middleware/endpoint/schemas.md +122 -0
  158. data/gems/server/lib/itsi/server/config/middleware/error_response.md +74 -0
  159. data/gems/server/lib/itsi/server/config/middleware/error_response.rb +36 -0
  160. data/gems/server/lib/itsi/server/config/middleware/etag.md +59 -0
  161. data/gems/server/lib/itsi/server/config/middleware/etag.rb +27 -0
  162. data/gems/server/lib/itsi/server/config/middleware/grpc.md +172 -0
  163. data/gems/server/lib/itsi/server/config/middleware/grpc.rb +54 -0
  164. data/gems/server/lib/itsi/server/config/middleware/intrusion_protection.md +124 -0
  165. data/gems/server/lib/itsi/server/config/middleware/intrusion_protection.rb +61 -0
  166. data/gems/server/lib/itsi/server/config/middleware/location.md +107 -0
  167. data/gems/server/lib/itsi/server/config/middleware/location.rb +99 -0
  168. data/gems/server/lib/itsi/server/config/middleware/log_requests.md +13 -11
  169. data/gems/server/lib/itsi/server/config/middleware/log_requests.rb +1 -3
  170. data/gems/server/lib/itsi/server/config/middleware/max_body.md +18 -0
  171. data/gems/server/lib/itsi/server/config/middleware/max_body.rb +21 -0
  172. data/gems/server/lib/itsi/server/config/middleware/proxy.md +62 -0
  173. data/gems/server/lib/itsi/server/config/middleware/proxy.rb +41 -0
  174. data/gems/server/lib/itsi/server/config/middleware/rackup_file.md +54 -0
  175. data/gems/server/lib/itsi/server/config/middleware/rackup_file.rb +44 -0
  176. data/gems/server/lib/itsi/server/config/middleware/rate_limit.md +126 -0
  177. data/gems/server/lib/itsi/server/config/middleware/rate_limit.rb +34 -0
  178. data/gems/server/lib/itsi/server/config/middleware/rate_limit_store.rb +25 -0
  179. data/gems/server/lib/itsi/server/config/middleware/redirect.md +55 -0
  180. data/gems/server/lib/itsi/server/config/middleware/redirect.rb +25 -0
  181. data/gems/server/lib/itsi/server/config/middleware/request_headers.md +34 -0
  182. data/gems/server/lib/itsi/server/config/middleware/request_headers.rb +24 -0
  183. data/gems/server/lib/itsi/server/config/middleware/response_headers.md +33 -0
  184. data/gems/server/lib/itsi/server/config/middleware/response_headers.rb +25 -0
  185. data/gems/server/lib/itsi/server/config/middleware/run.md +60 -0
  186. data/gems/server/lib/itsi/server/config/middleware/run.rb +43 -0
  187. data/gems/server/lib/itsi/server/config/middleware/static_assets.md +113 -0
  188. data/gems/server/lib/itsi/server/config/middleware/static_assets.rb +87 -0
  189. data/gems/server/lib/itsi/server/config/middleware/static_response.md +44 -0
  190. data/gems/server/lib/itsi/server/config/middleware/static_response.rb +29 -0
  191. data/gems/server/lib/itsi/server/config/middleware/string_rewrite.md +67 -0
  192. data/gems/server/lib/itsi/server/config/middleware/token_source.rb +32 -0
  193. data/gems/server/lib/itsi/server/config/middleware.rb +4 -0
  194. data/gems/server/lib/itsi/server/config/option.rb +4 -0
  195. data/gems/server/lib/itsi/server/config/options/_index.md +3 -2
  196. data/gems/server/lib/itsi/server/config/options/auto_reload_config.md +13 -0
  197. data/gems/server/lib/itsi/server/config/options/auto_reload_config.rb +41 -0
  198. data/gems/server/lib/itsi/server/config/options/bind.md +71 -0
  199. data/gems/server/lib/itsi/server/config/options/bind.rb +26 -0
  200. data/gems/server/lib/itsi/server/config/options/certificates.md +65 -0
  201. data/gems/server/lib/itsi/server/config/options/daemonize.md +14 -0
  202. data/gems/server/lib/itsi/server/config/options/daemonize.rb +19 -0
  203. data/gems/server/lib/itsi/server/config/options/fiber_scheduler.md +1 -2
  204. data/gems/server/lib/itsi/server/config/options/fiber_scheduler.rb +6 -3
  205. data/gems/server/lib/itsi/server/config/options/header_read_timeout.md +17 -0
  206. data/gems/server/lib/itsi/server/config/options/header_read_timeout.rb +19 -0
  207. data/gems/server/lib/itsi/server/config/options/hooks/_index.md +11 -0
  208. data/gems/server/lib/itsi/server/config/options/hooks/after_fork.md +13 -0
  209. data/gems/server/lib/itsi/server/config/options/hooks/after_fork.rb +28 -0
  210. data/gems/server/lib/itsi/server/config/options/hooks/after_memory_limit_reached.md +14 -0
  211. data/gems/server/lib/itsi/server/config/options/hooks/after_memory_limit_reached.rb +28 -0
  212. data/gems/server/lib/itsi/server/config/options/hooks/after_start.md +12 -0
  213. data/gems/server/lib/itsi/server/config/options/hooks/after_start.rb +28 -0
  214. data/gems/server/lib/itsi/server/config/options/hooks/before_fork.md +13 -0
  215. data/gems/server/lib/itsi/server/config/options/hooks/before_fork.rb +28 -0
  216. data/gems/server/lib/itsi/server/config/options/hooks/before_restart.md +12 -0
  217. data/gems/server/lib/itsi/server/config/options/hooks/before_restart.rb +28 -0
  218. data/gems/server/lib/itsi/server/config/options/hooks/before_shutdown.md +12 -0
  219. data/gems/server/lib/itsi/server/config/options/hooks/before_shutdown.rb +28 -0
  220. data/gems/server/lib/itsi/server/config/options/include.md +20 -0
  221. data/gems/server/lib/itsi/server/config/options/include.rb +36 -0
  222. data/gems/server/lib/itsi/server/config/options/listen_backlog.md +11 -0
  223. data/gems/server/lib/itsi/server/config/options/listen_backlog.rb +19 -0
  224. data/gems/server/lib/itsi/server/config/options/log_format.md +18 -0
  225. data/gems/server/lib/itsi/server/config/options/log_format.rb +19 -0
  226. data/gems/server/lib/itsi/server/config/options/log_level.md +34 -0
  227. data/gems/server/lib/itsi/server/config/options/log_level.rb +20 -0
  228. data/gems/server/lib/itsi/server/config/options/log_target.md +38 -0
  229. data/gems/server/lib/itsi/server/config/options/log_target.rb +19 -0
  230. data/gems/server/lib/itsi/server/config/options/log_target_filters.md +17 -0
  231. data/gems/server/lib/itsi/server/config/options/log_target_filters.rb +19 -0
  232. data/gems/server/lib/itsi/server/config/options/multithreaded_reactor.md +27 -0
  233. data/gems/server/lib/itsi/server/config/options/multithreaded_reactor.rb +24 -0
  234. data/gems/server/lib/itsi/server/config/options/nodelay.md +16 -0
  235. data/gems/server/lib/itsi/server/config/options/nodelay.rb +19 -0
  236. data/gems/server/lib/itsi/server/config/options/oob_gc_responses_threshold.md +19 -0
  237. data/gems/server/lib/itsi/server/config/options/oob_gc_responses_threshold.rb +18 -0
  238. data/gems/server/lib/itsi/server/config/options/pin_worker_cores.md +17 -0
  239. data/gems/server/lib/itsi/server/config/options/pin_worker_cores.rb +19 -0
  240. data/gems/server/lib/itsi/server/config/options/preload.md +21 -0
  241. data/gems/server/lib/itsi/server/config/options/preload.rb +18 -0
  242. data/gems/server/lib/itsi/server/config/options/recv_buffer_size.md +15 -0
  243. data/gems/server/lib/itsi/server/config/options/recv_buffer_size.rb +19 -0
  244. data/gems/server/lib/itsi/server/config/options/redirect_http_to_https.md +21 -0
  245. data/gems/server/lib/itsi/server/config/options/redirect_http_to_https.rb +30 -0
  246. data/gems/server/lib/itsi/server/config/options/request_timeout.md +23 -0
  247. data/gems/server/lib/itsi/server/config/options/request_timeout.rb +19 -0
  248. data/gems/server/lib/itsi/server/config/options/reuse_address.md +16 -0
  249. data/gems/server/lib/itsi/server/config/options/reuse_address.rb +19 -0
  250. data/gems/server/lib/itsi/server/config/options/reuse_port.md +16 -0
  251. data/gems/server/lib/itsi/server/config/options/reuse_port.rb +19 -0
  252. data/gems/server/lib/itsi/server/config/options/scheduler_threads.md +34 -0
  253. data/gems/server/lib/itsi/server/config/options/scheduler_threads.rb +17 -0
  254. data/gems/server/lib/itsi/server/config/options/shutdown_timeout.md +17 -0
  255. data/gems/server/lib/itsi/server/config/options/shutdown_timeout.rb +19 -0
  256. data/gems/server/lib/itsi/server/config/options/stream_body.md +32 -0
  257. data/gems/server/lib/itsi/server/config/options/stream_body.rb +18 -0
  258. data/gems/server/lib/itsi/server/config/options/threads.md +7 -2
  259. data/gems/server/lib/itsi/server/config/options/threads.rb +1 -1
  260. data/gems/server/lib/itsi/server/config/options/watch.md +16 -0
  261. data/gems/server/lib/itsi/server/config/options/watch.rb +28 -0
  262. data/gems/server/lib/itsi/server/config/options/worker_memory_limit.md +22 -0
  263. data/gems/server/lib/itsi/server/config/options/worker_memory_limit.rb +18 -0
  264. data/gems/server/lib/itsi/server/config/options/workers.md +1 -2
  265. data/gems/server/lib/itsi/server/config/options/workers.rb +1 -1
  266. data/gems/server/lib/itsi/server/config/typed_struct.rb +68 -32
  267. data/gems/server/lib/itsi/server/config.rb +163 -119
  268. data/gems/server/lib/itsi/server/default_app/default_app.rb +1 -1
  269. data/gems/server/lib/itsi/server/default_config/Itsi.rb +3 -3
  270. data/gems/server/lib/itsi/server/grpc/grpc_call.rb +4 -5
  271. data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +10 -4
  272. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +2 -3
  273. data/gems/server/lib/itsi/server/rack_interface.rb +0 -1
  274. data/gems/server/lib/itsi/server/route_tester.rb +61 -9
  275. data/gems/server/lib/itsi/server/signal_trap.rb +1 -1
  276. data/gems/server/lib/itsi/server/typed_handlers/param_parser.rb +14 -18
  277. data/gems/server/lib/itsi/server/typed_handlers/source_parser.rb +13 -10
  278. data/gems/server/lib/itsi/server/typed_handlers.rb +12 -4
  279. data/gems/server/lib/itsi/server/version.rb +1 -1
  280. data/gems/server/lib/itsi/server.rb +111 -27
  281. data/gems/server/lib/itsi/standard_headers.rb +80 -80
  282. data/gems/server/lib/ruby_lsp/itsi/addon.rb +20 -18
  283. data/gems/server/test/helpers/test_helper.rb +90 -29
  284. data/gems/server/test/middleware/allow_list.rb +128 -0
  285. data/gems/server/test/middleware/auth_api_key.rb +141 -0
  286. data/gems/server/test/middleware/auth_basic.rb +91 -0
  287. data/gems/server/test/middleware/auth_jwt.rb +214 -0
  288. data/gems/server/test/middleware/cache_control.rb +82 -0
  289. data/gems/server/test/middleware/cidr_to_regex.rb +46 -0
  290. data/gems/server/test/middleware/compression.rb +89 -0
  291. data/gems/server/test/middleware/cors.rb +113 -0
  292. data/gems/server/test/middleware/csp.rb +62 -0
  293. data/gems/server/test/middleware/deny_list.rb +131 -0
  294. data/gems/server/test/middleware/endpoint.rb +300 -0
  295. data/gems/server/test/middleware/etag.rb +75 -0
  296. data/gems/server/test/middleware/grpc/grpc.rb +158 -0
  297. data/gems/server/test/middleware/grpc/test_service.proto +32 -0
  298. data/gems/server/test/middleware/grpc/test_service_impl.rb +28 -0
  299. data/gems/server/test/middleware/grpc/test_service_pb.rb +18 -0
  300. data/gems/server/test/middleware/grpc/test_service_services_pb.rb +30 -0
  301. data/gems/server/test/middleware/header_interpolation.rb +35 -0
  302. data/gems/server/test/middleware/intrusion_protection.rb +259 -0
  303. data/gems/server/test/middleware/location.rb +220 -0
  304. data/gems/server/test/middleware/max_body.rb +20 -0
  305. data/gems/server/test/middleware/proxy.rb +415 -0
  306. data/gems/server/test/middleware/rate_limit.rb +211 -0
  307. data/gems/server/test/middleware/redirect.rb +85 -0
  308. data/gems/server/test/middleware/request_headers.rb +50 -0
  309. data/gems/server/test/middleware/response_headers.rb +50 -0
  310. data/gems/server/test/middleware/static_assets.rb +374 -0
  311. data/gems/server/test/middleware/static_response.rb +56 -0
  312. data/gems/server/test/middleware/string_rewrite.rb +112 -0
  313. data/gems/server/test/middleware/test_log_requests.rb +54 -2
  314. data/gems/server/test/options/bind.rb +47 -0
  315. data/gems/server/test/options/header_read_timeout.rb +23 -0
  316. data/gems/server/test/options/test_request_timeout.rb +16 -0
  317. data/gems/server/test/options/test_workers.rb +11 -6
  318. data/gems/server/test/{test_itsi_server.rb → rack/test_rack_server.rb} +2 -2
  319. data/lib/itsi/version.rb +1 -1
  320. data/tasks.txt +15 -72
  321. metadata +209 -10
  322. data/examples/static_assets_example.rb +0 -83
  323. data/gems/server/lib/itsi/server/default_config/Itsi-rackup.rb +0 -119
@@ -7,14 +7,14 @@ use crate::{
7
7
  },
8
8
  };
9
9
  use derive_more::Debug;
10
- use futures::executor::block_on;
10
+ use itsi_error::ItsiError;
11
11
  use itsi_rb_helpers::{call_with_gvl, print_rb_backtrace, HeapValue};
12
12
  use itsi_tracing::{set_format, set_level, set_target, set_target_filters};
13
13
  use magnus::{
14
14
  block::Proc,
15
15
  error::Result,
16
16
  value::{LazyId, ReprValue},
17
- RArray, RHash, Ruby, Symbol, Value,
17
+ RArray, RHash, Ruby, Symbol, TryConvert, Value,
18
18
  };
19
19
  use nix::{
20
20
  fcntl::{fcntl, FcntlArg, FdFlag},
@@ -25,7 +25,11 @@ use std::{
25
25
  collections::HashMap,
26
26
  os::fd::{AsRawFd, OwnedFd, RawFd},
27
27
  path::PathBuf,
28
- sync::{Arc, OnceLock},
28
+ str::FromStr,
29
+ sync::{
30
+ atomic::{AtomicBool, Ordering::Relaxed},
31
+ Arc, OnceLock,
32
+ },
29
33
  time::Duration,
30
34
  };
31
35
  use tracing::{debug, error};
@@ -71,8 +75,33 @@ pub struct ServerParams {
71
75
  #[debug(skip)]
72
76
  pub(crate) listeners: Mutex<Vec<Listener>>,
73
77
  listener_info: Mutex<HashMap<String, i32>>,
78
+ pub itsi_server_token_preference: ItsiServerTokenPreference,
79
+ pub preloaded: AtomicBool,
80
+ socket_opts: SocketOpts,
81
+ preexisting_listeners: Option<String>,
82
+ }
83
+
84
+ #[derive(Debug, Clone)]
85
+ pub enum ItsiServerTokenPreference {
86
+ Version,
87
+ Name,
88
+ None,
89
+ }
90
+
91
+ impl FromStr for ItsiServerTokenPreference {
92
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
93
+ Ok(match s {
94
+ "version" => ItsiServerTokenPreference::Version,
95
+ "name" => ItsiServerTokenPreference::Name,
96
+ "none" => ItsiServerTokenPreference::None,
97
+ _ => ItsiServerTokenPreference::Version,
98
+ })
99
+ }
100
+
101
+ type Err = ItsiError;
74
102
  }
75
103
 
104
+ #[derive(Debug, Clone)]
76
105
  pub struct SocketOpts {
77
106
  pub reuse_address: bool,
78
107
  pub reuse_port: bool,
@@ -83,8 +112,10 @@ pub struct SocketOpts {
83
112
 
84
113
  impl ServerParams {
85
114
  pub fn preload_ruby(self: &Arc<Self>) -> Result<()> {
115
+ if self.preloaded.load(Relaxed) {
116
+ return Ok(());
117
+ }
86
118
  call_with_gvl(|ruby| -> Result<()> {
87
- debug!("Preloading Ruby");
88
119
  if self
89
120
  .scheduler_class
90
121
  .as_ref()
@@ -93,9 +124,17 @@ impl ServerParams {
93
124
  debug!("Loading Itsi Scheduler");
94
125
  ruby.require("itsi/scheduler")?;
95
126
  }
96
- let routes_raw = self
127
+ let result_pair = self
97
128
  .middleware_loader
98
- .call::<_, Option<Value>>(())
129
+ .call::<(), RArray>(())
130
+ .inspect_err(|e| {
131
+ eprintln!("Error loading middleware: {:?}", e);
132
+ if let Some(err_value) = e.value() {
133
+ print_rb_backtrace(err_value);
134
+ }
135
+ })?;
136
+ let routes_raw = result_pair
137
+ .entry::<Option<Value>>(0)
99
138
  .inspect_err(|e| {
100
139
  eprintln!("Error loading middleware: {:?}", e);
101
140
  if let Some(err_value) = e.value() {
@@ -103,9 +142,22 @@ impl ServerParams {
103
142
  }
104
143
  })?
105
144
  .map(|mw| mw.into());
106
- debug!("Middleware routes returned");
145
+ let error_lines = result_pair.entry::<Option<RArray>>(1).inspect_err(|e| {
146
+ eprintln!("Error loading middleware: {:?}", e);
147
+ if let Some(err_value) = e.value() {
148
+ print_rb_backtrace(err_value);
149
+ }
150
+ })?;
151
+ if error_lines.is_some_and(|r| !r.is_empty()) {
152
+ let errors: Vec<String> =
153
+ Vec::<String>::try_convert(error_lines.unwrap().as_value())?;
154
+ ItsiServerConfig::print_config_errors(errors);
155
+ return Err(magnus::Error::new(
156
+ magnus::exception::runtime_error(),
157
+ "Failed to set middleware",
158
+ ));
159
+ }
107
160
  let middleware = MiddlewareSet::new(routes_raw)?;
108
- debug!("Middleware loaded");
109
161
  self.middleware.set(middleware).map_err(|_| {
110
162
  magnus::Error::new(
111
163
  magnus::exception::runtime_error(),
@@ -114,6 +166,7 @@ impl ServerParams {
114
166
  })?;
115
167
  Ok(())
116
168
  })?;
169
+ self.preloaded.store(true, Relaxed);
117
170
  Ok(())
118
171
  }
119
172
 
@@ -154,11 +207,11 @@ impl ServerParams {
154
207
  .transpose()?
155
208
  .unwrap_or_default();
156
209
  let preload: bool = rb_param_hash.fetch("preload")?;
157
- let request_timeout: Option<u64> = rb_param_hash.fetch("request_timeout")?;
158
- let request_timeout = request_timeout.map(Duration::from_secs);
210
+ let request_timeout: Option<f64> = rb_param_hash.fetch("request_timeout")?;
211
+ let request_timeout = request_timeout.map(Duration::from_secs_f64);
159
212
  let header_read_timeout: Duration = rb_param_hash
160
- .fetch::<_, Option<u64>>("header_read_timeout")?
161
- .map(Duration::from_secs)
213
+ .fetch::<_, Option<f64>>("header_read_timeout")?
214
+ .map(Duration::from_secs_f64)
162
215
  .unwrap_or(Duration::from_secs(1));
163
216
 
164
217
  let notify_watchers: Option<Vec<(String, Vec<Vec<String>>)>> =
@@ -226,6 +279,12 @@ impl ServerParams {
226
279
  .map(|s| s.parse())
227
280
  .collect::<itsi_error::Result<Vec<Bind>>>()?;
228
281
 
282
+ let itsi_server_token_preference: String = rb_param_hash
283
+ .fetch("itsi_server_token_preference")
284
+ .unwrap_or_default();
285
+ let itsi_server_token_preference: ItsiServerTokenPreference =
286
+ itsi_server_token_preference.parse()?;
287
+
229
288
  let socket_opts = SocketOpts {
230
289
  reuse_address,
231
290
  reuse_port,
@@ -233,10 +292,42 @@ impl ServerParams {
233
292
  nodelay,
234
293
  recv_buffer_size,
235
294
  };
236
- let listeners = if let Some(preexisting_listeners) =
237
- rb_param_hash.delete::<_, Option<String>>("listeners")?
238
- {
239
- let bind_to_fd_map: HashMap<String, i32> = serde_json::from_str(&preexisting_listeners)
295
+ let preexisting_listeners = rb_param_hash.delete::<_, Option<String>>("listeners")?;
296
+
297
+ let params = ServerParams {
298
+ workers,
299
+ worker_memory_limit,
300
+ silence,
301
+ multithreaded_reactor,
302
+ pin_worker_cores,
303
+ shutdown_timeout,
304
+ hooks,
305
+ preload,
306
+ request_timeout,
307
+ header_read_timeout,
308
+ notify_watchers,
309
+ threads,
310
+ scheduler_threads,
311
+ streamable_body,
312
+ scheduler_class,
313
+ oob_gc_responses_threshold,
314
+ binds,
315
+ itsi_server_token_preference,
316
+ socket_opts,
317
+ preexisting_listeners,
318
+ listener_info: Mutex::new(HashMap::new()),
319
+ listeners: Mutex::new(Vec::new()),
320
+ middleware_loader: middleware_loader.into(),
321
+ middleware: OnceLock::new(),
322
+ preloaded: AtomicBool::new(false),
323
+ };
324
+
325
+ Ok(params)
326
+ }
327
+
328
+ pub fn setup_listeners(&self) -> Result<()> {
329
+ let listeners = if let Some(preexisting_listeners) = self.preexisting_listeners.as_ref() {
330
+ let bind_to_fd_map: HashMap<String, i32> = serde_json::from_str(preexisting_listeners)
240
331
  .map_err(|e| {
241
332
  magnus::Error::new(
242
333
  magnus::exception::standard_error(),
@@ -244,24 +335,24 @@ impl ServerParams {
244
335
  )
245
336
  })?;
246
337
 
247
- binds
338
+ self.binds
248
339
  .iter()
249
340
  .cloned()
250
341
  .map(|bind| {
251
342
  if let Some(fd) = bind_to_fd_map.get(&bind.listener_address_string()) {
252
- Listener::inherit_fd(bind, *fd, &socket_opts)
343
+ Listener::inherit_fd(bind, *fd, &self.socket_opts)
253
344
  } else {
254
- Listener::build(bind, &socket_opts)
345
+ Listener::build(bind, &self.socket_opts)
255
346
  }
256
347
  })
257
348
  .collect::<std::result::Result<Vec<Listener>, _>>()?
258
349
  .into_iter()
259
350
  .collect::<Vec<_>>()
260
351
  } else {
261
- binds
352
+ self.binds
262
353
  .iter()
263
354
  .cloned()
264
- .map(|b| Listener::build(b, &socket_opts))
355
+ .map(|b| Listener::build(b, &self.socket_opts))
265
356
  .collect::<std::result::Result<Vec<Listener>, _>>()?
266
357
  .into_iter()
267
358
  .collect::<Vec<_>>()
@@ -276,29 +367,9 @@ impl ServerParams {
276
367
  })
277
368
  .collect::<Result<HashMap<String, i32>>>()?;
278
369
 
279
- Ok(ServerParams {
280
- workers,
281
- worker_memory_limit,
282
- silence,
283
- multithreaded_reactor,
284
- pin_worker_cores,
285
- shutdown_timeout,
286
- hooks,
287
- preload,
288
- request_timeout,
289
- header_read_timeout,
290
- notify_watchers,
291
- threads,
292
- scheduler_threads,
293
- streamable_body,
294
- scheduler_class,
295
- oob_gc_responses_threshold,
296
- binds,
297
- listener_info: Mutex::new(listener_info),
298
- listeners: Mutex::new(listeners),
299
- middleware_loader: middleware_loader.into(),
300
- middleware: OnceLock::new(),
301
- })
370
+ *self.listener_info.lock() = listener_info;
371
+ *self.listeners.lock() = listeners;
372
+ Ok(())
302
373
  }
303
374
  }
304
375
 
@@ -310,28 +381,34 @@ impl ItsiServerConfig {
310
381
  itsi_config_proc: Option<Proc>,
311
382
  ) -> Result<Self> {
312
383
  let itsi_config_proc = Arc::new(itsi_config_proc.map(HeapValue::from));
313
- let server_params = Self::combine_params(
384
+ match Self::combine_params(
314
385
  ruby,
315
386
  cli_params,
316
387
  itsifile_path.as_ref(),
317
388
  itsi_config_proc.clone(),
318
- )?;
319
-
320
- cli_params.delete::<_, Value>(Symbol::new("listeners"))?;
389
+ ) {
390
+ Ok(server_params) => {
391
+ cli_params.delete::<_, Value>(Symbol::new("listeners"))?;
321
392
 
322
- let watcher_fd = if let Some(watchers) = server_params.notify_watchers.clone() {
323
- file_watcher::watch_groups(watchers)?
324
- } else {
325
- None
326
- };
327
-
328
- Ok(ItsiServerConfig {
329
- cli_params: Arc::new(cli_params.into()),
330
- server_params: RwLock::new(server_params.clone()).into(),
331
- itsi_config_proc,
332
- itsifile_path,
333
- watcher_fd: watcher_fd.into(),
334
- })
393
+ let watcher_fd = if let Some(watchers) = server_params.notify_watchers.clone() {
394
+ file_watcher::watch_groups(watchers)?
395
+ } else {
396
+ None
397
+ };
398
+
399
+ Ok(ItsiServerConfig {
400
+ cli_params: Arc::new(cli_params.into()),
401
+ server_params: RwLock::new(server_params.clone()).into(),
402
+ itsi_config_proc,
403
+ itsifile_path,
404
+ watcher_fd: watcher_fd.into(),
405
+ })
406
+ }
407
+ Err(err) => Err(magnus::Error::new(
408
+ magnus::exception::standard_error(),
409
+ format!("Error loading initial configuration {:?}", err),
410
+ )),
411
+ }
335
412
  }
336
413
 
337
414
  /// Reload
@@ -397,7 +474,7 @@ impl ItsiServerConfig {
397
474
  Ok(())
398
475
  }
399
476
 
400
- pub fn get_config_errors(&self) -> Option<Vec<String>> {
477
+ pub async fn get_config_errors(&self) -> Option<Vec<String>> {
401
478
  let rb_param_hash = call_with_gvl(|ruby| {
402
479
  let inner = self
403
480
  .itsi_config_proc
@@ -424,8 +501,13 @@ impl ItsiServerConfig {
424
501
  let err_val = call_with_gvl(|_| format!("{}", err));
425
502
  return Some(vec![err_val]);
426
503
  }
427
- if let Err(err) =
428
- block_on(params_arc.middleware.get().unwrap().initialize_layers())
504
+
505
+ if let Err(err) = params_arc
506
+ .middleware
507
+ .get()
508
+ .unwrap()
509
+ .initialize_layers()
510
+ .await
429
511
  {
430
512
  let err_val = call_with_gvl(|_| format!("{}", err));
431
513
  return Some(vec![err_val]);
@@ -477,8 +559,8 @@ impl ItsiServerConfig {
477
559
  }
478
560
  }
479
561
 
480
- pub fn check_config(&self) -> bool {
481
- if let Some(errors) = self.get_config_errors() {
562
+ pub async fn check_config(&self) -> bool {
563
+ if let Some(errors) = self.get_config_errors().await {
482
564
  Self::print_config_errors(errors);
483
565
  return false;
484
566
  }
@@ -42,6 +42,7 @@ impl ItsiServer {
42
42
 
43
43
  #[instrument(skip(self))]
44
44
  pub fn start(&self) -> Result<()> {
45
+ self.config.lock().server_params.read().setup_listeners()?;
45
46
  let result = if self.config.lock().server_params.read().silence {
46
47
  run_silently(|| self.build_and_run_strategy())
47
48
  } else {
@@ -49,6 +50,7 @@ impl ItsiServer {
49
50
  self.build_and_run_strategy()
50
51
  };
51
52
  if let Err(e) = result {
53
+ error!("Error starting server: {:?}", e);
52
54
  if let Some(err_value) = e.value() {
53
55
  print_rb_backtrace(err_value);
54
56
  }
@@ -10,6 +10,7 @@ use std::{
10
10
  path::PathBuf,
11
11
  str::FromStr,
12
12
  };
13
+ use tracing::{instrument, Level};
13
14
 
14
15
  #[derive(Debug, Clone)]
15
16
  pub enum BindAddress {
@@ -86,6 +87,7 @@ impl std::fmt::Debug for Bind {
86
87
  impl FromStr for Bind {
87
88
  type Err = ItsiError;
88
89
 
90
+ #[instrument(ret(level = Level::DEBUG))]
89
91
  fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
90
92
  let (protocol, remainder) = if let Some((proto, rest)) = s.split_once("://") {
91
93
  (proto.parse::<BindProtocol>()?, rest)
@@ -180,6 +182,7 @@ fn parse_bind_options(query: &str) -> HashMap<String, String> {
180
182
  }
181
183
 
182
184
  /// Attempts to resolve a hostname into an IP address.
185
+ #[instrument(ret(level = Level::DEBUG))]
183
186
  fn resolve_hostname(hostname: &str) -> Option<IpAddr> {
184
187
  (hostname, 0)
185
188
  .to_socket_addrs()
@@ -418,8 +418,12 @@ fn connect_tcp_socket(addr: IpAddr, port: u16, socket_opts: &SocketOpts) -> Resu
418
418
  .set_recv_buffer_size(socket_opts.recv_buffer_size)
419
419
  .ok();
420
420
  socket.set_only_v6(false).ok();
421
- socket.bind(&socket_address.into())?;
422
- socket.listen(socket_opts.listen_backlog as i32)?;
421
+ if let Err(e) = socket.bind(&socket_address.into()) {
422
+ error!("Failed to bind socket: {}", e);
423
+ };
424
+ if let Err(e) = socket.listen(socket_opts.listen_backlog as i32) {
425
+ error!("Failed to listen on socket: {}", e);
426
+ };
423
427
  Ok(socket.into())
424
428
  }
425
429
 
@@ -430,8 +434,11 @@ fn connect_unix_socket(path: &PathBuf, socket_opts: &SocketOpts) -> Result<UnixL
430
434
 
431
435
  let socket_address = socket2::SockAddr::unix(path)?;
432
436
 
433
- socket.bind(&socket_address)?;
434
- socket.listen(socket_opts.listen_backlog as i32)?;
435
-
437
+ if let Err(e) = socket.bind(&socket_address) {
438
+ error!("Failed to bind socket: {}", e);
439
+ };
440
+ if let Err(e) = socket.listen(socket_opts.listen_backlog as i32) {
441
+ error!("Failed to listen on socket: {}", e);
442
+ };
436
443
  Ok(socket.into())
437
444
  }
@@ -3,8 +3,10 @@ use itsi_acme::{AcmeAcceptor, AcmeConfig, AcmeState};
3
3
  use itsi_error::Result;
4
4
  use itsi_tracing::info;
5
5
  use locked_dir_cache::LockedDirCache;
6
+ use rcgen::ExtendedKeyUsagePurpose;
6
7
  use rcgen::{
7
- BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa, KeyPair, SanType,
8
+ BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa, KeyPair, KeyUsagePurpose,
9
+ SanType,
8
10
  };
9
11
  use rustls::{
10
12
  pki_types::{CertificateDer, PrivateKeyDer},
@@ -228,13 +230,14 @@ pub fn generate_ca_signed_cert(
228
230
  .push(DnType::CommonName, domains[0].clone());
229
231
 
230
232
  ee_params.use_authority_key_identifier_extension = true;
233
+ ee_params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth];
231
234
 
232
235
  let ee_cert = ee_params.signed_by(&ee_key, &ca_cert, &ca_kp).unwrap();
233
236
  let ee_cert_der = ee_cert.der().to_vec();
234
237
  let ee_cert = CertificateDer::from(ee_cert_der);
235
- let ca_cert = CertificateDer::from(ca_cert.der().to_vec());
238
+
236
239
  Ok((
237
- vec![ee_cert, ca_cert],
240
+ vec![ee_cert],
238
241
  PrivateKeyDer::try_from(ee_key.serialize_der()).unwrap(),
239
242
  ))
240
243
  }
@@ -253,12 +256,17 @@ fn get_or_create_local_dev_ca() -> Result<(String, String)> {
253
256
 
254
257
  Ok((key_pem, cert_pem))
255
258
  } else {
256
- let subject_alt_names = vec!["dev.itsi.fyi".to_string(), "localhost".to_string()];
259
+ let subject_alt_names = vec!["ca.itsi.fyi".to_string(), "localhost".to_string()];
257
260
  let mut params = CertificateParams::new(subject_alt_names)?;
258
261
  let mut distinguished_name = DistinguishedName::new();
259
- distinguished_name.push(DnType::CommonName, "Itsi Development CA");
262
+ distinguished_name.push(DnType::CommonName, "ca.itsi.fyi");
260
263
  params.distinguished_name = distinguished_name;
261
264
  params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
265
+ params.key_usages = vec![
266
+ KeyUsagePurpose::KeyCertSign,
267
+ KeyUsagePurpose::CrlSign,
268
+ KeyUsagePurpose::DigitalSignature, // useful for OCSP/CRL signing
269
+ ];
262
270
  let key_pair = KeyPair::generate()?;
263
271
  let cert = params.self_signed(&key_pair)?;
264
272
 
@@ -1,23 +1,23 @@
1
+ use super::{token_source::TokenSource, ErrorResponse, FromValue, MiddlewareLayer};
1
2
  use crate::{
2
3
  server::http_message_types::{HttpRequest, HttpResponse, RequestExt},
3
4
  services::itsi_http_service::HttpRequestContext,
4
5
  };
5
-
6
- use super::{ErrorResponse, FromValue, MiddlewareLayer};
7
-
8
6
  use async_trait::async_trait;
9
7
  use either::Either;
10
8
  use itsi_error::ItsiError;
11
9
  use magnus::error::Result;
12
10
  use regex::RegexSet;
13
11
  use serde::Deserialize;
14
- use std::sync::OnceLock;
12
+ use std::{collections::HashMap, sync::OnceLock};
13
+ use tracing::debug;
15
14
 
16
15
  #[derive(Debug, Clone, Deserialize)]
17
16
  pub struct AllowList {
18
17
  #[serde(skip_deserializing)]
19
18
  pub allowed_ips: OnceLock<RegexSet>,
20
19
  pub allowed_patterns: Vec<String>,
20
+ pub trusted_proxies: HashMap<String, TokenSource>,
21
21
  #[serde(default = "forbidden_error_response")]
22
22
  pub error_response: ErrorResponse,
23
23
  }
@@ -42,7 +42,14 @@ impl MiddlewareLayer for AllowList {
42
42
  context: &mut HttpRequestContext,
43
43
  ) -> Result<Either<HttpRequest, HttpResponse>> {
44
44
  if let Some(allowed_ips) = self.allowed_ips.get() {
45
- if !allowed_ips.is_match(&context.addr) {
45
+ let addr = if self.trusted_proxies.contains_key(&context.addr) {
46
+ let source = self.trusted_proxies.get(&context.addr).unwrap();
47
+ source.extract_token(&req).unwrap_or(&context.addr)
48
+ } else {
49
+ &context.addr
50
+ };
51
+ if !allowed_ips.is_match(addr) {
52
+ debug!(target: "middleware::allow_list", "IP address {} is not allowed", addr);
46
53
  return Ok(Either::Right(
47
54
  self.error_response
48
55
  .to_http_response(req.accept().into())
@@ -11,6 +11,7 @@ use async_trait::async_trait;
11
11
  use either::Either;
12
12
  use magnus::error::Result;
13
13
  use serde::Deserialize;
14
+ use tracing::debug;
14
15
 
15
16
  type PasswordHash = String;
16
17
 
@@ -51,6 +52,8 @@ impl MiddlewareLayer for AuthAPIKey {
51
52
  }
52
53
  TokenSource::Query(query_name) => req.query_param(query_name),
53
54
  } {
55
+ debug!(target: "middleware::auth_api_key", "API Key Retrieved. Anonymous {}", self.key_id_source.is_none());
56
+
54
57
  if let Some(key_id) = self.key_id_source.as_ref() {
55
58
  let key_id = match &key_id {
56
59
  TokenSource::Header { name, prefix } => {
@@ -66,17 +69,21 @@ impl MiddlewareLayer for AuthAPIKey {
66
69
  }
67
70
  TokenSource::Query(query_name) => req.query_param(query_name),
68
71
  };
72
+ debug!(target: "middleware::auth_api_key", "Key ID Retrieved");
69
73
  if let Some(hash) = key_id.and_then(|kid| self.valid_keys.get(kid)) {
74
+ debug!(target: "middleware::auth_api_key", "Key for ID found");
70
75
  if password_hasher::verify_password_hash(submitted_key, hash).is_ok_and(|v| v) {
71
76
  return Ok(Either::Left(req));
72
77
  }
73
78
  }
74
- } else if self.valid_keys.iter().any(|(_key_id, key)| {
79
+ } else if self.valid_keys.values().any(|key| {
75
80
  password_hasher::verify_password_hash(submitted_key, key).is_ok_and(|v| v)
76
81
  }) {
77
82
  return Ok(Either::Left(req));
78
83
  }
79
84
  }
85
+
86
+ debug!(target: "middleware::auth_api_key", "Failed to authenticate API key");
80
87
  Ok(Either::Right(
81
88
  self.error_response
82
89
  .to_http_response(req.accept().into())
@@ -8,6 +8,7 @@ use magnus::error::Result;
8
8
  use serde::{Deserialize, Serialize};
9
9
  use std::collections::HashMap;
10
10
  use std::str;
11
+ use tracing::debug;
11
12
 
12
13
  use crate::{
13
14
  server::http_message_types::{HttpRequest, HttpResponse, RequestExt},
@@ -48,6 +49,7 @@ impl MiddlewareLayer for AuthBasic {
48
49
  let auth_header = req.header("Authorization");
49
50
 
50
51
  if !auth_header.is_some_and(|header| header.starts_with("Basic ")) {
52
+ debug!(target: "middleware::auth_basic", "Basic auth failed. Authorization Header doesn't start with 'Basic '");
51
53
  return Ok(Either::Right(self.basic_auth_failed_response()));
52
54
  }
53
55
 
@@ -57,6 +59,7 @@ impl MiddlewareLayer for AuthBasic {
57
59
  let decoded = match general_purpose::STANDARD.decode(encoded_credentials) {
58
60
  Ok(bytes) => bytes,
59
61
  Err(_) => {
62
+ debug!(target: "middleware::auth_basic", "Basic auth failed. Decoding failed");
60
63
  return Ok(Either::Right(self.basic_auth_failed_response()));
61
64
  }
62
65
  };
@@ -64,6 +67,7 @@ impl MiddlewareLayer for AuthBasic {
64
67
  let decoded_str = match str::from_utf8(&decoded) {
65
68
  Ok(s) => s,
66
69
  Err(_) => {
70
+ debug!(target: "middleware::auth_basic", "Basic auth failed. Decoding failed");
67
71
  return Ok(Either::Right(self.basic_auth_failed_response()));
68
72
  }
69
73
  };
@@ -71,6 +75,7 @@ impl MiddlewareLayer for AuthBasic {
71
75
  let mut parts = decoded_str.splitn(2, ':');
72
76
  let username = parts.next().unwrap_or("");
73
77
  let password = parts.next().unwrap_or("");
78
+
74
79
  match self.credential_pairs.get(username) {
75
80
  Some(expected_password_hash) => {
76
81
  match verify_password_hash(password, expected_password_hash) {
@@ -78,7 +83,10 @@ impl MiddlewareLayer for AuthBasic {
78
83
  _ => Ok(Either::Right(self.basic_auth_failed_response())),
79
84
  }
80
85
  }
81
- None => Ok(Either::Right(self.basic_auth_failed_response())),
86
+ None => {
87
+ debug!(target: "middleware::auth_basic", "Basic auth failed. Username {} not found", username);
88
+ Ok(Either::Right(self.basic_auth_failed_response()))
89
+ }
82
90
  }
83
91
  }
84
92
  }