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
@@ -1,5 +1,7 @@
1
- require 'date'
2
- require 'time'
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "time"
3
5
 
4
6
  module Itsi
5
7
  class Server
@@ -10,18 +12,19 @@ module Itsi
10
12
 
11
13
  def self.new(defaults = nil, &defaults_blk)
12
14
  defaults = TypedStruct.module_eval(&defaults_blk) if defaults_blk
13
- unless defaults.is_a?(Hash)
14
- return defaults
15
- end
15
+ return defaults unless defaults.is_a?(Hash)
16
+
16
17
  defaults.transform_values! { _1.is_a?(Validation) ? _1.default(nil) : _1 }
17
18
  Struct.new(*defaults.keys, keyword_init: true) do
18
19
  define_method(:initialize) do |*input, validate: true, **raw_input|
19
- raw_input.transform_keys!{|k| k.to_s.downcase.to_sym }
20
- raw_input.merge!(input.pop) if input.last.is_a?(Hash)
20
+ raise "─ Invalid input to #{self}: #{input.last}" if input.last && !input.last.is_a?(Hash)
21
+
22
+ raw_input.transform_keys! { |k| k.to_s.downcase.to_sym }
23
+ raw_input.merge!(input.pop.transform_keys! { |k| k.to_s.downcase.to_sym }) if input.last.is_a?(Hash)
21
24
 
22
25
  excess_keys = raw_input.keys - defaults.keys
23
26
 
24
- raise "Unsupported keys #{excess_keys}" if excess_keys.any?
27
+ raise "Unsupported keys #{excess_keys}" if excess_keys.any?
25
28
 
26
29
  initial_values = defaults.each_with_object({}) do |(k, default_config), inputs|
27
30
  value = raw_input.key?(k) ? raw_input[k] : default_config[VALUE].dup
@@ -30,7 +33,7 @@ module Itsi
30
33
  begin
31
34
  inputs[k] = default_config[VALIDATE].validate!(value)
32
35
  rescue StandardError => e
33
- raise ArgumentError, e.message
36
+ raise ArgumentError, "─ #{k}: #{e.message}"
34
37
  end
35
38
  end
36
39
 
@@ -97,11 +100,9 @@ module Itsi
97
100
  "#{@name}"
98
101
  end
99
102
 
100
- def +(other)
103
+ def &(other)
101
104
  tail = self
102
- while tail.next
103
- tail = tail.next
104
- end
105
+ tail = tail.next while tail.next
105
106
  tail.next = other
106
107
  self
107
108
  end
@@ -115,40 +116,51 @@ module Itsi
115
116
  @validations.each do |validation|
116
117
  case validation
117
118
  when Proc
118
- unless validation.call(value)
119
- raise ArgumentError,
120
- "─ `#{@name}` validation failed. Invalid value: #{value.inspect}"
121
- end
119
+ validation.call(value)
122
120
  when Array
123
121
  unless !value || validation.include?(value)
124
122
  raise ArgumentError,
125
- "─ `#{@name}` validation failed. Invalid #{validation} value: #{value.inspect}"
123
+ "─ `#{@name}` validation failed. Invalid #{validation} value: #{value.inspect}"
126
124
  end
127
125
  when Range
128
126
  unless !value || validation.include?(value)
129
127
  raise ArgumentError,
130
- "─ `#{@name}` validation failed. Invalid #{validation} value: #{value.inspect}"
128
+ "─ `#{@name}` validation failed. Invalid #{validation} value: #{value.inspect}"
131
129
  end
132
130
  when Regexp
133
131
  unless !value || validation.match?(value)
134
132
  raise ArgumentError,
135
- "─ `#{@name}` validation failed. Invalid #{validation} value: #{value.inspect}"
133
+ "─ `#{@name}` validation failed. Invalid #{validation} value: #{value.inspect}"
136
134
  end
135
+ when Validation
136
+ validation.validate!(value)
137
137
  when Class
138
-
139
138
  if value && !value.is_a?(validation)
140
139
  begin
141
140
  value = \
142
141
  if validation.eql?(Time) then Time.parse(value.to_s)
143
142
  elsif validation.eql?(::Date) then Date.parse(value.to_s)
144
143
  elsif validation.eql?(Float) then Float(value)
