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
@@ -92,7 +92,14 @@ module Itsi
92
92
  )
93
93
 
94
94
  if json
95
- validate!(json, as: as) if as
95
+ if as
96
+ begin
97
+ validate!(json, as: as)
98
+ rescue ValidationError => e
99
+ json = {type: 'error', message: "Validation Error: #{e.message}"}
100
+ status = 400
101
+ end
102
+ end
96
103
  body = json.to_json
97
104
  headers ||= {}
98
105
  headers["Content-Type"] ||= "application/json"
@@ -111,6 +118,8 @@ module Itsi
111
118
  end
112
119
 
113
120
  response.respond(status: status, headers: headers, body: body, hijack: hijack, &blk)
121
+
122
+
114
123
  end
115
124
 
116
125
  def hijack
@@ -124,12 +133,16 @@ module Itsi
124
133
  end
125
134
  end
126
135
 
136
+ def body
137
+ @body ||= build_input_io
138
+ end
139
+
127
140
  def build_input_io
128
- case body
141
+ case body_parts
129
142
  when nil then EMPTY_IO
130
- when String then StringIO.new(body)
131
- when Array then File.open(body.first, "rb")
132
- else body
143
+ when String then StringIO.new(body_parts)
144
+ when Array then File.open(body_parts.first, "rb")
145
+ else body_parts
133
146
  end
134
147
  end
135
148
 
@@ -155,15 +168,33 @@ module Itsi
155
168
  end
156
169
 
157
170
  params.merge!(query_params).merge!(url_params)
158
-
159
- yield schema ? apply_schema!(params, schema) : params
160
-
161
- rescue StandardError => e
171
+ validated = schema ? apply_schema!(params, schema) : params
172
+ unless block_given?
173
+ if multipart?
174
+ raise "#params must take a block for multipart requests"
175
+ else
176
+ return validated
177
+ end
178
+ else
179
+ yield validated
180
+ end
181
+ rescue ValidationError => e
162
182
  if response.json?
163
183
  respond(json: {error: e.message}, status: 400)
164
184
  else
165
185
  respond(e.message, 400)
166
186
  end
187
+ rescue StandardError => e
188
+ Itsi.log_error e.message
189
+ puts e.backtrace
190
+
191
+ # Unexpected error.
192
+ # Don't reveal potential sensitive information to client.
193
+ if response.json?
194
+ respond(json: {error: "Internal Server Error"}, status: 500)
195
+ else
196
+ respond("Internal Server Error", 500)
197
+ end
167
198
  ensure
168
199
  clean_temp_files(params)
169
200
  end
@@ -8,7 +8,8 @@ module Itsi
8
8
  class HttpResponse
9
9
 
10
10
  def respond _body=nil, _status=200, _header=nil, status: _status, headers: _header, body: _body, hijack: false, &blk
11
- self.status = status
11
+
12
+ self.status = status.kind_of?(Symbol) ? HTTP_STATUS_NAME_TO_CODE_MAP.fetch(status) : status.to_i
12
13
 
13
14
  body = body.to_s unless body.is_a?(String)
14
15
 
@@ -34,7 +34,6 @@ module Itsi
34
34
  end
35
35
 
36
36
  def echo(filename, algorithm)
37
- return unless (creds = load(filename))
38
37
  print "Enter username: "
39
38
  username = $stdin.gets.chomp
40
39
 
@@ -4,10 +4,11 @@ module Itsi
4
4
  module ConfigHelpers
5
5
 
6
6
  def self.load_and_register(klass)
7
- config_type = klass.name.split("::").last.downcase
7
+ config_type = klass.name.split("::").last.downcase.gsub(/([a-z]()[A-Z])/, '\1_\2')
8
+
8
9
  listing = [
9
- Dir[File.expand_path(File.dirname(__FILE__) + "/#{config_type}/**.rb")],
10
- Dir[File.expand_path(File.dirname(__FILE__) + "/#{config_type}s/**.rb")]
10
+ Dir[File.expand_path(File.dirname(__FILE__) + "/#{config_type}/**/*.rb")],
11
+ Dir[File.expand_path(File.dirname(__FILE__) + "/#{config_type}s/**/*.rb")]
11
12
  ].flatten
12
13
 
13
14
  listing.each do |file|
@@ -17,6 +18,9 @@ module Itsi
17
18
  new_class = (following - current).first
18
19
 
19
20
  documentation_file = "#{file[/(.*)\.rb/,1]}.md"
