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
@@ -41,10 +41,9 @@ module Itsi
41
41
  elsif args[:static]
42
42
  DSL.evaluate do
43
43
  location "/" do
44
- allow_list allowed_patterns: ['127.0.0.1']
45
44
  rate_limit key: 'address', store_config: 'in_memory', requests: 2, seconds: 5
46
45
  etag type: 'strong', algorithm: 'md5', min_body_size: 1024 * 1024
47
- compress min_size: 1024 * 1024, level: 'fastest', algorithms: %w[zstd gzip brotli deflate], mime_types: %w[all], compress_streams: true
46
+ compress min_size: 1024 * 1024, level: 'fastest', algorithms: %w[zstd gzip br deflate], mime_types: %w[all], compress_streams: true
48
47
  log_requests before: { level: "INFO", format: "[{request_id}] {method} {path_and_query} - {addr} " }, after: { level: "INFO", format: "[{request_id}] └─ {status} in {response_time}" }
49
48
  static_assets \
50
49
  relative_path: true,
@@ -82,50 +81,63 @@ module Itsi
82
81
  case preload
83
82
  # If we preload everything, then we'll load middleware and default rack app ahead of time
84
83
  when true
85
- preloaded_middleware = middleware_loader.call
86
- middleware_loader = -> { preloaded_middleware }
84
+ begin
85
+ Itsi.log_debug("Preloading middleware and default rack app")
86
+ preloaded_middleware = middleware_loader.call
87
+ middleware_loader = -> { preloaded_middleware }
88
+ rescue StandardError => e
89
+ errors << [e, e.backtrace[0]]
90
+ end
87
91
  # If we're just preloading a specific gem group, we'll do that here too
88
92
  when Symbol
93
+ Itsi.log_debug("Preloading gem group #{preload}")
89
94
  Bundler.require(preload)
90
95
  end
91
96
 