145
- elsif validation.eql?(String) then value.to_s
146
- elsif validation.eql?(Symbol) then value.is_a?(Symbol) ? value : value.to_s.to_sym
147
- else validation.new(value)
144
+ elsif validation.eql?(Integer) then Integer(value)
145
+ elsif validation.eql?(Proc)
146
+ raise ArgumentError, "Invalid #{validation} value: #{value.inspect}" unless value.is_a?(Proc)
147
+ elsif validation.eql?(String) || validation.eql?(Symbol)
148
+ unless value.is_a?(String) || value.is_a?(Symbol)
149
+ raise ArgumentError,
150
+ "Invalid #{validation} value: #{value.inspect}"
151
+ end
152
+
153
+ if validation.eql?(String)
154
+ value.to_s
155
+ elsif validation.eql?(Symbol)
156
+ value.to_s.to_sym
157
+ end
158
+ else
159
+ validation.new(value)
148
160
  end
149
161
  rescue StandardError => e
150
162
  raise ArgumentError,
151
- "─ `#{@name}` Validation Failed. Invalid #{validation.to_s.split("::").last} value: #{value.inspect}. Failure reason: \n └#{e.message}"
163
+ "─ `#{@name}` Validation Failed. Invalid #{validation.to_s.split("::").last} value: #{value.inspect}. Failure reason: \n └─ #{e.message}"
152
164
  end
153
165
  end
154
166
  end