21
+ if ! File.exist?(documentation_file)
22
+ documentation_file = "#{file[/(.*)\/[^\/]+\.rb/,1]}/_index.md"
23
+ end
20
24
  if File.exist?(documentation_file) && new_class
21
25
  new_class.documentation IO.read(documentation_file)
22
26
  .gsub(/^---.*?\n.*?-+/m,'') # Strip frontmatter
@@ -59,6 +63,8 @@ module Itsi
59
63
  @middleware_class_attrs ||= {}
60
64
  if blk
61
65
  @middleware_class_attrs[:schema] = TypedStruct.new(&blk)
66
+ elsif value
67
+ @middleware_class_attrs[:schema] = value
62
68
  else
63
69
  @middleware_class_attrs[:schema]
64
70
  end
@@ -72,21 +78,27 @@ module Itsi
72
78
  cls.define_singleton_method("#{config_type}_name") do |name=self.name|
73
79
  @config_name ||= name.split("::").last.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
74
80
  end
81
+ cls.define_method(:opt_name){ self.class.send("#{config_type}_name") }
82
+ cls.define_method(:location){ @location }
75
83
  end
76
84
 
77
- def initialize(location, params)
85
+ def initialize(location, params={})
86
+ if !self.class.ancestors.include?(Middleware) && !location.parent.nil?
87
+ raise "#{opt_name} must be set at the top level"
88
+ end
78
89
  @location = location
79
90
  @params = case self.schema
80
91
  when TypedStruct::Validation
81
92
  self.schema.validate!(params)
93
+ when Array
94
+ default, validation = self.schema
95
+ params ? validation.validate!(params) : default
96
+ when nil
97
+ nil
82
98
  else
83
99
  self.schema.new(params).to_h
84
100
  end
85
101
  end
86
-
87
- def build!
88
- @params
89
- end
90
102
  end
91
103
  end
92
104
  end
@@ -6,7 +6,7 @@ module Itsi
6
6
  require_relative "option"
7
7
  require_relative "middleware"
8
8
 
9
- attr_reader :parent, :children, :middleware, :controller_class, :routes, :http_methods, :protocols,
9
+ attr_reader :parent, :children, :middleware, :controller, :routes, :http_methods, :protocols,
10
10
  :hosts, :ports, :extensions, :content_types, :accepts, :options
11
11
 
12
12
  def self.evaluate(config = Itsi::Server::Config.config_file_path, &blk)
@@ -17,6 +17,7 @@ module Itsi
17
17
  code = IO.read(config)
18
18
  instance_eval(code, config.to_s, 1)
19
19
  end
20
+ location("*"){}
20
21
  end
21
22
  [config.options, config.errors]
22
23
  rescue Exception => e
@@ -39,7 +40,6 @@ module Itsi
39
40
  @parent = parent
40
41
  @children = []
41
42
  @middleware = {}
42
- @controller_class = nil
43
43
 
44
44
  @controller = controller
45
45
  @routes = Array(routes).flatten
@@ -57,6 +57,11 @@ module Itsi
57
57
  @options[:middleware_loaders].each(&:call)
58
58
  @middleware[:app] ||= {}
59
59
  @middleware[:app][:app_proc] = @middleware[:app]&.[](:preloader)&.call || DEFAULT_APP[]
60
+ if errors.any?
61
+ error = errors.first.first
62
+ error.set_backtrace(error.backtrace.drop_while{|r| r =~ /itsi\/server\/config/ })
63
+ raise error
64
+ end
60
65
  flatten_routes
61
66
  end
62
67
  }
@@ -66,13 +71,13 @@ module Itsi
66
71
  end
67
72
 
68
73
  def errors
69
- @children.map(&:errors).flatten + @errors
74
+ @children.map(&:errors).flatten(1) + @errors
70
75
  end
71
76
 
72
77
  Option.subclasses.each do |option|
73
78
  option_name = option.option_name
74
79
  define_method(option_name) do |*args, **kwargs, &blk|
75
- @options[option_name] = option.new(self, *args, **kwargs, &blk).build!
80
+ option.new(self, *args, **kwargs, &blk).build!
76
81
  rescue => e
77
82
  @errors << [e, caller[1]]
78
83
  end
@@ -81,452 +86,28 @@ module Itsi
81
86
  Middleware.subclasses.each do |middleware|
82
87
  middleware_name = middleware.middleware_name