92
- [
93
- {
94
- workers: args.fetch(:workers) { itsifile_config.fetch(:workers, 1) },
95
- worker_memory_limit: args.fetch(:worker_memory_limit) { itsifile_config.fetch(:worker_memory_limit, nil) },
96
- silence: args.fetch(:silence) { itsifile_config.fetch(:silence, false) },
97
- shutdown_timeout: args.fetch(:shutdown_timeout) { itsifile_config.fetch(:shutdown_timeout, 5) },
98
- hooks: itsifile_config.fetch(:hooks, nil),
99
- preload: !!preload,
100
- request_timeout: itsifile_config.fetch(:request_timeout, nil),
101
- header_read_timeout: args.fetch(:header_read_timeout) { itsifile_config.fetch(:header_read_timeout, nil) },
102
- notify_watchers: itsifile_config.fetch(:notify_watchers, nil),
103
- threads: args.fetch(:threads) { itsifile_config.fetch(:threads, 1) },
104
- scheduler_threads: args.fetch(:scheduler_threads) { itsifile_config.fetch(:scheduler_threads, nil) },
105
- streamable_body: args.fetch(:streamable_body) { itsifile_config.fetch(:streamable_body, false) },
106
- multithreaded_reactor: args.fetch(:multithreaded_reactor) do
107
- itsifile_config.fetch(:multithreaded_reactor, nil)
108
- end,
109
- pin_worker_cores: args.fetch(:pin_worker_cores) { itsifile_config.fetch(:pin_worker_cores, true) },
110
- scheduler_class: args.fetch(:scheduler_class) { itsifile_config.fetch(:scheduler_class, nil) },
111
- oob_gc_responses_threshold: args.fetch(:oob_gc_responses_threshold) do
112
- itsifile_config.fetch(:oob_gc_responses_threshold, nil)
113
- end,
114
- log_level: args.fetch(:log_level) { itsifile_config.fetch(:log_level, nil) },
115
- log_format: args.fetch(:log_format) { itsifile_config.fetch(:log_format, nil) },
116
- log_target: args.fetch(:log_target) { itsifile_config.fetch(:log_target, nil) },
117
- log_target_filters: args.fetch(:log_target_filters) { itsifile_config.fetch(:log_target_filters, nil) },
118
- binds: args.fetch(:binds) { itsifile_config.fetch(:binds, ["http://0.0.0.0:3000"]) },
119
- middleware_loader: middleware_loader,
120
- listeners: args.fetch(:listeners) { nil },
121
- reuse_address: itsifile_config.fetch(:reuse_address, true),
122
- reuse_port: itsifile_config.fetch(:reuse_port, true),
123
- listen_backlog: itsifile_config.fetch(:listen_backlog, 1024 ),
124
- nodelay: itsifile_config.fetch(:nodelay, true),
125
- recv_buffer_size: itsifile_config.fetch(:recv_buffer_size, 262_144)
126
- }.transform_keys(&:to_s),
97
+ if itsifile_config[:daemonize] && !@daemonized
98
+ @daemonized = true
99
+ Itsi.log_info("Itsi is running in the background. Writing pid to #{Itsi::Server::Config.pid_file_path}")
100
+ Itsi.log_info("To stop Itsi, run 'itsi stop' from this directory.")
101
+ Process.daemon(true, false)
102
+ Server.write_pid
103
+ end
104
+
105
+ srv_config = {
106
+ workers: args.fetch(:workers) { itsifile_config.fetch(:workers, 1) },
107
+ worker_memory_limit: args.fetch(:worker_memory_limit) { itsifile_config.fetch(:worker_memory_limit, nil) },
108
+ silence: args.fetch(:silence) { itsifile_config.fetch(:silence, false) },
109
+ shutdown_timeout: args.fetch(:shutdown_timeout) { itsifile_config.fetch(:shutdown_timeout, 5) },
110
+ hooks: args[:hooks] && itsifile_config[:hooks] ? args[:hooks].merge(itsifile_config[:hooks]) : itsifile_config.fetch(:hooks, args[:hooks]),
111
+ preload: !!preload,
112
+ request_timeout: itsifile_config.fetch(:request_timeout, nil),
113
+ header_read_timeout: args.fetch(:header_read_timeout) { itsifile_config.fetch(:header_read_timeout, nil) },
114
+ notify_watchers: itsifile_config.fetch(:notify_watchers, nil),
115
+ threads: args.fetch(:threads) { itsifile_config.fetch(:threads, 1) },
116
+ scheduler_threads: args.fetch(:scheduler_threads) { itsifile_config.fetch(:scheduler_threads, nil) },
117
+ streamable_body: args.fetch(:streamable_body) { itsifile_config.fetch(:streamable_body, false) },
118
+ multithreaded_reactor: args.fetch(:multithreaded_reactor) do
119
+ itsifile_config.fetch(:multithreaded_reactor, nil)
120
+ end,
121
+ pin_worker_cores: args.fetch(:pin_worker_cores) { itsifile_config.fetch(:pin_worker_cores, true) },
122
+ scheduler_class: args.fetch(:scheduler_class) { itsifile_config.fetch(:scheduler_class, nil) },
123
+ oob_gc_responses_threshold: args.fetch(:oob_gc_responses_threshold) do
124
+ itsifile_config.fetch(:oob_gc_responses_threshold, nil)
125
+ end,
126
+ log_level: args.fetch(:log_level) { itsifile_config.fetch(:log_level, nil) },
127
+ log_format: args.fetch(:log_format) { itsifile_config.fetch(:log_format, nil) },
128
+ log_target: args.fetch(:log_target) { itsifile_config.fetch(:log_target, nil) },
129
+ log_target_filters: args.fetch(:log_target_filters) { itsifile_config.fetch(:log_target_filters, nil) },
130
+ binds: args.fetch(:binds) { itsifile_config.fetch(:binds, ["http://0.0.0.0:3000"]) },
131
+ middleware_loader: middleware_loader,
132
+ listeners: args.fetch(:listeners) { nil },
133
+ reuse_address: itsifile_config.fetch(:reuse_address, true),
134
+ reuse_port: itsifile_config.fetch(:reuse_port, true),
135
+ listen_backlog: itsifile_config.fetch(:listen_backlog, 1024 ),
136
+ nodelay: itsifile_config.fetch(:nodelay, true),
137
+ recv_buffer_size: itsifile_config.fetch(:recv_buffer_size, 262_144)
138
+ }.transform_keys(&:to_s)
127
139
 
128
- errors.flat_map do |(error, message)|
140
+ error_lines = errors.flat_map do |(error, message)|
129
141
  location = message[/(.*?)\:in/,1]
130
142
  file, lineno = location.split(":")
131
143
  lineno = lineno.to_i
@@ -135,12 +147,17 @@ module Itsi
135
147
  []
136
148
  else
137
149
  ([lineno-2, 0].max...[file_lines.length, lineno.succ.succ].min).map do |currline|
138
- [
139
- " #{currline.succ.to_s.rjust(3)} | #{file_lines[currline].strip}",
140
- if currline == lineno-1
141
- " | ^^^ "
142
- end
143
- ].compact
150
+ if currline == lineno-1
151
+ line = file_lines[currline][0...-1]
152
+ padding = line[/^\s+/]&.length || 0
153
+
154
+ [
155
+ " \e[31m#{currline.succ.to_s.rjust(3)} | #{line}\e[0m",
156
+ " | #{' ' * padding}\e[33m^^^\e[0m "
157
+ ]
158
+ else
159
+ " #{currline.succ.to_s.rjust(3)} | #{file_lines[currline][0...-1]}"
160
+ end
144
161
  end.flatten
145
162
  end
146
163
  [
@@ -149,11 +166,23 @@ module Itsi
149
166
  *info_lines
150
167
  ]
151
168
  end
152
- ]
169
+
170
+ return srv_config, error_lines
171
+ rescue
172
+ Itsi.log_error e.message
173
+ puts e.backtrace
153
174
  end
