itsi 0.1.11 → 0.1.12

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 (293) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1535 -45
  3. data/{sandbox/itsi_itsi_file/Itsi.rb → Itsi.rb} +19 -13
  4. data/Rakefile +8 -7
  5. data/crates/itsi_error/src/lib.rs +9 -0
  6. data/crates/itsi_rb_helpers/Cargo.toml +1 -0
  7. data/crates/itsi_rb_helpers/src/heap_value.rs +18 -0
  8. data/crates/itsi_rb_helpers/src/lib.rs +34 -7
  9. data/crates/itsi_server/Cargo.toml +69 -30
  10. data/crates/itsi_server/src/lib.rs +79 -147
  11. data/crates/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  12. data/crates/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +22 -3
  13. data/crates/itsi_server/src/ruby_types/itsi_grpc_request.rs +147 -0
  14. data/crates/itsi_server/src/ruby_types/itsi_grpc_response.rs +19 -0
  15. data/crates/itsi_server/src/ruby_types/itsi_grpc_stream/mod.rs +216 -0
  16. data/{gems/server/ext/itsi_server/src/request/itsi_request.rs → crates/itsi_server/src/ruby_types/itsi_http_request.rs} +101 -117
  17. data/crates/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +72 -41
  18. data/crates/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
  19. data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +355 -0
  20. data/crates/itsi_server/src/ruby_types/itsi_server.rs +82 -0
  21. data/crates/itsi_server/src/ruby_types/mod.rs +55 -0
  22. data/crates/itsi_server/src/server/bind.rs +13 -5
  23. data/crates/itsi_server/src/server/byte_frame.rs +32 -0
  24. data/crates/itsi_server/src/server/cache_store.rs +74 -0
  25. data/crates/itsi_server/src/server/itsi_service.rs +172 -0
  26. data/crates/itsi_server/src/server/lifecycle_event.rs +3 -0
  27. data/crates/itsi_server/src/server/listener.rs +102 -2
  28. data/crates/itsi_server/src/server/middleware_stack/middleware.rs +153 -0
  29. data/crates/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +47 -0
  30. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +58 -0
  31. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +82 -0
  32. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +321 -0
  33. data/crates/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +139 -0
  34. data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +300 -0
  35. data/crates/itsi_server/src/server/middleware_stack/middlewares/cors.rs +287 -0
  36. data/crates/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +48 -0
  37. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +127 -0
  38. data/crates/itsi_server/src/server/middleware_stack/middlewares/etag.rs +191 -0
  39. data/crates/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +72 -0
  40. data/crates/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +85 -0
  41. data/crates/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +195 -0
  42. data/crates/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  43. data/crates/itsi_server/src/server/middleware_stack/middlewares/mod.rs +82 -0
  44. data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +216 -0
  45. data/crates/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +124 -0
  46. data/crates/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
  47. data/crates/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +43 -0
  48. data/crates/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +34 -0
  49. data/crates/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +93 -0
  50. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +162 -0
  51. data/crates/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +158 -0
  52. data/crates/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
  53. data/crates/itsi_server/src/server/middleware_stack/mod.rs +315 -0
  54. data/crates/itsi_server/src/server/mod.rs +8 -1
  55. data/crates/itsi_server/src/server/process_worker.rs +38 -12
  56. data/crates/itsi_server/src/server/rate_limiter.rs +565 -0
  57. data/crates/itsi_server/src/server/request_job.rs +11 -0
  58. data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +119 -42
  59. data/crates/itsi_server/src/server/serve_strategy/mod.rs +9 -6
  60. data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +256 -111
  61. data/crates/itsi_server/src/server/signal.rs +19 -0
  62. data/crates/itsi_server/src/server/static_file_server.rs +984 -0
  63. data/crates/itsi_server/src/server/thread_worker.rs +139 -94
  64. data/crates/itsi_server/src/server/types.rs +43 -0
  65. data/crates/itsi_server/test.md +14 -0
  66. data/crates/itsi_tracing/Cargo.toml +1 -0
  67. data/crates/itsi_tracing/src/lib.rs +216 -45
  68. data/docs/.gitignore +7 -0
  69. data/docs/.gitpod.yml +15 -0
  70. data/docs/Itsi.rb +17 -0
  71. data/docs/content/_index.md +17 -0
  72. data/docs/content/about.md +6 -0
  73. data/docs/content/docs/_index.md +18 -0
  74. data/docs/content/docs/first-page.md +9 -0
  75. data/docs/content/docs/folder/_index.md +10 -0
  76. data/docs/content/docs/folder/leaf.md +7 -0
  77. data/docs/go.mod +5 -0
  78. data/docs/go.sum +2 -0
  79. data/docs/hugo.yaml +77 -0
  80. data/examples/static_assets_example.rb +83 -0
  81. data/gems/_index.md +18 -0
  82. data/gems/scheduler/CODE_OF_CONDUCT.md +7 -0
  83. data/gems/scheduler/Cargo.lock +75 -14
  84. data/gems/scheduler/README.md +5 -0
  85. data/gems/scheduler/_index.md +7 -0
  86. data/gems/scheduler/itsi-scheduler.gemspec +4 -1
  87. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  88. data/gems/scheduler/lib/itsi/scheduler.rb +2 -2
  89. data/gems/scheduler/test/test_file_io.rb +0 -1
  90. data/gems/scheduler/test/test_itsi_scheduler.rb +1 -1
  91. data/gems/server/CHANGELOG.md +5 -0
  92. data/gems/server/CODE_OF_CONDUCT.md +7 -0
  93. data/gems/server/Cargo.lock +1536 -45
  94. data/gems/server/README.md +4 -0
  95. data/gems/server/_index.md +6 -0
  96. data/gems/server/exe/itsi +33 -74
  97. data/gems/server/itsi-server.gemspec +3 -2
  98. data/gems/server/lib/itsi/{request.rb → http_request.rb} +29 -5
  99. data/gems/server/lib/itsi/http_response.rb +39 -0
  100. data/gems/server/lib/itsi/server/Itsi.rb +11 -19
  101. data/gems/server/lib/itsi/server/config/dsl.rb +506 -0
  102. data/gems/server/lib/itsi/server/config.rb +103 -8
  103. data/gems/server/lib/itsi/server/default_app/default_app.rb +38 -0
  104. data/gems/server/lib/itsi/server/grpc_interface.rb +213 -0
  105. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +8 -17
  106. data/gems/server/lib/itsi/server/rack_interface.rb +23 -4
  107. data/gems/server/lib/itsi/server/scheduler_interface.rb +1 -1
  108. data/gems/server/lib/itsi/server/scheduler_mode.rb +4 -0
  109. data/gems/server/lib/itsi/server/signal_trap.rb +7 -1
  110. data/gems/server/lib/itsi/server/version.rb +1 -1
  111. data/gems/server/lib/itsi/server.rb +74 -63
  112. data/gems/server/lib/itsi/standard_headers.rb +86 -0
  113. data/gems/server/test/helpers/test_helper.rb +12 -12
  114. data/gems/server/test/test_itsi_server.rb +2 -2
  115. data/lib/itsi/version.rb +1 -1
  116. data/sandbox/itsi_file/Gemfile +11 -0
  117. data/sandbox/itsi_file/Gemfile.lock +69 -0
  118. data/sandbox/itsi_file/Itsi.rb +276 -0
  119. data/sandbox/itsi_file/error.html +2 -0
  120. data/sandbox/itsi_file/organisations_controller.rb +20 -0
  121. data/sandbox/itsi_file/public/assets/image.png +0 -0
  122. data/sandbox/itsi_file/public/assets/index.html +1 -0
  123. data/sandbox/itsi_sandbox_hanami/Gemfile.lock +2 -2
  124. data/sandbox/itsi_sandbox_rack/Gemfile.lock +2 -2
  125. data/sandbox/itsi_sandbox_rack/config.ru +2 -15
  126. data/sandbox/itsi_sandbox_rails/.dockerignore +2 -5
  127. data/sandbox/itsi_sandbox_rails/.github/workflows/ci.yml +1 -1
  128. data/sandbox/itsi_sandbox_rails/.gitignore +2 -1
  129. data/sandbox/itsi_sandbox_rails/Dockerfile +6 -9
  130. data/sandbox/itsi_sandbox_rails/Gemfile +16 -22
  131. data/sandbox/itsi_sandbox_rails/Gemfile.lock +100 -225
  132. data/sandbox/itsi_sandbox_rails/app/assets/config/manifest.js +4 -0
  133. data/sandbox/itsi_sandbox_rails/app/assets/stylesheets/application.css +11 -6
  134. data/sandbox/itsi_sandbox_rails/app/channels/application_cable/channel.rb +4 -0
  135. data/sandbox/itsi_sandbox_rails/app/channels/application_cable/connection.rb +4 -0
  136. data/sandbox/itsi_sandbox_rails/app/controllers/live_controller.rb +7 -8
  137. data/sandbox/itsi_sandbox_rails/app/controllers/uploads_controller.rb +0 -3
  138. data/sandbox/itsi_sandbox_rails/app/views/layouts/application.html.erb +2 -7
  139. data/sandbox/itsi_sandbox_rails/bin/docker-entrypoint +3 -4
  140. data/sandbox/itsi_sandbox_rails/bin/setup +8 -5
  141. data/sandbox/itsi_sandbox_rails/config/application.rb +1 -35
  142. data/sandbox/itsi_sandbox_rails/config/cable.yml +3 -10
  143. data/sandbox/itsi_sandbox_rails/config/credentials.yml.enc +1 -1
  144. data/sandbox/itsi_sandbox_rails/config/database.yml +9 -19
  145. data/sandbox/itsi_sandbox_rails/config/environment.rb +1 -1
  146. data/sandbox/itsi_sandbox_rails/config/environments/development.rb +21 -12
  147. data/sandbox/itsi_sandbox_rails/config/environments/production.rb +49 -34
  148. data/sandbox/itsi_sandbox_rails/config/environments/test.rb +19 -5
  149. data/sandbox/itsi_sandbox_rails/config/initializers/assets.rb +5 -0
  150. data/sandbox/itsi_sandbox_rails/config/initializers/filter_parameter_logging.rb +1 -1
  151. data/sandbox/itsi_sandbox_rails/config/initializers/permissions_policy.rb +13 -0
  152. data/sandbox/itsi_sandbox_rails/config/puma.rb +2 -9
  153. data/sandbox/itsi_sandbox_rails/config.ru +0 -1
  154. data/sandbox/itsi_sandbox_rails/db/migrate/20250301041554_create_posts.rb +1 -1
  155. data/sandbox/itsi_sandbox_rails/db/schema.rb +2 -2
  156. data/sandbox/itsi_sandbox_rails/lib/assets/.keep +0 -0
  157. data/sandbox/itsi_sandbox_rails/public/404.html +66 -113
  158. data/sandbox/itsi_sandbox_rails/public/406-unsupported-browser.html +65 -113
  159. data/sandbox/itsi_sandbox_rails/public/422.html +66 -113
  160. data/sandbox/itsi_sandbox_rails/public/500.html +65 -113
  161. data/sandbox/itsi_sandbox_rails/public/icon.png +0 -0
  162. data/sandbox/itsi_sandbox_rails/public/icon.svg +2 -2
  163. data/sandbox/itsi_sandbox_rails/test/channels/application_cable/connection_test.rb +13 -0
  164. data/sandbox/itsi_sandbox_roda/Gemfile.lock +3 -10
  165. data/tasks.txt +72 -35
  166. metadata +89 -139
  167. data/crates/itsi_server/src/body_proxy/mod.rs +0 -2
  168. data/crates/itsi_server/src/request/itsi_request.rs +0 -298
  169. data/crates/itsi_server/src/request/mod.rs +0 -1
  170. data/crates/itsi_server/src/response/mod.rs +0 -1
  171. data/crates/itsi_server/src/server/itsi_server.rs +0 -288
  172. data/gems/scheduler/ext/itsi_error/Cargo.lock +0 -368
  173. data/gems/scheduler/ext/itsi_error/Cargo.toml +0 -11
  174. data/gems/scheduler/ext/itsi_error/src/from.rs +0 -68
  175. data/gems/scheduler/ext/itsi_error/src/lib.rs +0 -24
  176. data/gems/scheduler/ext/itsi_instrument_entry/Cargo.toml +0 -15
  177. data/gems/scheduler/ext/itsi_instrument_entry/src/lib.rs +0 -31
  178. data/gems/scheduler/ext/itsi_rb_helpers/Cargo.lock +0 -355
  179. data/gems/scheduler/ext/itsi_rb_helpers/Cargo.toml +0 -10
  180. data/gems/scheduler/ext/itsi_rb_helpers/src/heap_value.rs +0 -121
  181. data/gems/scheduler/ext/itsi_rb_helpers/src/lib.rs +0 -201
  182. data/gems/scheduler/ext/itsi_scheduler/Cargo.toml +0 -24
  183. data/gems/scheduler/ext/itsi_scheduler/extconf.rb +0 -6
  184. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +0 -56
  185. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +0 -44
  186. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +0 -44
  187. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler.rs +0 -308
  188. data/gems/scheduler/ext/itsi_scheduler/src/lib.rs +0 -38
  189. data/gems/scheduler/ext/itsi_server/Cargo.lock +0 -2956
  190. data/gems/scheduler/ext/itsi_server/Cargo.toml +0 -50
  191. data/gems/scheduler/ext/itsi_server/extconf.rb +0 -6
  192. data/gems/scheduler/ext/itsi_server/src/body_proxy/big_bytes.rs +0 -104
  193. data/gems/scheduler/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +0 -122
  194. data/gems/scheduler/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  195. data/gems/scheduler/ext/itsi_server/src/env.rs +0 -43
  196. data/gems/scheduler/ext/itsi_server/src/lib.rs +0 -180
  197. data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +0 -298
  198. data/gems/scheduler/ext/itsi_server/src/request/mod.rs +0 -1
  199. data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +0 -357
  200. data/gems/scheduler/ext/itsi_server/src/response/mod.rs +0 -1
  201. data/gems/scheduler/ext/itsi_server/src/server/bind.rs +0 -174
  202. data/gems/scheduler/ext/itsi_server/src/server/bind_protocol.rs +0 -37
  203. data/gems/scheduler/ext/itsi_server/src/server/io_stream.rs +0 -104
  204. data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +0 -288
  205. data/gems/scheduler/ext/itsi_server/src/server/lifecycle_event.rs +0 -9
  206. data/gems/scheduler/ext/itsi_server/src/server/listener.rs +0 -318
  207. data/gems/scheduler/ext/itsi_server/src/server/mod.rs +0 -11
  208. data/gems/scheduler/ext/itsi_server/src/server/process_worker.rs +0 -203
  209. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +0 -260
  210. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/mod.rs +0 -27
  211. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +0 -276
  212. data/gems/scheduler/ext/itsi_server/src/server/signal.rs +0 -74
  213. data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +0 -399
  214. data/gems/scheduler/ext/itsi_server/src/server/tls/locked_dir_cache.rs +0 -132
  215. data/gems/scheduler/ext/itsi_server/src/server/tls.rs +0 -265
  216. data/gems/scheduler/ext/itsi_tracing/Cargo.lock +0 -274
  217. data/gems/scheduler/ext/itsi_tracing/Cargo.toml +0 -16
  218. data/gems/scheduler/ext/itsi_tracing/src/lib.rs +0 -58
  219. data/gems/server/ext/itsi_error/Cargo.lock +0 -368
  220. data/gems/server/ext/itsi_error/Cargo.toml +0 -11
  221. data/gems/server/ext/itsi_error/src/from.rs +0 -68
  222. data/gems/server/ext/itsi_error/src/lib.rs +0 -24
  223. data/gems/server/ext/itsi_instrument_entry/Cargo.toml +0 -15
  224. data/gems/server/ext/itsi_instrument_entry/src/lib.rs +0 -31
  225. data/gems/server/ext/itsi_rb_helpers/Cargo.lock +0 -355
  226. data/gems/server/ext/itsi_rb_helpers/Cargo.toml +0 -10
  227. data/gems/server/ext/itsi_rb_helpers/src/heap_value.rs +0 -121
  228. data/gems/server/ext/itsi_rb_helpers/src/lib.rs +0 -201
  229. data/gems/server/ext/itsi_scheduler/Cargo.toml +0 -24
  230. data/gems/server/ext/itsi_scheduler/extconf.rb +0 -6
  231. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +0 -56
  232. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +0 -44
  233. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +0 -44
  234. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler.rs +0 -308
  235. data/gems/server/ext/itsi_scheduler/src/lib.rs +0 -38
  236. data/gems/server/ext/itsi_server/Cargo.lock +0 -2956
  237. data/gems/server/ext/itsi_server/Cargo.toml +0 -50
  238. data/gems/server/ext/itsi_server/extconf.rb +0 -6
  239. data/gems/server/ext/itsi_server/src/body_proxy/big_bytes.rs +0 -104
  240. data/gems/server/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +0 -122
  241. data/gems/server/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  242. data/gems/server/ext/itsi_server/src/env.rs +0 -43
  243. data/gems/server/ext/itsi_server/src/lib.rs +0 -180
  244. data/gems/server/ext/itsi_server/src/request/mod.rs +0 -1
  245. data/gems/server/ext/itsi_server/src/response/itsi_response.rs +0 -357
  246. data/gems/server/ext/itsi_server/src/response/mod.rs +0 -1
  247. data/gems/server/ext/itsi_server/src/server/bind.rs +0 -174
  248. data/gems/server/ext/itsi_server/src/server/bind_protocol.rs +0 -37
  249. data/gems/server/ext/itsi_server/src/server/io_stream.rs +0 -104
  250. data/gems/server/ext/itsi_server/src/server/itsi_server.rs +0 -288
  251. data/gems/server/ext/itsi_server/src/server/lifecycle_event.rs +0 -9
  252. data/gems/server/ext/itsi_server/src/server/listener.rs +0 -318
  253. data/gems/server/ext/itsi_server/src/server/mod.rs +0 -11
  254. data/gems/server/ext/itsi_server/src/server/process_worker.rs +0 -203
  255. data/gems/server/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +0 -260
  256. data/gems/server/ext/itsi_server/src/server/serve_strategy/mod.rs +0 -27
  257. data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +0 -276
  258. data/gems/server/ext/itsi_server/src/server/signal.rs +0 -74
  259. data/gems/server/ext/itsi_server/src/server/thread_worker.rs +0 -399
  260. data/gems/server/ext/itsi_server/src/server/tls/locked_dir_cache.rs +0 -132
  261. data/gems/server/ext/itsi_server/src/server/tls.rs +0 -265
  262. data/gems/server/ext/itsi_tracing/Cargo.lock +0 -274
  263. data/gems/server/ext/itsi_tracing/Cargo.toml +0 -16
  264. data/gems/server/ext/itsi_tracing/src/lib.rs +0 -58
  265. data/gems/server/lib/itsi/server/options_dsl.rb +0 -401
  266. data/gems/server/lib/itsi/stream_io.rb +0 -38
  267. data/location_dsl.rb +0 -381
  268. data/sandbox/itsi_sandbox_rails/.kamal/hooks/docker-setup.sample +0 -3
  269. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-app-boot.sample +0 -3
  270. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-deploy.sample +0 -14
  271. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-proxy-reboot.sample +0 -3
  272. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-app-boot.sample +0 -3
  273. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-build.sample +0 -51
  274. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-connect.sample +0 -47
  275. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-deploy.sample +0 -109
  276. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-proxy-reboot.sample +0 -3
  277. data/sandbox/itsi_sandbox_rails/.kamal/secrets +0 -17
  278. data/sandbox/itsi_sandbox_rails/bin/dev +0 -2
  279. data/sandbox/itsi_sandbox_rails/bin/jobs +0 -6
  280. data/sandbox/itsi_sandbox_rails/bin/kamal +0 -27
  281. data/sandbox/itsi_sandbox_rails/bin/thrust +0 -5
  282. data/sandbox/itsi_sandbox_rails/config/cache.yml +0 -16
  283. data/sandbox/itsi_sandbox_rails/config/deploy.yml +0 -116
  284. data/sandbox/itsi_sandbox_rails/config/queue.yml +0 -18
  285. data/sandbox/itsi_sandbox_rails/config/recurring.yml +0 -10
  286. data/sandbox/itsi_sandbox_rails/db/cable_schema.rb +0 -11
  287. data/sandbox/itsi_sandbox_rails/db/cache_schema.rb +0 -14
  288. data/sandbox/itsi_sandbox_rails/db/queue_schema.rb +0 -129
  289. data/sandbox/itsi_sandbox_rails/public/400.html +0 -114
  290. data/sandbox/itsi_sandbox_rails/test/fixtures/posts.yml +0 -9
  291. data/sandbox/itsi_sandbox_rails/test/models/post_test.rb +0 -7
  292. /data/{sandbox/itsi_sandbox_rails/script/.keep → crates/_index.md} +0 -0
  293. /data/gems/server/lib/itsi/{index.html → server/default_app/index.html} +0 -0