83
88
  define_method(middleware_name) do |*args, **kwargs, &blk|
84
- @middleware[middleware_name] = middleware.new(@parent, *args, **kwargs, &blk).build!
89
+ middleware.new(self, *args, **kwargs, &blk).build!
90
+ rescue Config::Endpoint::InvalidHandlerException => e
91
+ @errors << [e, "#{e.backtrace[0]}:in #{e.message}"]
85
92
  rescue => e
86
93
  @errors << [e, caller[1]]
87
94
  end
88
95
  end
89
96
 
90
- def oob_gc_responses_threshold(threshold)
91
- raise "OOB GC responses threshold must be set at the root" unless @parent.nil?
92
-
93
- @options[:oob_gc_responses_threshold] = threshold.to_i
94
- end
95
-
96
- def log_level(level)
97
- raise "Log level must be set at the root" unless @parent.nil?
98
-
99
- @options[:log_level] = level.to_s
100
- end
101
-
102
- def log_target(target)
103
- raise "Log target must be set at the root" unless @parent.nil?
104
-
105
- @options[:log_target] = target.to_s
106
- end
107
-
108
- def log_target_filters(target_filters)
109
- raise "Log target filters must be set at the root" unless @parent.nil?
110
-
111
- @options[:log_target_filters] = target_filters
112
- end
113
-
114
- def log_format(target)
115
- raise "Log format must be set at the root" unless @parent.nil?
116
-
117
- @options[:log_format] = target.to_s
118
- end
119
-
120
- def get(route, app_proc = nil, nonblocking: false, &blk)
121
- endpoint(route, [:get], app_proc, nonblocking: nonblocking, &blk)
122
- end
123
-
124
- def post(route, app_proc = nil, nonblocking: false, &blk)
125
- endpoint(route, [:post], app_proc, nonblocking: nonblocking, &blk)
126
- end
127
-
128
- def put(route, app_proc = nil, nonblocking: false, &blk)
129
- endpoint(route, [:put], app_proc, nonblocking: nonblocking, &blk)
130
- end
131
-
132
- def delete(route, app_proc = nil, nonblocking: false, &blk)
133
- endpoint(route, [:delete], app_proc, nonblocking: nonblocking, &blk)
134
- end
135
-
136
- def patch(route, app_proc = nil, nonblocking: false, &blk)
137
- endpoint(route, [:patch], app_proc, nonblocking: nonblocking, &blk)
138
- end
139
-
140
- def endpoint(route=nil, methods=[], app_proc = nil, nonblocking: false, &blk)
141
- raise "You must provide either a block or an explicit handler for the endpoint" if app_proc.nil? && blk.nil?
142
-
143
- app_proc = @controller.method(app_proc).to_proc if app_proc.is_a?(Symbol)
144
-
145
- app_proc ||= blk
146
- num_required, keywords = Itsi::Server::TypedHandlers::SourceParser.extract_expr_from_source_location(app_proc)
147
- params_schema = keywords[:params]
148
-
149
- if params_schema && num_required > 1
150
- raise "Cannot accept multiple required parameters in a single endpoint. A single typed or untyped params argument is supported"
151
- end
152
- if num_required > 2
153
- raise "Cannot accept more than two required parameters in a single endpoint. An can either accept a single request argument, or a request and a params argument (which may be typed or untyped)"
154
- end
155
- if num_required == 0
156
- raise "Cannot accept zero required parameters in a single endpoint. Endpoint must accept a request parameter"
157
- end
158
-
159
- accepts_params = !params_schema.nil? || num_required > 1
160
-
161
- if accepts_params
162
- app_proc = Itsi::Server::TypedHandlers.handler_for(app_proc, params_schema)
163
- end
164
-
165
- if route || http_methods.any?
166
- # For endpoints, it's usually assumed trailing slash and non-trailing slash behaviour is the same
167
- routes = route == "/" ? ["", "/"] : [route]
168
- location(*routes, methods: http_methods) do
169
- @middleware[:app] = { preloader: -> { app_proc }, nonblocking: nonblocking }
170
- end
171
- else
172
- app = { preloader: -> { app_proc }, nonblocking: nonblocking }
173
- @middleware[:app] = app
174
- location("*") do
175
- @middleware[:app] = app
176
- end
177
- end
178
- end
179
-
180
- def grpc(*handlers, reflection: true, nonblocking: false, **, &blk)
181
- if @middleware[:app] && @middleware[:app][:request_type].to_s != "grpc"
182
- raise "App has already been set. You can use only one of `run` and `rackup_file` or `grpc` per location"
183
- end
184
-
185
- grpc_reflection(handlers) if reflection
186
-
187
- handlers.each do |handler|
188
- location(Regexp.new("#{Regexp.escape(handler.class.service_name)}/(?:#{handler.class.rpc_descs.keys.map(&:to_s).join("|")})")) do
189
- @middleware[:app] = { preloader: -> { Itsi::Server::GrpcInterface.for(handler) }, request_type: "grpc", nonblocking: nonblocking }
190
- instance_exec(&blk)
191
- end
192
- end
193
- end
194
-
195
97
  def grpc_reflection(handlers)