154
175
 
155
176
  def self.test!(cli_params)
156
177
  _, errors = build_config(cli_params, Itsi::Server::Config.config_file_path(cli_params[:config_file]))
178
+ unless errors.any?
179
+ begin
180
+ _["middleware_loader"][]
181
+ rescue Exception => e
182
+ errors = [e]
183
+ end
184
+ end
185
+
157
186
  if errors.any?
158
187
  Itsi.log_error("Config file is invalid")
159
188
  puts errors
@@ -181,7 +210,7 @@ module Itsi
181
210
  elsif File.exist?("config/#{ITSI_DEFAULT_CONFIG_FILE}")
182
211
  "config/#{ITSI_DEFAULT_CONFIG_FILE}"
183
212
  end
184
- # Options simply pass through unless we've specified a config file
213
+ # Options pass through unless we've specified a config file
185
214
  return unless File.exist?(config_file_path.to_s)
186
215
 
187
216
  config_file_path
@@ -73,7 +73,7 @@ worker_memory_limit 1024 * 1024 * 1024
73
73
 
74
74
  # You can provide an optional block of code to run, when a worker hits its memory threshold (Use this to send yourself an alert,
75
75
  # write metrics to disk etc. etc.)
76
- after_memory_threshold_reached do |pid|
76
+ after_memory_limit_reached do |pid|
77
77
  puts "Worker #{pid} has reached its memory threshold and will restart"
78
78
  end
79
79
 
@@ -102,6 +102,6 @@ oob_gc_responses_threshold 512
102
102
  log_level :info
103
103
 
104
104
  # Log Format
105
- # Set this to be either :ansi or :json. If you leave it blank Itsi will try
105
+ # Set this to be either :plain or :json. If you leave it blank Itsi will try
106
106
  # and auto-detect the format based on the TTY environment.
107
- log_format :auto
107
+ log_format :plain
@@ -74,7 +74,6 @@ module Itsi
74
74
 
75
75
  break
76
76
  end
77
-
78
77
  # The next non-whitespace, non-comma character should be the start of an object.
79
78
  return nil unless char == "{"
80
79
 
@@ -90,6 +89,7 @@ module Itsi
90
89
 
91
90
  buffer << ch
92
91
 
92
+
93
93
  if in_string
94
94
  if escape
95
95
  escape = false
@@ -23,13 +23,14 @@ module Itsi
23
23
  def initialize(service)
24
24
  @service = service
25
25
  @service_class = service.class
26
- @service_class.rpc_descs.transform_keys! do |k|
26
+ @rpc_descs = @service_class.rpc_descs.transform_keys do |k|
27
27
  k.to_s.gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase.to_sym
28
28
  end
29
29
  end
30
30
 
31
31
  def handle_request(active_call)
32
- unless (active_call.rpc_desc = service_class.rpc_descs[active_call.method_name])
32
+ puts "Handling active call"
33
+ unless (active_call.rpc_desc = @rpc_descs[active_call.method_name])
33
34
  active_call.stream.write("\n")
34
35
  active_call.send_status(13, "Method not found")
35
36
  active_call.close
@@ -85,15 +86,21 @@ module Itsi
85
86
 
86
87
  def handle_server_streaming(active_call)
87
88
  message = active_call.remote_read
88
- service.send(active_call.method_name, message, active_call) do |response|
89
+ result = service.send(active_call.method_name, message, active_call) do |response|
89
90
  active_call.remote_send(response)