@@ -164,23 +176,47 @@ module Itsi
164
176
  {
165
177
  Bool: Validation.new(:Bool, [[true, false]]),
166
178
  Required: Validation.new(:Required, ->(value) { !value.nil? }),
167
- Or: ->(validation_a, validation_b){
168
- Validation.new(:Or, ->(v){
169
- validation_a.validate!(v) || validation_b.validate!(v)
179
+ Or: lambda { |*validations|
180
+ Validation.new(:Or, lambda { |v|
181
+ return true if v.nil?
182
+
183
+ errs = []
184
+ validations.each do |validation|
185
+ v = validation.validate!(v)
186
+ return v
187
+ rescue StandardError => e
188
+ errs << e.message
189
+ end
190
+ raise StandardError.new("─ Validation failed (None match:) \n └#{errs.join("\n └")}")
170
191
  })
171
192
  },
172
- Range: ->(input_range) {
193
+ Range: lambda { |input_range|
173
194
  Validation.new(:Range, [input_range])
174
195
  },
175
196
  Length: lambda { |input_length|
176
197
  Validation.new(:Length, ->(value) { input_length === value.length })
177
198
  },
199
+ Hash: lambda { |key_type, value_type|
200
+ Validation.new(:Hash, lambda { |hash|
201
+ return true if hash.nil?
202
+ raise StandardError.new("Expected hash got #{hash.class}") unless hash.is_a?(Hash)
203
+
204
+ hash.map do |k, v|
205
+ [
206
+ key_type.validate!(k),
207
+ value_type.validate!(v)
208
+ ]
209
+ end.to_h
210
+ })
211
+ },
178
212
  Type: ->(input_type) { Validation.new(:Type, input_type) },
179
- Enum: ->(allowed_values) { Validation.new(:Enum, [allowed_values.map{|v| v.kind_of?(Symbol) ? v.to_s : v}]) },
213
+ Enum: ->(allowed_values) { Validation.new(:Enum, [allowed_values.map { |v| v.is_a?(Symbol) ? v.to_s : v }]) },
180
214
  Array: lambda { |*value_validations|
181
215
  Validation.new(:Array, [::Array, lambda { |value|
182
216
  return true unless value
183
217
 
218
+ raise StandardError.new("Expected Array got #{value.class}") unless value.is_a?(Array)
219
+
184
220
  value&.map! do |v|
185
221
  value_validations.all? { v = _1.validate!(v) }
186
222
  v
@@ -193,8 +229,8 @@ module Itsi
193
229
  define_singleton_method(name.to_s.downcase, &factory)
194
230
  else
195
231
  const_set(name, factory)
196
- define_singleton_method(name, -> { factory })
197
- define_singleton_method(name.to_s.downcase, -> { factory })
232
+ define_singleton_method(name, -> { factory.dup })
233
+ define_singleton_method(name.to_s.downcase, -> { factory.dup })
198
234
  end
199
235
  end
200
236
  end
@@ -41,16 +41,18 @@ 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
- rate_limit key: 'address', store_config: 'in_memory', requests: 2, seconds: 5
46
- 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
48
- log_requests before: { level: "INFO", format: "[{request_id}] {method} {path_and_query} - {addr} " }, after: { level: "INFO", format: "[{request_id}] └─ {status} in {response_time}" }
44
+ rate_limit key: "address", store_config: "in_memory", requests: 2, seconds: 5
45
+ etag type: "strong", algorithm: "md5", min_body_size: 1024 * 1024
46
+ compress min_size: 1024 * 1024, level: "fastest", algorithms: %w[zstd gzip br deflate],
47
+ mime_types: %w[all], compress_streams: true
48
+ log_requests before: { level: "INFO", format: "[{request_id}] {method} {path_and_query} - {addr} " },
49
+ after: { level: "INFO",
50
+ format: "[{request_id}] └─ {status} in {response_time}" }
49
51
  static_assets \
50
52
  relative_path: true,
51
53
  allowed_extensions: [],
52
- root_dir: '.',
53
- not_found_behavior: {error: 'not_found'},
54
+ root_dir: ".",
55
+ not_found_behavior: { error: "not_found" },
54
56
  auto_index: true,
55
57
  try_html_extension: true,
56
58
  max_file_size_in_memory: 1024 * 1024, # 1MB
@@ -58,7 +60,7 @@ module Itsi
58
60
  file_check_interval: 1,
59
61
  serve_hidden_files: false,
60
62
  headers: {
61
- 'X-Content-Type-Options' => 'nosniff'
63
+ "X-Content-Type-Options" => "nosniff"
62
64
  }
63
65
  end
64
66
  end
@@ -70,7 +72,7 @@ module Itsi
70
72
  rackup_file args.fetch(:rackup_file, "./config.ru")
71
73
  end
72
74
  else
73
- DSL.evaluate{}
75
+ DSL.evaluate {}
74
76
  end
75
77
 
76
78
  itsifile_config.transform_keys!(&:to_sym)
@@ -82,78 +84,84 @@ module Itsi
82
84
  case preload
83
85
  # If we preload everything, then we'll load middleware and default rack app ahead of time
84
86
  when true
85
- preloaded_middleware = middleware_loader.call
86
- middleware_loader = -> { preloaded_middleware }
87
+ begin
88
+ Itsi.log_debug("Preloading middleware and default rack app")
89
+ preloaded_middleware = middleware_loader.call
90
+ middleware_loader = -> { preloaded_middleware }
91
+ rescue Exception => e # rubocop:disable Lint/RescueException
92
+ errors << [e, e.backtrace[0]]
93
+ end
87
94
  # If we're just preloading a specific gem group, we'll do that here too
88
95
  when Symbol
96
+ Itsi.log_debug("Preloading gem group #{preload}")
89
97
  Bundler.require(preload)
90
98
  end
91
99
 
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),
100
+ if itsifile_config[:daemonize] && !@daemonized
101
+ @daemonized = true
102
+ Itsi.log_info("Itsi is running in the background. Writing pid to #{Itsi::Server::Config.pid_file_path}")
103
+ Itsi.log_info("To stop Itsi, run 'itsi stop' from this directory.")
104
+ Process.daemon(true, false)
105
+ Server.write_pid
106
+ end
127
107
 
128
- errors.flat_map do |(error, message)|
129
- location = message[/(.*?)\:in/,1]
130
- file, lineno = location.split(":")
131
- lineno = lineno.to_i
132
- err_message = error.kind_of?(NoMethodError) ? error.detailed_message : error.message
133
- file_lines = IO.readlines(file)
134
- info_lines = if error.kind_of?(SyntaxError)
135
- []
136
- else
137
- ([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
144
- end.flatten
145
- end
146
- [
147
- err_message,
148
- " --> #{File.expand_path(file)}:#{lineno}",
149
- *info_lines
150
- ]
151
- end
152
- ]
108
+ srv_config = {
109
+ workers: args.fetch(:workers) { itsifile_config.fetch(:workers, 1) },
110
+ worker_memory_limit: args.fetch(:worker_memory_limit) { itsifile_config.fetch(:worker_memory_limit, nil) },
111
+ silence: args.fetch(:silence) { itsifile_config.fetch(:silence, false) },
112
+ shutdown_timeout: args.fetch(:shutdown_timeout) { itsifile_config.fetch(:shutdown_timeout, 5) },
113
+ hooks: if args[:hooks] && itsifile_config[:hooks]
114
+ args[:hooks].merge(itsifile_config[:hooks])
115
+ else
116
+ itsifile_config.fetch(
117
+ :hooks, args[:hooks]
118
+ )
119
+ end,
120
+ preload: !!preload,
121
+ request_timeout: itsifile_config.fetch(:request_timeout, nil),
122
+ header_read_timeout: args.fetch(:header_read_timeout) { itsifile_config.fetch(:header_read_timeout, nil) },
123
+ notify_watchers: itsifile_config.fetch(:notify_watchers, nil),
124
+ threads: args.fetch(:threads) { itsifile_config.fetch(:threads, 1) },
125
+ scheduler_threads: args.fetch(:scheduler_threads) { itsifile_config.fetch(:scheduler_threads, nil) },
126
+ streamable_body: args.fetch(:streamable_body) { itsifile_config.fetch(:streamable_body, false) },
127
+ multithreaded_reactor: args.fetch(:multithreaded_reactor) do
128
+ itsifile_config.fetch(:multithreaded_reactor, nil)
129
+ end,
130
+ pin_worker_cores: args.fetch(:pin_worker_cores) { itsifile_config.fetch(:pin_worker_cores, true) },
131
+ scheduler_class: args.fetch(:scheduler_class) { itsifile_config.fetch(:scheduler_class, nil) },
132
+ oob_gc_responses_threshold: args.fetch(:oob_gc_responses_threshold) do
133
+ itsifile_config.fetch(:oob_gc_responses_threshold, nil)
134
+ end,
135
+ log_level: args.fetch(:log_level) { itsifile_config.fetch(:log_level, nil) },
136
+ log_format: args.fetch(:log_format) { itsifile_config.fetch(:log_format, nil) },
137
+ log_target: args.fetch(:log_target) { itsifile_config.fetch(:log_target, nil) },
138
+ log_target_filters: args.fetch(:log_target_filters) { itsifile_config.fetch(:log_target_filters, nil) },
139
+ binds: args.fetch(:binds) { itsifile_config.fetch(:binds, ["http://0.0.0.0:3000"]) },
140
+ middleware_loader: middleware_loader,
141
+ listeners: args.fetch(:listeners) { nil },
142
+ reuse_address: itsifile_config.fetch(:reuse_address, true),
143
+ reuse_port: itsifile_config.fetch(:reuse_port, true),
144
+ listen_backlog: itsifile_config.fetch(:listen_backlog, 1024),
145
+ nodelay: itsifile_config.fetch(:nodelay, true),
146
+ recv_buffer_size: itsifile_config.fetch(:recv_buffer_size, 262_144)
147
+ }.transform_keys(&:to_s)
148
+
149
+ [srv_config, errors_to_error_lines(errors)]
150
+ rescue StandardError
151
+ Itsi.log_error e.message
152
+ puts e.backtrace
153
153
  end
154
154
 
155
155
  def self.test!(cli_params)
156
- _, errors = build_config(cli_params, Itsi::Server::Config.config_file_path(cli_params[:config_file]))
156
+ config, errors = build_config(cli_params, Itsi::Server::Config.config_file_path(cli_params[:config_file]))
157
+ unless errors.any?
158
+ begin
159
+ config["middleware_loader"][]
160
+ rescue Exception => e # rubocop:disable Lint/RescueException
161
+ errors = [e]
162
+ end
163
+ end
164
+
157
165
  if errors.any?
158
166
  Itsi.log_error("Config file is invalid")
159
167
  puts errors
@@ -162,6 +170,42 @@ module Itsi
162
170
  end
163
171
  end
164
172
 
173
+ def self.errors_to_error_lines(errors)
174
+ return unless errors
175
+
176
+ errors.flat_map do |(error, message)|
177
+ location = message[/(.*?):in/, 1]
178
+ file, lineno = location.split(":")
179
+ lineno = lineno.to_i
180
+ err_message = error.is_a?(NoMethodError) ? error.detailed_message : error.message
181
+ file_lines = IO.readlines(file)
182
+ info_lines = \
183
+ if error.is_a?(SyntaxError)
184
+ []
185
+ else
186
+
187
+ ([lineno - 2, 0].max...[file_lines.length, lineno.succ.succ].min).map do |currline|
188
+ if currline == lineno - 1
189
+ line = file_lines[currline][0...-1]
190
+ padding = line[/^\s+/]&.length || 0
191
+
192
+ [
193
+ " \e[31m#{currline.succ.to_s.rjust(3)} | #{line}\e[0m",
194
+ " | #{" " * padding}\e[33m^^^\e[0m "
195
+ ]
196
+ else
197
+ " #{currline.succ.to_s.rjust(3)} | #{file_lines[currline][0...-1]}"
198
+ end
199
+ end.flatten
200
+ end
201
+ [
202
+ err_message,
203
+ " --> #{File.expand_path(file)}:#{lineno}",
204
+ *info_lines
205
+ ]
206
+ end
207
+ end
208
+
165
209
  # Reloads the entire process
166
210
  # using exec, passing in any active file descriptors
167
211
  # and previous invocation arguments
@@ -181,7 +225,7 @@ module Itsi
181
225
  elsif File.exist?("config/#{ITSI_DEFAULT_CONFIG_FILE}")
182
226
  "config/#{ITSI_DEFAULT_CONFIG_FILE}"
183
227
  end
184
- # Options simply pass through unless we've specified a config file
228
+ # Options pass through unless we've specified a config file
185
229
  return unless File.exist?(config_file_path.to_s)
186
230
 
187
231
  config_file_path
@@ -204,56 +248,56 @@ module Itsi
204
248
 
205
249
  default_config = IO.read("#{__dir__}/default_config/Itsi.rb")
206
250
 
207
- if File.exist?("./config.ru")
208
- default_config << <<~RUBY
209
- # You can mount several Ruby apps as either
210
- # 1. rackup files
211
- # 2. inline rack apps
212
- # 3. inline Ruby endpoints
213
- #
214
- # 1. rackup_file
215
- rackup_file "./config.ru"
216
- #
217
- # 2. inline rack app
218
- # require 'rack'
219
- # run(Rack::Builder.app do
220
- # use Rack::CommonLogger
221
- # run ->(env) { [200, { 'content-type' => 'text/plain' }, ['OK']] }
222
- # end)
223
- #
224
- # 3. Endpoints
225
- # endpoint "/" do |req|
226
- # req.ok "Hello from Itsi"
227
- # end
228
- RUBY
229
- else
230
- default_config << <<~RUBY
231
- # You can mount several Ruby apps as either
232
- # 1. rackup files
233
- # 2. inline rack apps
234
- # 3. inline Ruby endpoints
235
- #
236
- # 1. rackup_file
237
- # Use `rackup_file` to specify the Rack app file name.
238
- #
239
- # 2. inline rack app
240
- # require 'rack'
241
- # run(Rack::Builder.app do
242
- # use Rack::CommonLogger
243
- # run ->(env) { [200, { 'content-type' => 'text/plain' }, ['OK']] }
244
- # end)
245
- #
246
- # 3. Endpoint
247
- endpoint "/" do |req|
248
- req.ok "Hello from Itsi"
249
- end
250
- RUBY
251
- end
251
+ default_config << \
252
+ if File.exist?("./config.ru")
253
+ <<~RUBY
254
+ # You can mount several Ruby apps as either
255
+ # 1. rackup files
256
+ # 2. inline rack apps
257
+ # 3. inline Ruby endpoints
258
+ #
259
+ # 1. rackup_file
260
+ rackup_file "./config.ru"
261
+ #
262
+ # 2. inline rack app
263
+ # require 'rack'
264
+ # run(Rack::Builder.app do
265
+ # use Rack::CommonLogger
266
+ # run ->(env) { [200, { 'content-type' => 'text/plain' }, ['OK']] }
267
+ # end)
268
+ #
269
+ # 3. Endpoints
270
+ # endpoint "/" do |req|
271
+ # req.ok "Hello from Itsi"
272
+ # end
273
+ RUBY
274
+ else
275
+ <<~RUBY
276
+ # You can mount several Ruby apps as either
277
+ # 1. rackup files
278
+ # 2. inline rack apps
279
+ # 3. inline Ruby endpoints
280
+ #
281
+ # 1. rackup_file
282
+ # Use `rackup_file` to specify the Rack app file name.
283
+ #
284
+ # 2. inline rack app
285
+ # require 'rack'
286
+ # run(Rack::Builder.app do
287
+ # use Rack::CommonLogger
288
+ # run ->(env) { [200, { 'content-type' => 'text/plain' }, ['OK']] }
289
+ # end)
290
+ #
291
+ # 3. Endpoint
292
+ endpoint "/" do |req|
293
+ req.ok "Hello from Itsi"
294
+ end
295
+ RUBY
296
+ end
252
297
 
253
298
  File.open(ITSI_DEFAULT_CONFIG_FILE, "w") do |file|
254
299
  file.write(default_config)
255
300
  end
256
-
257
301
  end
258
302
  end
259
303
  end
@@ -11,7 +11,7 @@ DEFAULT_APP = lambda {
11
11
  [
12
12
  { "Content-Type" => "application/json" },
13
13
  [{ "message" => "You're running on Itsi!", "rack_env" => env,
14
- "version" => Itsi::Server::VERSION }.to_json]
14
+ "version" => Itsi::Server::VERSION }.to_json]
15
15
  ]
16
16
  else
17
17
  [
@@ -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
@@ -7,11 +7,11 @@ module Itsi
7
7
  attr_accessor :rpc_desc
8
8
 
9
9
  def input_stream?
10
- @input_stream ||= @rpc_desc&.input&.is_a?(GRPC::RpcDesc::Stream) || false
10
+ @input_stream ||= @rpc_desc&.input.is_a?(GRPC::RpcDesc::Stream) || false
11
11
  end
12
12
 
13
13
  def output_stream?
14
- @output_stream ||= @rpc_desc&.output&.is_a?(GRPC::RpcDesc::Stream) || false
14
+ @output_stream ||= @rpc_desc&.output.is_a?(GRPC::RpcDesc::Stream) || false
15
15
  end
16
16
 
17
17
  def input_type
@@ -64,7 +64,7 @@ module Itsi
64
64
  return nil if first_char.nil?
65
65
 
66
66
  # Step 2: Process objects until we hit the end of the JSON stream or array.
67
- loop do
67
+ loop do # rubocop:disable Lint/UnreachableLoop,Metrics/BlockLength
68
68
  # Skip any whitespace or commas preceding an object.
69
69
  char = nil
70
70
  loop do
@@ -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
 
@@ -120,7 +119,7 @@ module Itsi
120
119
  def remote_read
121
120
  if content_type == "application/json"
122
121
  if input_stream?
123
- if next_item = parse_from_json_stream(reader)
122
+ if (next_item = parse_from_json_stream(reader))
124
123
  input_type.decode_json(next_item)
125
124
  end
126
125
  else
@@ -23,13 +23,13 @@ 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
+ unless (active_call.rpc_desc = @rpc_descs[active_call.method_name])
33
33
  active_call.stream.write("\n")
34
34
  active_call.send_status(13, "Method not found")
35
35
  active_call.close
@@ -85,15 +85,21 @@ module Itsi
85
85
 
86
86
  def handle_server_streaming(active_call)
87
87
  message = active_call.remote_read
88
- service.send(active_call.method_name, message, active_call) do |response|
88
+ result = service.send(active_call.method_name, message, active_call) do |response|
89
89
  active_call.remote_send(response)
90
90
  end
91
+ return unless result.is_a?(Enumerator)
92
+
93
+ result.each { |response| active_call.remote_send(response) }
91
94
  end
92
95
 
93
96
  def handle_bidi_streaming(active_call)
94
- service.send(active_call.method_name, active_call.each_remote_read, active_call) do |response|
97
+ result = service.send(active_call.method_name, active_call.each_remote_read, active_call) do |response|
95
98
  active_call.remote_send(response)
96
99
  end
100
+ return unless result.is_a?(Enumerator)
101
+
102
+ result.each { |response| active_call.remote_send(response) }
97
103
  end
98
104
  end
99
105
  end
@@ -6,12 +6,11 @@ module Rack
6
6
  def self.run(app, options = {})
7
7
  host = options.fetch(:host, "127.0.0.1")
8
8
  port = options.fetch(:Port, 3001)
9
- Itsi::Server.start(
9
+ ::Itsi::Server.start(
10
10
  {
11
11
  app: app,
12
12
  binds: ["http://#{host}:#{port}"]
13
- },
14
- Itsi::Server::Config.config_file_path
13
+ }
15
14
  )
16
15
  end
17
16
  end