@@ -0,0 +1,225 @@
1
+ use derive_more::Debug;
2
+ use globset::{Glob, GlobSet, GlobSetBuilder};
3
+ use magnus::error::Result;
4
+ use nix::unistd::{close, fork, pipe, read};
5
+ use notify::{event::ModifyKind, EventKind, RecommendedWatcher};
6
+ use notify::{Event, RecursiveMode, Watcher};
7
+ use std::path::Path;
8
+ use std::sync::mpsc::Sender;
9
+ use std::time::{Duration, Instant};
10
+ use std::{collections::HashSet, fs};
11
+ use std::{
12
+ os::fd::{AsRawFd, IntoRawFd, OwnedFd},
13
+ path::PathBuf,
14
+ process::Command,
15
+ sync::mpsc,
16
+ thread::{self},
17
+ };
18
+
19
+ /// Represents a set of patterns and commands.
20
+ #[derive(Debug, Clone)]
21
+ struct PatternGroup {
22
+ base_dir: PathBuf,
23
+ glob_set: GlobSet,
24
+ pattern: String,
25
+ commands: Vec<Vec<String>>,
26
+ last_triggered: Option<Instant>,
27
+ }
28
+
29
+ /// Extracts the base directory from a wildcard pattern by taking the portion up to the first
30
+ /// component that contains a wildcard character.
31
+ fn extract_and_canonicalize_base_dir(pattern: &str) -> PathBuf {
32
+ if !(pattern.contains("*") || pattern.contains("?") || pattern.contains('[')) {
33
+ if let Ok(metadata) = fs::metadata(pattern) {
34
+ if metadata.is_dir() {
35
+ return fs::canonicalize(pattern).unwrap();
36
+ }
37
+ if metadata.is_file() {
38
+ return fs::canonicalize(pattern)
39
+ .unwrap()
40
+ .parent()
41
+ .unwrap()
42
+ .to_path_buf();
43
+ }
44
+ }
45
+ }
46
+
47
+ let path = Path::new(pattern);
48
+ let mut base = PathBuf::new();
49
+ for comp in path.components() {
50
+ let comp_str = comp.as_os_str().to_string_lossy();
51
+ if comp_str.contains('*') || comp_str.contains('?') || comp_str.contains('[') {
52
+ break;
53
+ } else {
54
+ base.push(comp);
55
+ }
56
+ }
57
+ // If no base was built, default to "."
58
+ let base = if base.as_os_str().is_empty() || !base.exists() {
59
+ PathBuf::from(".")
60
+ } else {
61
+ base
62
+ };
63
+
64
+ // Canonicalize to get the absolute path.
65
+ fs::canonicalize(&base).unwrap_or(base)
66
+ }
67
+
68
+ /// Minimum time between triggering the same pattern group (debounce time)
69
+ const DEBOUNCE_DURATION: Duration = Duration::from_millis(500);
70
+
71
+ pub fn watch_groups(pattern_groups: Vec<(String, Vec<Vec<String>>)>) -> Result<Option<OwnedFd>> {
72
+ let (r_fd, w_fd): (OwnedFd, OwnedFd) = pipe().map_err(|e| {
73
+ magnus::Error::new(
74
+ magnus::exception::exception(),
75
+ format!("Failed to create watcher pipe: {}", e),
76
+ )
77
+ })?;
78
+
79
+ let fork_result = unsafe {
80
+ fork().map_err(|e| {
81
+ magnus::Error::new(
82
+ magnus::exception::exception(),
83
+ format!("Failed to fork file watcher: {}", e),
84
+ )
85
+ })
86
+ }?;
87
+
88
+ if fork_result.is_child() {
89
+ let _ = close(w_fd.into_raw_fd());
90
+ thread::spawn(move || {
91
+ let mut buf = [0u8; 1];
92
+ loop {
93
+ match read(r_fd.as_raw_fd(), &mut buf) {
94
+ Ok(0) => {
95
+ std::process::exit(0);
96
+ }
97
+ Ok(_) => {}
98
+ Err(_) => {
99
+ std::process::exit(0);
100
+ }
101
+ }
102
+ }
103
+ });
104
+
105
+ let mut groups = Vec::new();
106
+ for (pattern, commands) in pattern_groups.into_iter() {
107
+ let base_dir = extract_and_canonicalize_base_dir(&pattern);
108
+ let glob = Glob::new(&pattern).map_err(|e| {
109
+ magnus::Error::new(
110
+ magnus::exception::exception(),
111
+ format!("Failed to create watch glob: {}", e),
112
+ )
113
+ })?;
114
+ let glob_set = GlobSetBuilder::new().add(glob).build().map_err(|e| {
115
+ magnus::Error::new(
116
+ magnus::exception::exception(),
117
+ format!("Failed to create watch glob set: {}", e),
118
+ )
119
+ })?;
120
+ groups.push(PatternGroup {
121
+ base_dir,
122
+ glob_set,
123
+ pattern,
124
+ commands,
125
+ last_triggered: None,
126
+ });
127
+ }
128
+
129
+ // Create a channel and a watcher.
130
+ let (tx, rx) = mpsc::channel::<notify::Result<Event>>();
131
+ let sender = tx.clone();
132
+ fn event_fn(sender: Sender<notify::Result<Event>>) -> impl Fn(notify::Result<Event>) {
133
+ move |res| match res {
134
+ Ok(event) => {
135
+ sender.send(Ok(event)).unwrap();
136
+ }
137
+ Err(e) => println!("watch error: {:?}", e),
138
+ }
139
+ }
140
+
141
+ let mut watched_dirs = HashSet::new();
142
+ let mut watcher: RecommendedWatcher =
143
+ notify::recommended_watcher(event_fn(sender)).expect("Failed to create watcher");
144
+ for group in &groups {
145
+ if watched_dirs.insert(group.base_dir.clone()) {
146
+ watcher
147
+ .watch(&group.base_dir, RecursiveMode::Recursive)
148
+ .expect("Failed to add watch");
149
+ }
150
+ }
151
+
152
+ // Main event loop.
153
+ for res in rx {
154
+ match res {
155
+ Ok(event) => {
156
+ if !matches!(event.kind, EventKind::Modify(ModifyKind::Metadata(_))) {
157
+ continue;
158
+ }
159
+ let now = Instant::now();
160
+ for group in &mut groups {
161
+ for path in event.paths.iter() {
162
+ if let Ok(rel_path) = path.strip_prefix(&group.base_dir) {
163
+ if group.glob_set.is_match(rel_path)
164
+ || rel_path.to_str().is_some_and(|s| s == group.pattern)
165
+ {
166
+ // Check if we should debounce this event
167
+ if let Some(last_triggered) = group.last_triggered {
168
+ if now.duration_since(last_triggered) < DEBOUNCE_DURATION {
169
+ // Skip this event as we've recently triggered for this pattern
170
+ continue;
171
+ }
172
+ }
173
+
174
+ // Update the last triggered time
175
+ group.last_triggered = Some(now);
176
+
177
+ // Execute the commands for this group.
178
+ for command in &group.commands {
179
+ if command.is_empty() {
180
+ continue;
181
+ }
182
+ let mut cmd = Command::new(&command[0]);
183
+ if command.len() > 1 {
184
+ cmd.args(&command[1..]);
185
+ }
186
+ match cmd.spawn() {
187
+ Ok(mut child) => {
188
+ if let Err(e) = child.wait() {
189
+ eprintln!(
190
+ "Command {:?} failed: {:?}",
191
+ command, e
192
+ );
193
+ }
194
+ }
195
+ Err(e) => {
196
+ eprintln!(
197
+ "Failed to execute command {:?}: {:?}",
198
+ command, e
199
+ );
200
+ }
201
+ }
202
+ }
203
+ break;
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ Err(e) => println!("Watch error: {:?}", e),
210
+ }
211
+ }
212
+
213
+ // Clean up the watches.
214
+ for group in &groups {
215
+ watcher
216
+ .unwatch(&group.base_dir)
217
+ .expect("Failed to remove watch");
218
+ }
219
+ drop(watcher);
220
+ std::process::exit(0);
221
+ } else {
222
+ let _ = close(r_fd.into_raw_fd());
223
+ Ok(Some(w_fd))
224
+ }
225
+ }
@@ -0,0 +1,355 @@
1
+ use super::file_watcher::{self};
2
+ use crate::{
3
+ ruby_types::ITSI_SERVER_CONFIG,
4
+ server::{bind::Bind, listener::Listener, middleware_stack::MiddlewareSet},
5
+ };
6
+ use derive_more::Debug;
7
+ use itsi_rb_helpers::{call_with_gvl, print_rb_backtrace, HeapVal, HeapValue};
8
+ use itsi_tracing::set_level;
9
+ use magnus::{
10
+ block::Proc,
11
+ error::Result,
12
+ value::{LazyId, ReprValue},
13
+ RArray, RHash, Ruby, Symbol, Value,
14
+ };
15
+ use nix::{
16
+ fcntl::{fcntl, FcntlArg, FdFlag},
17
+ unistd::{close, dup},
18
+ };
19
+ use parking_lot::{Mutex, RwLock};
20
+ use std::{
21
+ collections::HashMap,
22
+ os::fd::{AsRawFd, OwnedFd, RawFd},
23
+ path::PathBuf,
24
+ sync::{Arc, OnceLock},
25
+ };
26
+
27
+ static DEFAULT_BIND: &str = "http://localhost:3000";
28
+ static ID_BUILD_CONFIG: LazyId = LazyId::new("build_config");
29
+ static ID_RELOAD_EXEC: LazyId = LazyId::new("reload_exec");
30
+
31
+ #[derive(Debug, Clone)]
32
+ pub struct ItsiServerConfig {
33
+ pub cli_params: Arc<HeapValue<RHash>>,
34
+ pub itsifile_path: Option<PathBuf>,
35
+ pub itsi_config_proc: Arc<Option<HeapValue<Proc>>>,
36
+ #[debug(skip)]
37
+ pub server_params: Arc<RwLock<Arc<ServerParams>>>,
38
+ pub watcher_fd: Arc<Option<OwnedFd>>,
39
+ }
40
+
41
+ #[derive(Debug)]
42
+ pub struct ServerParams {
43
+ /// Cluster params
44
+ pub workers: u8,
45
+ pub worker_memory_limit: Option<u64>,
46
+ pub silence: bool,
47
+ pub shutdown_timeout: f64,
48
+ pub hooks: HashMap<String, HeapValue<Proc>>,
49
+ pub preload: bool,
50
+
51
+ pub notify_watchers: Option<Vec<(String, Vec<Vec<String>>)>>,
52
+ /// Worker params
53
+ pub threads: u8,
54
+ pub script_name: String,
55
+ pub streamable_body: bool,
56
+ pub multithreaded_reactor: bool,
57
+ pub scheduler_class: Option<String>,
58
+ pub oob_gc_responses_threshold: Option<u64>,
59
+ pub middleware_loader: HeapValue<Proc>,
60
+ pub default_app_loader: HeapValue<Proc>,
61
+ pub middleware: OnceLock<MiddlewareSet>,
62
+ pub binds: Vec<Bind>,
63
+ #[debug(skip)]
64
+ pub(crate) listeners: Mutex<Vec<Listener>>,
65
+ listener_info: Mutex<HashMap<String, i32>>,
66
+ }
67
+
68
+ impl ServerParams {
69
+ pub fn preload_ruby(self: &Arc<Self>) -> Result<()> {
70
+ call_with_gvl(|ruby| -> Result<()> {
71
+ if self
72
+ .scheduler_class
73
+ .as_ref()
74
+ .is_some_and(|t| t == "Itsi::Scheduler")
75
+ {
76
+ ruby.require("itsi/scheduler")?;
77
+ }
78
+ let default_app: HeapVal = self.default_app_loader.call::<_, Value>(())?.into();
79
+ let middleware = MiddlewareSet::new(
80
+ self.middleware_loader
81
+ .call::<_, Option<Value>>(())
82
+ .inspect_err(|e| {
83
+ if let Some(err_value) = e.value() {
84
+ print_rb_backtrace(err_value);
85
+ }
86
+ })?
87
+ .map(|mw| mw.into()),
88
+ default_app,
89
+ )?;
90
+ self.middleware.set(middleware).map_err(|_| {
91
+ magnus::Error::new(
92
+ magnus::exception::runtime_error(),
93
+ "Failed to set middleware",
94
+ )
95
+ })?;
96
+ Ok(())
97
+ })?;
98
+ Ok(())
99
+ }
100
+
101
+ fn from_rb_hash(rb_param_hash: RHash) -> Result<ServerParams> {
102
+ let workers = rb_param_hash
103
+ .fetch::<_, Option<u8>>("workers")?
104
+ .unwrap_or(num_cpus::get() as u8);
105
+ let worker_memory_limit: Option<u64> = rb_param_hash.fetch("worker_memory_limit")?;
106
+ let silence: bool = rb_param_hash.fetch("silence")?;
107
+ let multithreaded_reactor: bool = rb_param_hash.fetch("multithreaded_reactor")?;
108
+ let shutdown_timeout: f64 = rb_param_hash.fetch("shutdown_timeout")?;
109
+
110
+ let hooks: Option<RHash> = rb_param_hash.fetch("hooks")?;
111
+ let hooks = hooks
112
+ .map(|rhash| -> Result<HashMap<String, HeapValue<Proc>>> {
113
+ let mut hook_map: HashMap<String, HeapValue<Proc>> = HashMap::new();
114
+ for pair in rhash.enumeratorize::<_, ()>("each", ()) {
115
+ if let Some(pair_value) = RArray::from_value(pair?) {
116
+ if let (Ok(key), Ok(value)) =
117
+ (pair_value.entry::<Value>(0), pair_value.entry::<Proc>(1))
118
+ {
119
+ hook_map.insert(key.to_string(), HeapValue::from(value));
120
+ }
121
+ }
122
+ }
123
+ Ok(hook_map)
124
+ })
125
+ .transpose()?
126
+ .unwrap_or_default();
127
+ let preload: bool = rb_param_hash.fetch("preload")?;
128
+ let notify_watchers: Option<Vec<(String, Vec<Vec<String>>)>> =
129
+ rb_param_hash.fetch("notify_watchers")?;
130
+ let threads: u8 = rb_param_hash.fetch("threads")?;
131
+ let script_name: String = rb_param_hash.fetch("script_name")?;
132
+ let streamable_body: bool = rb_param_hash.fetch("streamable_body")?;
133
+ let scheduler_class: Option<String> = rb_param_hash.fetch("scheduler_class")?;
134
+ let oob_gc_responses_threshold: Option<u64> =
135
+ rb_param_hash.fetch("oob_gc_responses_threshold")?;
136
+ let middleware_loader: Proc = rb_param_hash.fetch("middleware_loader")?;
137
+ let default_app_loader: Proc = rb_param_hash.fetch("default_app_loader")?;
138
+ let log_level: Option<String> = rb_param_hash.fetch("log_level")?;
139
+
140
+ if let Some(level) = log_level {
141
+ set_level(&level);
142
+ }
143
+
144
+ let binds: Option<Vec<String>> = rb_param_hash.fetch("binds")?;
145
+ let binds = binds
146
+ .unwrap_or_else(|| vec![DEFAULT_BIND.to_string()])
147
+ .into_iter()
148
+ .map(|s| s.parse())
149
+ .collect::<itsi_error::Result<Vec<Bind>>>()?;
150
+
151
+ let listeners = if let Some(preexisting_listeners) =
152
+ rb_param_hash.delete::<_, Option<String>>("listeners")?
153
+ {
154
+ let bind_to_fd_map: HashMap<String, i32> = serde_json::from_str(&preexisting_listeners)
155
+ .map_err(|e| {
156
+ magnus::Error::new(
157
+ magnus::exception::exception(),
158
+ format!("Invalid listener info: {}", e),
159
+ )
160
+ })?;
161
+
162
+ binds
163
+ .iter()
164
+ .cloned()
165
+ .map(|bind| {
166
+ if let Some(fd) = bind_to_fd_map.get(&bind.listener_address_string()) {
167
+ Listener::inherit_fd(bind, *fd)
168
+ } else {
169
+ Listener::try_from(bind)
170
+ }
171
+ })
172
+ .collect::<std::result::Result<Vec<Listener>, _>>()?
173
+ .into_iter()
174
+ .collect::<Vec<_>>()
175
+ } else {
176
+ binds
177
+ .iter()
178
+ .cloned()
179
+ .map(Listener::try_from)
180
+ .collect::<std::result::Result<Vec<Listener>, _>>()?
181
+ .into_iter()
182
+ .collect::<Vec<_>>()
183
+ };
184
+
185
+ let listener_info = listeners
186
+ .iter()
187
+ .map(|listener| {
188
+ listener.handover().map_err(|e| {
189
+ magnus::Error::new(magnus::exception::runtime_error(), e.to_string())
190
+ })
191
+ })
192
+ .collect::<Result<HashMap<String, i32>>>()?;
193
+
194
+ Ok(ServerParams {
195
+ workers,
196
+ worker_memory_limit,
197
+ silence,
198
+ multithreaded_reactor,
199
+ shutdown_timeout,
200
+ hooks,
201
+ preload,
202
+ notify_watchers,
203
+ threads,
204
+ script_name,
205
+ streamable_body,
206
+ scheduler_class,
207
+ oob_gc_responses_threshold,
208
+ binds,
209
+ listener_info: Mutex::new(listener_info),
210
+ listeners: Mutex::new(listeners),
211
+ middleware_loader: middleware_loader.into(),
212
+ default_app_loader: default_app_loader.into(),
213
+ middleware: OnceLock::new(),
214
+ })
215
+ }
216
+ }
217
+
218
+ impl ItsiServerConfig {
219
+ pub fn new(
220
+ ruby: &Ruby,
221
+ cli_params: RHash,
222
+ itsifile_path: Option<PathBuf>,
223
+ itsi_config_proc: Option<Proc>,
224
+ ) -> Result<Self> {
225
+ let itsi_config_proc = Arc::new(itsi_config_proc.map(HeapValue::from));
226
+ let server_params = Self::combine_params(
227
+ ruby,
228
+ cli_params,
229
+ itsifile_path.as_ref(),
230
+ itsi_config_proc.clone(),
231
+ )?;
232
+ cli_params.delete::<_, Value>(Symbol::new("listeners"))?;
233
+
234
+ let watcher_fd = if let Some(watchers) = server_params.notify_watchers.clone() {
235
+ file_watcher::watch_groups(watchers)?
236
+ } else {
237
+ None
238
+ };
239
+
240
+ Ok(ItsiServerConfig {
241
+ cli_params: Arc::new(cli_params.into()),
242
+ server_params: RwLock::new(server_params.clone()).into(),
243
+ itsi_config_proc,
244
+ itsifile_path,
245
+ watcher_fd: watcher_fd.into(),
246
+ })
247
+ }
248
+
249
+ /// Reload
250
+ pub fn reload(self: Arc<Self>, cluster_worker: bool) -> Result<bool> {
251
+ let server_params = call_with_gvl(|ruby| {
252
+ Self::combine_params(
253
+ &ruby,
254
+ self.cli_params.cloned(),
255
+ self.itsifile_path.as_ref(),
256
+ self.itsi_config_proc.clone(),
257
+ )
258
+ })?;
259
+
260
+ let is_single_mode = self.server_params.read().workers == 1;
261
+
262
+ let requires_exec = if !is_single_mode && !server_params.preload {
263
+ // In cluster mode children are cycled during a reload
264
+ // and if preload is disabled, will get a clean memory slate,
265
+ // so we don't need to exec.
266
+ false
267
+ } else {
268
+ // In non-cluster mode, or when preloading is enabled, we shouldn't try to
269
+ // reload inside the existing process (as new code may conflict with old),
270
+ // and should re-exec instead.
271
+ true
272
+ };
273
+
274
+ *self.server_params.write() = server_params.clone();
275
+ Ok(requires_exec && (cluster_worker || is_single_mode))
276
+ }
277
+
278
+ fn combine_params(
279
+ ruby: &Ruby,
280
+ cli_params: RHash,
281
+ itsifile_path: Option<&PathBuf>,
282
+ itsi_config_proc: Arc<Option<HeapValue<Proc>>>,
283
+ ) -> Result<Arc<ServerParams>> {
284
+ let inner = itsi_config_proc
285
+ .as_ref()
286
+ .clone()
287
+ .map(|hv| hv.clone().inner());
288
+ let rb_param_hash: RHash = ruby.get_inner_ref(&ITSI_SERVER_CONFIG).funcall(
289
+ *ID_BUILD_CONFIG,
290
+ (cli_params, itsifile_path.cloned(), inner),
291
+ )?;
292
+ Ok(Arc::new(ServerParams::from_rb_hash(rb_param_hash)?))
293
+ }
294
+
295
+ fn clear_cloexec(fd: RawFd) -> nix::Result<()> {
296
+ let current_flags = fcntl(fd, FcntlArg::F_GETFD)?;
297
+ let mut flags = FdFlag::from_bits_truncate(current_flags);
298
+ // Remove the FD_CLOEXEC flag
299
+ flags.remove(FdFlag::FD_CLOEXEC);
300
+ // Set the new flags back on the file descriptor
301
+ fcntl(fd, FcntlArg::F_SETFD(flags))?;
302
+ Ok(())
303
+ }
304
+
305
+ pub fn dup_fds(self: &Arc<Self>) -> Result<()> {
306
+ let binding = self.server_params.read();
307
+ let mut listener_info_guard = binding.listener_info.lock();
308
+ let dupped_fd_map = listener_info_guard
309
+ .iter()
310
+ .map(|(str, fd)| {
311
+ let dupped_fd = dup(*fd).map_err(|errno| {
312
+ magnus::Error::new(
313
+ magnus::exception::exception(),
314
+ format!("Errno {} while trying to dup {}", errno, fd),
315
+ )
316
+ })?;
317
+ Self::clear_cloexec(dupped_fd).map_err(|e| {
318
+ magnus::Error::new(
319
+ magnus::exception::exception(),
320
+ format!("Failed to clear cloexec flag for fd {}: {}", dupped_fd, e),
321
+ )
322
+ })?;
323
+ Ok((str.clone(), dupped_fd))
324
+ })
325
+ .collect::<Result<HashMap<String, i32>>>()?;
326
+ *listener_info_guard = dupped_fd_map;
327
+ Ok(())
328
+ }
329
+
330
+ pub fn stop_watcher(self: &Arc<Self>) -> Result<()> {
331
+ if let Some(r_fd) = self.watcher_fd.as_ref() {
332
+ close(r_fd.as_raw_fd()).ok();
333
+ }
334
+ Ok(())
335
+ }
336
+
337
+ pub fn reload_exec(self: &Arc<Self>) -> Result<()> {
338
+ let listener_json =
339
+ serde_json::to_string(&self.server_params.read().listener_info.lock().clone())
340
+ .map_err(|e| {
341
+ magnus::Error::new(
342
+ magnus::exception::exception(),
343
+ format!("Invalid listener info: {}", e),
344
+ )
345
+ })?;
346
+
347
+ self.stop_watcher()?;
348
+ call_with_gvl(|ruby| -> Result<()> {
349
+ ruby.get_inner_ref(&ITSI_SERVER_CONFIG)
350
+ .funcall::<_, _, Value>(*ID_RELOAD_EXEC, (listener_json,))?;
351
+ Ok(())
352
+ })?;
353
+ Ok(())
354
+ }
355
+ }
@@ -0,0 +1,82 @@
1
+ use crate::server::{
2
+ serve_strategy::{cluster_mode::ClusterMode, single_mode::SingleMode, ServeStrategy},
3
+ signal::{clear_signal_handlers, reset_signal_handlers, send_shutdown_event},
4
+ };
5
+ use itsi_rb_helpers::{call_without_gvl, print_rb_backtrace};
6
+ use itsi_server_config::ItsiServerConfig;
7
+ use itsi_tracing::{error, run_silently};
8
+ use magnus::{block::Proc, error::Result, RHash, Ruby};
9
+ use parking_lot::Mutex;
10
+ use std::{path::PathBuf, sync::Arc};
11
+ use tracing::{info, instrument};
12
+ mod file_watcher;
13
+ pub mod itsi_server_config;
14
+ #[magnus::wrap(class = "Itsi::Server", free_immediately, size)]
15
+ #[derive(Clone)]
16
+ pub struct ItsiServer {
17
+ pub config: Arc<Mutex<Arc<ItsiServerConfig>>>,
18
+ }
19
+
20
+ impl ItsiServer {
21
+ pub fn new(
22
+ ruby: &Ruby,
23
+ cli_params: RHash,
24
+ itsifile_path: Option<PathBuf>,
25
+ itsi_config_proc: Option<Proc>,
26
+ ) -> Result<Self> {
27
+ Ok(Self {
28
+ config: Arc::new(Mutex::new(Arc::new(ItsiServerConfig::new(
29
+ ruby,
30
+ cli_params,
31
+ itsifile_path,
32
+ itsi_config_proc,
33
+ )?))),
34
+ })
35
+ }
36
+
37
+ pub fn stop(&self) -> Result<()> {
38
+ send_shutdown_event();
39
+ Ok(())
40
+ }
41
+
42
+ #[instrument(skip(self))]
43
+ pub fn start(&self) -> Result<()> {
44
+ let result = if self.config.lock().server_params.read().silence {
45
+ run_silently(|| self.build_and_run_strategy())
46
+ } else {
47
+ info!("Itsi - Rolling into action. 💨 ⚪ ");
48
+ self.build_and_run_strategy()
49
+ };
50
+ if let Err(e) = result {
51
+ if let Some(err_value) = e.value() {
52
+ print_rb_backtrace(err_value);
53
+ }
54
+ return Err(e);
55
+ }
56
+ Ok(())
57
+ }
58
+
59
+ pub(crate) fn build_strategy(&self) -> Result<ServeStrategy> {
60
+ let server_config = self.config.lock();
61
+ Ok(if server_config.server_params.read().workers > 1 {
62
+ ServeStrategy::Cluster(Arc::new(ClusterMode::new(server_config.clone())))
63
+ } else {
64
+ ServeStrategy::Single(Arc::new(SingleMode::new(server_config.clone())?))
65
+ })
66
+ }
67
+
68
+ fn build_and_run_strategy(&self) -> Result<()> {
69
+ reset_signal_handlers();
70
+ call_without_gvl(move || -> Result<()> {
71
+ let strategy = self.build_strategy()?;
72
+ if let Err(e) = strategy.clone().run() {
73
+ error!("Error running server: {}", e);
74
+ strategy.stop()?;
75
+ }
76
+ Ok(())
77
+ })?;
78
+ clear_signal_handlers();
79
+ info!("Server stopped");
80
+ Ok(())
81
+ }
82
+ }