90
91
  end
92
+ if result.kind_of?(Enumerator)
93
+ result.each { |response| active_call.remote_send(response) }
94
+ end
91
95
  end
92
96
 
93
97
  def handle_bidi_streaming(active_call)
94
- service.send(active_call.method_name, active_call.each_remote_read, active_call) do |response|
98
+ result = service.send(active_call.method_name, active_call.each_remote_read, active_call) do |response|
95
99
  active_call.remote_send(response)
96
100
  end
101
+ if result.kind_of?(Enumerator)
102
+ result.each { |response| active_call.remote_send(response) }
103
+ end
97
104
  end
98
105
  end
99
106
  end
@@ -1,17 +1,17 @@
1
1
  return unless defined?(::Rackup::Handler) || defined?(Rack::Handler)
2
2
 
3
+
3
4
  module Rack
4
5
  module Handler
5
6
  module Itsi
6
7
  def self.run(app, options = {})
7
8
  host = options.fetch(:host, "127.0.0.1")
8
9
  port = options.fetch(:Port, 3001)
9
- Itsi::Server.start(
10
+ ::Itsi::Server.start(
10
11
  {
11
12
  app: app,
12
13
  binds: ["http://#{host}:#{port}"]
13
- },
14
- Itsi::Server::Config.config_file_path
14
+ }
15
15
  )
16
16
  end
17
17
  end
@@ -4,13 +4,63 @@ module Itsi
4
4
 
5
5
  require "set"
6
6
  require "strscan"
7
-
7
+ require "debug"
8
8
  def format_mw(mw)
9
- case mw["type"]
9
+ mw_name, mw_args = mw
10
+ case mw_name
10
11
  when "app"
11
- "app #{mw["parameters"]["app_proc"].inspect.split(" ")[1]}"
12
+ "\e[33mapp\e[0m(#{mw_args['app_proc'].inspect.split(' ')[1][0...-1]})"
13
+ when "log_requests"
14
+ if mw_args['before'] && mw_args['after']
15
+ "\e[33mlog_requests\e[0m(before: #{mw_args['before']['format'][0..6]}..., after: #{mw_args['after']['format'][0..6]}...)"
16
+ elsif mw_args['before']
17
+ "\e[33mlog_requests\e[0m(before: #{mw_args['before']['format'][0..6]}...)"
18
+ elsif mw_args['after']
19
+ "\e[33mlog_requests\e[0m(before: nil, after: #{mw_args['after']['format'][0..6]}...)"
20
+ end
21
+ when "compress"
22
+ "\e[33mcompress\e[0m(#{mw_args['algorithms'].join(' ')}, #{mw_args['mime_types']})"
23
+ when "cors"
24
+ "\e[33mcors\e[0m(#{mw_args['allow_origins'].join(' ')}, #{mw_args['allow_methods'].join(' ')})"
25
+ when "etag"
26
+ "\e[33metag\e[0m(#{mw_args['type']}/#{mw_args['algorithm']}, #{mw_args['handle_if_none_match'] ? 'if_none_match' : ''})"
27
+ when "cache_control"
28
+ "\e[33mcache_control\e[0m(max_age: #{mw_args['max_age']}, #{mw_args.select{|_,v| v == true }.keys.join(", ")})"
29
+ when "redirect"
30
+ "\e[33mredirect\e[0m(to: #{mw_args['to']}, type: #{mw_args['type']})"
31
+ when "static_assets"
32
+ "\e[33mstatic_assets\e[0m(path: #{mw_args['root_dir']})"
33
+ when "auth_api_key"
34
+ "\e[33mauth_api_key\e[0m(keys: #{mw_args['valid_keys'].keys}#{mw_args['credentials_file'] ? ", credentials_file: #{mw_args['credentials_file']}" : ""})"
35
+ when "auth_basic"
36
+ "\e[33mbasic_auth\e[0m(keys: #{mw_args['realm']}#{mw_args['credentials_file'] ? ", credentials_file: #{mw_args['credentials_file']}" : ""})"
37
+ when "auth_jwt"
38
+ "\e[33mjwt_auth\e[0m(#{mw_args['verifiers'].keys.join(",")})"
39
+ when "rate_limit"
40
+ key = mw_args['key'].kind_of?(Hash) ? mw_args['key']["parameter"] : mw_args['key']
41
+ "\e[33mrate_limit\e[0m(rps: #{mw_args['requests']}/#{mw_args['seconds']}, key: #{key})"
42
+ when "allow_list"
43
+ "\e[33mallow_list\e[0m(patterns: #{mw_args['allowed_patterns'].join(", ")})"
44
+ when "deny_list"
45
+ "\e[33mdeny_list\e[0m(patterns: #{mw_args['denied_patterns'].join(", ")})"
46
+ when "csp"
47
+ "\e[33mcsp\e[0m(#{mw_args['policy'].map{|k,v| "#{k}: #{v.join(",")}"}.join(", ")})"
48
+ when "intrusion_protection"
49
+ [mw_args].flatten.map do |mw_args|
50
+ "\e[33mintrusion_protection\e[0m(banned_url_patterns: #{mw_args['banned_url_patterns']&.length}, banned_header_patterns: #{mw_args['banned_header_patterns']&.keys&.join(", ")}, #{mw_args['banned_time_seconds']}s)"
51
+ end.join("\n")
52
+ when "request_headers"
53
+ [mw_args].flatten.map do |mw_args|
54
+ "\e[33mrequest_headers\e[0m(added: #{mw_args["additions"].keys}, removed: #{mw_args["removals"]})"
55
+ end.join("\n")
56
+ when "response_headers"
57
+ [mw_args].flatten.map do |mw_args|
58
+ "\e[33mresponse_headers\e[0m(added: #{mw_args["additions"].keys}, removed: #{mw_args["removals"]})"
59
+ end.join("\n")
60
+ when "static_response"
61
+ "\e[response_headers\e[0m(#{mw_args["code"]} body: #{mw_args["body"][0..10]})"
12
62
  else