196
98
  @grpc_reflected_services ||= []
197
99
  @grpc_reflected_services.concat(handlers)
198
100
 
199
- location(["grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo",
200
- "grpc.reflection.v1.ServerReflection/ServerReflectionInfo"]) do
101
+ location("grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo",
102
+ "grpc.reflection.v1.ServerReflection/ServerReflectionInfo") do
201
103
  @middleware[:app] = { preloader: lambda {
202
104
  Itsi::Server::GrpcInterface.reflection_for(handlers)
203
105
  }, request_type: "grpc" }
204
106
  end
205
107
  end
206
108
 
207
- def run(app, sendfile: true, nonblocking: false, path_info: "/")
208
- app_args = { preloader: -> { Itsi::Server::RackInterface.for(app) }, sendfile: sendfile, base_path: "^(?<base_path>#{paths_from_parent.gsub(/\.\*\)$/, ')')}).*$", path_info: path_info, nonblocking: nonblocking }
209
- base_path = "^(?<base_path>#{paths_from_parent.gsub(/\.\*\)$/, ')')}).*$"
210
- @middleware[:app] = app_args
211
- location("*") do
212
- @middleware[:app] = app_args
213
- end
214
- end
215
-
216
- def rackup_file(rackup_file, nonblocking: false, sendfile: true, path_info: "/")
217
- raise "Rackup file #{rackup_file} doesn't exist" unless File.exist?(rackup_file)
218
- app_args = { preloader: -> { Itsi::Server::RackInterface.for(rackup_file) }, sendfile: sendfile, base_path: "^(?<base_path>#{paths_from_parent.gsub(/\.\*\)$/, ')')}).*$", path_info: path_info, nonblocking: nonblocking }
219
- @middleware[:app] = app_args
220
- location("*") do
221
- @middleware[:app] = app_args
222
- end
223
- end
224
-
225
- def include(path)
226
- code = IO.read("#{path}.rb")
227
- instance_eval(code, "#{path}.rb", 1)
228
- end
229
-
230
- def reuse_address(reuse_address)
231
- raise "reuse_address must be set at the root" unless @parent.nil?
232
- @options[:reuse_address] = reuse_address
233
- end
234
-
235
- def reuse_port(reuse_port)
236
- raise "reuse_port must be set at the root" unless @parent.nil?
237
- @options[:reuse_port] = reuse_port
238
- end
239
-
240
- def listen_backlog(listen_backlog)
241
- raise "listen_backlog must be set at the root" unless @parent.nil?
242
- @options[:listen_backlog] = listen_backlog
243
- end
244
-
245
- def nodelay(nodelay)
246
- raise "nodelay must be set at the root" unless @parent.nil?
247
- @options[:nodelay] = nodelay
248
- end
249
-
250
- def recv_buffer_size(recv_buffer_size)
251
- raise "recv_buffer_size must be set at the root" unless @parent.nil?
252
- @options[:recv_buffer_size] = recv_buffer_size
253
- end
254
-
255
- def bind(bind_str)
256
- raise "Bind must be set at the root" unless @parent.nil?
257
-
258
- @options[:binds] ||= []
259
- @options[:binds] << bind_str.to_s
260
- end
261
-
262
- def after_fork(&block)
263
- raise "After fork must be set at the root" unless @parent.nil?
264
-
265
- @options[:hooks] ||= {}
266
- @options[:hooks][:after_fork] = block
267
- end
268
-
269
- def before_fork(&block)
270
- raise "Before fork must be set at the root" unless @parent.nil?
271
-
272
- @options[:hooks] ||= {}
273
- @options[:hooks][:before_fork] = block
274
- end
275
-
276
- def after_memory_threshold_reached(&block)
277
- raise "Before fork must be set at the root" unless @parent.nil?
278
-
279
- @options[:hooks] ||= {}
280
- @options[:hooks][:after_memory_threshold_reached] = block
281
- end
282
-
283
- def worker_memory_limit(memory_limit)
284
- raise "Worker memory limit must be set at the root" unless @parent.nil?
285
-
286
- @options[:worker_memory_limit] = memory_limit
287
- end
288
-
289
- def multithreaded_reactor(multithreaded)
290
- raise "Multithreaded reactor must be set at the root" unless @parent.nil?
291
-
292
- @options[:multithreaded_reactor] = !!multithreaded
293
- end
294
-
295
- def pin_worker_cores(pin_worker_cores)
296
- raise "Pin worker cores must be set at the root" unless @parent.nil?
297
-
298
- @options[:pin_worker_cores] = !!pin_worker_cores
299
- end
300
-
301
- def auto_reload_config!
302
- if ENV["BUNDLE_BIN_PATH"]
303
- watch "Itsi.rb", [%w[bundle exec itsi restart]]
304
- else
305
- watch "Itsi.rb", [%w[itsi restart]]
306
- end
307
- end
308
-
309
- def watch(path, commands)
310
- raise "Watch be set at the root" unless @parent.nil?
311
-
312
- @options[:notify_watchers] ||= []
313
- @options[:notify_watchers] << [path, commands]
314
- end
315
-
316
- def fiber_scheduler(klass_name = true)
317
- raise "Fiber scheduler must be set at the root" unless @parent.nil?
318
-
319
- klass_name = "Itsi::Scheduler" if klass_name == true
320
- @options[:scheduler_class] = klass_name if klass_name
321
- end
322
-
323
- def scheduler_threads(threads = 1)
324
- raise "Scheduler threads must be set at the root" unless @parent.nil?
325
-
326
- @options[:scheduler_threads] = threads
327
- end
328
-
329
- def request_timeout(request_timeout)
330
- raise "Request timeout must be set at the root" unless @parent.nil?
331
-
332
- @options[:request_timeout] = request_timeout
333
- end
334
-
335
- def preload(preload)
336
- raise "Preload must be set at the root" unless @parent.nil?
337
-
338
- @options[:preload] = preload
339
- end
340
-
341
- def shutdown_timeout(shutdown_timeout)
342
- raise "Shutdown timeout must be set at the root" unless @parent.nil?
343
-
344
- @options[:shutdown_timeout] = shutdown_timeout.to_f
345
- end
346
-
347
- def header_read_timeout(header_read_timeout)
348
- raise "Header read timeout must be set at the root" unless @parent.nil?
349
-
350
- @options[:header_read_timeout] = header_read_timeout.to_f
351
- end
352
-
353
- def stream_body(stream_body)
354
- raise "Stream body must be set at the root" unless @parent.nil?
355
-
356
- @options[:stream_body] = !!stream_body
357
- end
358
-
359
- def location(*routes, methods: [], protocols: [], hosts: [], ports: [], extensions: [], content_types: [],
360
- accepts: [], &block)
361
- build_child = lambda {
362
- @children << DSL.new(
363
- self,
364
- routes: routes,
365
- methods: Array(methods) | self.http_methods,
366
- protocols: Array(protocols) | self.protocols,
367
- hosts: Array(hosts) | self.hosts,
368
- ports: Array(ports) | self.ports,
369
- extensions: Array(extensions) | self.extensions,
370
- content_types: Array(content_types) | self.content_types,
371
- accepts: Array(accepts) | self.accepts,
372
- controller: @controller,
373
- &block
374
- )
375
- }
376
- if @parent.nil?
377
- @options[:middleware_loaders] << build_child
378
- else
379
- build_child[]
380
- end
381
- end
382
-
383
-
384
- def allow_list(**args)
385
- args[:allowed_patterns] = Array(args[:allowed_patterns]).map do |pattern|
386
- if pattern.is_a?(Regexp)
387
- pattern.source
388
- else
389
- pattern
390
- end
391
- end
392
- @middleware[:allow_list] = args
393
- end
394
-
395
- def deny_list(**args)
396
- args[:denied_patterns] = Array(args[:denied_patterns]).map do |pattern|
397
- if pattern.is_a?(Regexp)
398
- pattern.source
399
- else
400
- pattern
401
- end
402
- end
403
- @middleware[:deny_list] = args
404
- end
405
-
406
- def controller(controller)
407
- @controller = controller
408
- end
409
-
410
- def auth_basic(**args)
411
- if File.exist?(".itsi-credentials") && !args[:credential_file]
412
- args[:credential_file] = ".itsi-credentials"
413
- end
414
-
415
- if args[:credential_file] && File.exist?(args[:credential_file])
416
- args[:credential_pairs] = Passfile.load(args[:credential_file])
417
- end
418
-
419
- @middleware[:auth_basic] = args
420
- end
421
-
422
- def redirect(**args)
423
- @middleware[:redirect] = args
424
- end
425
-
426
- def proxy(**args)
427
- @middleware[:proxy] = args
428
- end
429
-
430
- def static_response(**args)
431
- args[:body] = args[:body].bytes
432
- @middleware[:static_response] = args
433
- end
434
-
435
- def auth_jwt(**args)
436
- @middleware[:auth_jwt] = args
437
- end
438
-
439
- def auth_api_key(**args)
440
- if args[:valid_keys] && args[:valid_keys].is_a?(Array)
441
- args[:valid_keys] = args[:valid_keys].each_with_index.map { |key, index| [index, key] }.to_h
442
- args[:key_id_source] = nil
443
- end
444
-
445
- if File.exist?(".itsi-credentials") && !args[:credential_file]
446
- args[:credential_file] = ".itsi-credentials"
447
- end
448
-
449
- if args[:credential_file] && File.exist?(args[:credential_file])
450
- args[:valid_keys] = Passfile.load(args[:credential_file])
451
- end
452
-
453
- @middleware[:auth_api_key] = args
454
- end
455
-
456
- def compress(**args)
457
- @middleware[:compression] = args
458
- end
459
-
460
- def request_headers(**args)
461
- @middleware[:request_headers] = args
462
- end
463
-
464
- def max_body(**args)
465
- @middleware[:max_body] = args
466
- end
467
-
468
- def response_headers(**args)
469
- @middleware[:response_headers] = args
470
- end
471
-
472
- def rate_limit(**args)
473
- @middleware[:rate_limit] = args
474
- end
475
-
476
- def cache_control(**args)
477
- @middleware[:cache_control] = args
478
- end
479
-
480
- def etag(**args)
481
- @middleware[:etag] = args
482
- end
483
-
484
- def csp(**args)
485
- @middleware[:csp] = args
486
- end
487
-
488
- def intrusion_protection(**args)
489
- args[:banned_url_patterns] = Array(args[:banned_url_patterns]).flatten.map do |pattern|
490
- if pattern.is_a?(Regexp)
491
- pattern.source
492
- else
493
- pattern
494
- end
495
- end
496
- @middleware[:intrusion_protection] = args
497
- end
498
-
499
- def cors(**args)
500
- @middleware[:cors] = args
501
- end
502
-
503
- def static_assets(**args)
504
- root_dir = args[:root_dir] || "."
505
-
506
- if !File.exist?(root_dir)
507
- warn "Warning: static_assets root_dir '#{root_dir}' does not exist!"
508
- elsif !File.directory?(root_dir)
509
- warn "Warning: static_assets root_dir '#{root_dir}' is not a directory!"
510
- end
511
-
512
- args[:relative_path] = true unless args.key?(:relative_path)
513
-
514
- args[:allowed_extensions] ||= []
515
-
516
- if (args[:allowed_extensions].include?("html") || args[:allowed_extensions].include?(:html)) && args[:try_html_extension]
517
- args[:allowed_extensions] << ""
518
- end
519
-
520
- args[:base_path] = "^(?<base_path>#{paths_from_parent}).*$"
521
-
522
- location("*", extensions: args[:allowed_extensions]) do
523
- @middleware[:static_assets] = args
524
- end
525
- end
526
-
527
109
  def file_server(**args)
528
- # Forward to static_assets for implementation
529
- puts "Note: file_server is an alias for static_assets"
110
+ Itsi.log_info "Note: file_server is an alias for static_assets"
530
111
  static_assets(**args)
531
112
  end
532
113
 
@@ -603,7 +184,11 @@ module Itsi
603
184
 
604
185
  chain.each do |n|
605
186
  n.middleware.each do |k, v|
606
- merged[k] = v
187
+ if v[:combine]
188
+ merged[k] = ([merged[k] || []] + [v]).flatten
189
+ else
190
+ merged[k] = v
191
+ end
607
192
  end
608
193
  end
609
194
  deep_stringify_keys(merged)