13
- mw["type"]
63
+ "\e[33m#{mw.first}\e[0m"
14
64
  end
15
65
  end
16
66
 
@@ -21,12 +71,12 @@ module Itsi
21
71
  end.compact
22
72
  filter_str = filters.any? ? filters.join(", ") : "(none)"
23
73
 
24
- middlewares = stack["middleware"]
74
+ middlewares = stack["middleware"].to_a
25
75
 
26
76
  puts "─" * 76
27
- puts "Route: #{route_str}"
28
- puts "Conditions: #{filter_str}"
29
- puts "Middleware: • #{format_mw(middlewares.first)}"
77
+ puts "\e[32mRoute:\e[0m \e[33m#{route_str}\e[0m"
78
+ puts "\e[32mConditions:\e[0m \e[34m#{filter_str}\e[0m"
79
+ puts "\e[32mMiddleware:\e[0m • #{format_mw(middlewares.first)}"
30
80
  middlewares[1..].each do |mw|
31
81
  puts " • #{format_mw(mw)}"
32
82
  end
@@ -19,7 +19,7 @@ module Itsi
19
19
  end
20
20
 
21
21
  def server_running?
22
- Itsi::Server.respond_to?(:running) && Itsi::Server.running?
22
+ Itsi::Server.respond_to?(:running?) && Itsi::Server.running?
23
23
  end
24
24
  end
25
25
  end
@@ -23,21 +23,21 @@ module Itsi
23
23
  case v
24
24
  when true, 'true', '1', 1 then true
25
25
  when false, 'false', '0', 0 then false
26
- else raise "Cannot cast #{v.inspect} to Boolean"
26
+ else raise ValidationError.new("Cannot cast #{v.inspect} to Boolean")
27
27
  end
28
28
  },
29
29
  FalseClass => ->(v){
30
30
  case v
31
31
  when true, 'true', '1', 1 then true
32
32
  when false, 'false', '0', 0 then false
33
- else raise "Cannot cast #{v.inspect} to Boolean"
33
+ else raise ValidationError.new("Cannot cast #{v.inspect} to Boolean")
34
34
  end
35
35
  },
36
36
  :Boolean => ->(v){
37
37
  case v
38
38
  when true, 'true', '1', 1 then true
39
39
  when false, 'false', '0', 0 then false
40
- else raise "Cannot cast #{v.inspect} to Boolean"
40
+ else raise ValidationError.new("Cannot cast #{v.inspect} to Boolean")
41
41
  end
42
42
  },
43
43
  Date => ->(v){ Date.parse(v.to_s) },
@@ -45,17 +45,6 @@ module Itsi
45
45
  DateTime => ->(v){ DateTime.parse(v.to_s) }
46
46
  }.compare_by_identity
47
47
 
48
- # Extracts the expected type and required flag from a schema definition.
49
- def extract_schema(schema_def)
50
- if schema_def.is_a?(Hash) && schema_def.key?(:_type)
51
- expected_type = schema_def[:_type]
52
- required = schema_def.fetch(:required, true)
53
- [expected_type, required]
54
- else
55
- [schema_def, true]
56
- end
57
- end
58
-
59
48
  # Preprocess the schema into fixed keys (as symbols) and regex keys.
60
49
  # Memoizes the result based on the schema.
61
50
  def processed_schema(schema)
@@ -63,8 +52,9 @@ module Itsi
63
52
  @@schema_cache[schema] ||= begin
64
53
  fixed = {}
65
54
  regex = []
55
+ required_params = schema[:_required] || []
66
56
  schema.each do |k, schema_def|
67
- expected_type, required = extract_schema(schema_def)
57
+ expected_type, required = schema_def, required_params.include?(k)
68
58
  if k.is_a?(Regexp)
69
59
  regex << [k, [expected_type, required]]
70
60
  else
@@ -179,16 +169,22 @@ module Itsi
179
169
  end
180
170
 
181
171
  # Process regex keys (only string keys not already handled as fixed keys).
182
- params.keys.select { |k| k.is_a?(String) }.each do |key|
172
+ params.keys.each do |key|
173
+ if key == :_required
174
+ params.delete(key)
175
+ next
176
+ end
183
177
  next if fixed_schema.has_key?(key.to_sym) || fixed_schema.has_key?(key)
184
- regex_schema.each do |regex, (expected_type, _required)|
178
+ unless regex_schema.find do |regex, (expected_type, _required)|
185
179
  if regex.match(key)
186
180
  new_path = path + [key]
187
181
  err = cast_value!(params, key, expected_type, new_path)
188
182
  errors << err if err
189
- break # only use the first matching regex
183
+ true # only use the first matching regex
190
184
  end
191
185
  end
186
+ params.delete(key)
187
+ end
192
188
  end
193
189
 
194
190
  raise ValidationError.new(errors) unless errors.empty?
@@ -6,17 +6,18 @@ module Itsi
6
6
 
7
7
 
8
8
  def self.extract_expr_from_source_location(proc)
9
- source_lines = IO.readlines(proc.source_location.first)
9
+ source_location = proc.source_location
10
+ source_lines = IO.readlines(source_location.first)
10
11
 
11
12
  proc_line = source_location.last - 1
12
13
  first_line = source_lines[proc_line]
13
14
 
14
- until first_line =~ /(?:lambda|proc|->|def|.*?do\s*\|)/ || proc_line.zero?
15
+ until first_line =~ /(?:lambda|proc|->|def|.*?do\s*|.*?\{.*?\|)/ || proc_line.zero?
15
16
  proc_line -= 1
16
17
  first_line = source_lines[proc_line]
17
18
  end
18
19
  lines = source_lines[proc_line..]
19
- lines[0] = lines[0][/(?:lambda|proc|->|def|.*?do\s*\|).*/]
20
+ lines[0] = lines[0][/(?:lambda|proc|->|def|.*?do\s+|.*?\{.*?\|).*/]
20
21
  src_str = lines.first << "\n"
21
22
  intermediate = Prism.parse(src_str)
22
23
 
@@ -47,7 +48,7 @@ module Itsi
47
48
 
48
49
  [requireds.length, keywords]
49
50
  rescue
50
- [ proc.parameters.first&.length || 0, {}]
51
+ [ proc.parameters.select{|p| p == :req }&.length || 0, {}]
51
52
  end
52
53
  end
53
54
  end
@@ -5,10 +5,18 @@ module Itsi
5
5
  class Server
6
6
  module TypedHandlers
7
7
  def self.handler_for(proc, input_schema)
8
- input_schema = proc.binding.eval(input_schema) if input_schema
9
- lambda do |req|
10
- req.params(input_schema) do |params|
11
- proc.call(req, params: params)
8
+ if input_schema
9
+ input_schema = proc.binding.eval(input_schema)
10
+ lambda do |req|
11
+ req.params(input_schema) do |params|
12
+ proc.call(req, params: params)
13
+ end
14
+ end
15
+ else
16
+ lambda do |req|
17
+ req.params do |params|
18
+ proc.call(req, params)
19
+ end
12
20
  end
13
21
  end
14
22
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Server
5
- VERSION = "0.1.20"
5
+ VERSION = "0.2.2"
6
6
  end
7
7
  end