itsi 0.1.9 → 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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1542 -43
  3. data/Itsi.rb +125 -0
  4. data/Rakefile +8 -4
  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 +59 -9
  9. data/crates/itsi_server/Cargo.toml +70 -28
  10. data/crates/itsi_server/src/lib.rs +80 -80
  11. data/crates/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  12. data/{gems/server/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs → crates/itsi_server/src/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/crates/itsi_server/src/ruby_types/itsi_http_request.rs +282 -0
  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 +29 -17
  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 +111 -11
  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 +44 -11
  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 +129 -46
  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 +325 -167
  61. data/crates/itsi_server/src/server/signal.rs +20 -4
  62. data/crates/itsi_server/src/server/static_file_server.rs +984 -0
  63. data/crates/itsi_server/src/server/thread_worker.rs +165 -88
  64. data/crates/itsi_server/src/server/tls.rs +1 -1
  65. data/crates/itsi_server/src/server/types.rs +43 -0
  66. data/crates/itsi_server/test.md +14 -0
  67. data/crates/itsi_tracing/Cargo.toml +1 -0
  68. data/crates/itsi_tracing/src/lib.rs +216 -45
  69. data/docs/.gitignore +7 -0
  70. data/docs/.gitpod.yml +15 -0
  71. data/docs/Itsi.rb +17 -0
  72. data/docs/content/_index.md +17 -0
  73. data/docs/content/about.md +6 -0
  74. data/docs/content/docs/_index.md +18 -0
  75. data/docs/content/docs/first-page.md +9 -0
  76. data/docs/content/docs/folder/_index.md +10 -0
  77. data/docs/content/docs/folder/leaf.md +7 -0
  78. data/docs/go.mod +5 -0
  79. data/docs/go.sum +2 -0
  80. data/docs/hugo.yaml +77 -0
  81. data/examples/static_assets_example.rb +83 -0
  82. data/gems/_index.md +18 -0
  83. data/gems/scheduler/CODE_OF_CONDUCT.md +7 -0
  84. data/gems/scheduler/Cargo.lock +75 -14
  85. data/gems/scheduler/README.md +5 -0
  86. data/gems/scheduler/_index.md +7 -0
  87. data/gems/scheduler/itsi-scheduler.gemspec +4 -1
  88. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  89. data/gems/scheduler/lib/itsi/scheduler.rb +2 -2
  90. data/gems/scheduler/test/test_file_io.rb +0 -1
  91. data/gems/scheduler/test/test_itsi_scheduler.rb +1 -1
  92. data/gems/server/CHANGELOG.md +5 -0
  93. data/gems/server/CODE_OF_CONDUCT.md +7 -0
  94. data/gems/server/Cargo.lock +1543 -43
  95. data/gems/server/README.md +4 -0
  96. data/gems/server/_index.md +6 -0
  97. data/gems/server/exe/itsi +46 -57
  98. data/gems/server/itsi-server.gemspec +3 -2
  99. data/gems/server/lib/itsi/{request.rb → http_request.rb} +29 -5
  100. data/gems/server/lib/itsi/http_response.rb +39 -0
  101. data/gems/server/lib/itsi/server/Itsi.rb +119 -0
  102. data/gems/server/lib/itsi/server/config/dsl.rb +506 -0
  103. data/gems/server/lib/itsi/server/config.rb +131 -0
  104. data/gems/server/lib/itsi/server/default_app/default_app.rb +38 -0
  105. data/gems/server/lib/itsi/server/grpc_interface.rb +213 -0
  106. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +9 -6
  107. data/gems/server/lib/itsi/server/rack_interface.rb +24 -9
  108. data/gems/server/lib/itsi/server/scheduler_interface.rb +1 -1
  109. data/gems/server/lib/itsi/server/scheduler_mode.rb +4 -0
  110. data/gems/server/lib/itsi/server/signal_trap.rb +6 -1
  111. data/gems/server/lib/itsi/server/version.rb +1 -1
  112. data/gems/server/lib/itsi/server.rb +75 -60
  113. data/gems/server/lib/itsi/standard_headers.rb +86 -0
  114. data/gems/server/test/helpers/test_helper.rb +14 -12
  115. data/gems/server/test/test_itsi_server.rb +21 -2
  116. data/lib/itsi/version.rb +1 -1
  117. data/sandbox/itsi_file/Gemfile +11 -0
  118. data/sandbox/itsi_file/Gemfile.lock +69 -0
  119. data/sandbox/itsi_file/Itsi.rb +276 -0
  120. data/sandbox/itsi_file/error.html +2 -0
  121. data/sandbox/itsi_file/organisations_controller.rb +20 -0
  122. data/sandbox/itsi_file/public/assets/image.png +0 -0
  123. data/sandbox/itsi_file/public/assets/index.html +1 -0
  124. data/sandbox/itsi_sandbox_async/Gemfile +1 -1
  125. data/sandbox/itsi_sandbox_hanami/Gemfile.lock +2 -2
  126. data/sandbox/itsi_sandbox_rack/Gemfile.lock +2 -2
  127. data/sandbox/itsi_sandbox_rack/config.ru +2 -15
  128. data/sandbox/itsi_sandbox_rails/.dockerignore +2 -5
  129. data/sandbox/itsi_sandbox_rails/.github/workflows/ci.yml +1 -1
  130. data/sandbox/itsi_sandbox_rails/.gitignore +2 -1
  131. data/sandbox/itsi_sandbox_rails/Dockerfile +6 -9
  132. data/sandbox/itsi_sandbox_rails/Gemfile +16 -22
  133. data/sandbox/itsi_sandbox_rails/Gemfile.lock +100 -225
  134. data/sandbox/itsi_sandbox_rails/app/assets/config/manifest.js +4 -0
  135. data/sandbox/itsi_sandbox_rails/app/assets/stylesheets/application.css +11 -6
  136. data/sandbox/itsi_sandbox_rails/app/channels/application_cable/channel.rb +4 -0
  137. data/sandbox/itsi_sandbox_rails/app/channels/application_cable/connection.rb +4 -0
  138. data/sandbox/itsi_sandbox_rails/app/controllers/live_controller.rb +7 -8
  139. data/sandbox/itsi_sandbox_rails/app/controllers/uploads_controller.rb +0 -3
  140. data/sandbox/itsi_sandbox_rails/app/views/layouts/application.html.erb +2 -7
  141. data/sandbox/itsi_sandbox_rails/bin/docker-entrypoint +3 -4
  142. data/sandbox/itsi_sandbox_rails/bin/setup +8 -5
  143. data/sandbox/itsi_sandbox_rails/config/application.rb +1 -35
  144. data/sandbox/itsi_sandbox_rails/config/cable.yml +3 -10
  145. data/sandbox/itsi_sandbox_rails/config/credentials.yml.enc +1 -1
  146. data/sandbox/itsi_sandbox_rails/config/database.yml +9 -19
  147. data/sandbox/itsi_sandbox_rails/config/environment.rb +1 -1
  148. data/sandbox/itsi_sandbox_rails/config/environments/development.rb +21 -12
  149. data/sandbox/itsi_sandbox_rails/config/environments/production.rb +49 -34
  150. data/sandbox/itsi_sandbox_rails/config/environments/test.rb +19 -5
  151. data/sandbox/itsi_sandbox_rails/config/initializers/assets.rb +5 -0
  152. data/sandbox/itsi_sandbox_rails/config/initializers/filter_parameter_logging.rb +1 -1
  153. data/sandbox/itsi_sandbox_rails/config/initializers/permissions_policy.rb +13 -0
  154. data/sandbox/itsi_sandbox_rails/config/puma.rb +2 -9
  155. data/sandbox/itsi_sandbox_rails/config.ru +0 -1
  156. data/sandbox/itsi_sandbox_rails/db/migrate/20250301041554_create_posts.rb +1 -1
  157. data/sandbox/itsi_sandbox_rails/db/schema.rb +2 -2
  158. data/sandbox/itsi_sandbox_rails/lib/assets/.keep +0 -0
  159. data/sandbox/itsi_sandbox_rails/public/404.html +66 -113
  160. data/sandbox/itsi_sandbox_rails/public/406-unsupported-browser.html +65 -113
  161. data/sandbox/itsi_sandbox_rails/public/422.html +66 -113
  162. data/sandbox/itsi_sandbox_rails/public/500.html +65 -113
  163. data/sandbox/itsi_sandbox_rails/public/icon.png +0 -0
  164. data/sandbox/itsi_sandbox_rails/public/icon.svg +2 -2
  165. data/sandbox/itsi_sandbox_rails/test/channels/application_cable/connection_test.rb +13 -0
  166. data/sandbox/itsi_sandbox_roda/Gemfile.lock +3 -10
  167. data/tasks.txt +72 -12
  168. metadata +94 -139
  169. data/crates/itsi_server/src/body_proxy/itsi_body_proxy.rs +0 -122
  170. data/crates/itsi_server/src/body_proxy/mod.rs +0 -2
  171. data/crates/itsi_server/src/request/itsi_request.rs +0 -305
  172. data/crates/itsi_server/src/request/mod.rs +0 -1
  173. data/crates/itsi_server/src/response/mod.rs +0 -1
  174. data/crates/itsi_server/src/server/itsi_server.rs +0 -294
  175. data/gems/scheduler/ext/itsi_error/Cargo.lock +0 -368
  176. data/gems/scheduler/ext/itsi_error/Cargo.toml +0 -11
  177. data/gems/scheduler/ext/itsi_error/src/from.rs +0 -68
  178. data/gems/scheduler/ext/itsi_error/src/lib.rs +0 -24
  179. data/gems/scheduler/ext/itsi_instrument_entry/Cargo.toml +0 -15
  180. data/gems/scheduler/ext/itsi_instrument_entry/src/lib.rs +0 -31
  181. data/gems/scheduler/ext/itsi_rb_helpers/Cargo.lock +0 -355
  182. data/gems/scheduler/ext/itsi_rb_helpers/Cargo.toml +0 -10
  183. data/gems/scheduler/ext/itsi_rb_helpers/src/heap_value.rs +0 -121
  184. data/gems/scheduler/ext/itsi_rb_helpers/src/lib.rs +0 -178
  185. data/gems/scheduler/ext/itsi_scheduler/Cargo.toml +0 -24
  186. data/gems/scheduler/ext/itsi_scheduler/extconf.rb +0 -6
  187. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +0 -56
  188. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +0 -44
  189. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +0 -44
  190. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler.rs +0 -308
  191. data/gems/scheduler/ext/itsi_scheduler/src/lib.rs +0 -38
  192. data/gems/scheduler/ext/itsi_server/Cargo.lock +0 -2956
  193. data/gems/scheduler/ext/itsi_server/Cargo.toml +0 -47
  194. data/gems/scheduler/ext/itsi_server/extconf.rb +0 -6
  195. data/gems/scheduler/ext/itsi_server/src/body_proxy/big_bytes.rs +0 -104
  196. data/gems/scheduler/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +0 -122
  197. data/gems/scheduler/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  198. data/gems/scheduler/ext/itsi_server/src/env.rs +0 -43
  199. data/gems/scheduler/ext/itsi_server/src/lib.rs +0 -112
  200. data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +0 -305
  201. data/gems/scheduler/ext/itsi_server/src/request/mod.rs +0 -1
  202. data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +0 -357
  203. data/gems/scheduler/ext/itsi_server/src/response/mod.rs +0 -1
  204. data/gems/scheduler/ext/itsi_server/src/server/bind.rs +0 -170
  205. data/gems/scheduler/ext/itsi_server/src/server/bind_protocol.rs +0 -37
  206. data/gems/scheduler/ext/itsi_server/src/server/io_stream.rs +0 -104
  207. data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +0 -294
  208. data/gems/scheduler/ext/itsi_server/src/server/lifecycle_event.rs +0 -9
  209. data/gems/scheduler/ext/itsi_server/src/server/listener.rs +0 -318
  210. data/gems/scheduler/ext/itsi_server/src/server/mod.rs +0 -11
  211. data/gems/scheduler/ext/itsi_server/src/server/process_worker.rs +0 -196
  212. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +0 -254
  213. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/mod.rs +0 -27
  214. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +0 -263
  215. data/gems/scheduler/ext/itsi_server/src/server/signal.rs +0 -77
  216. data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +0 -367
  217. data/gems/scheduler/ext/itsi_server/src/server/tls/locked_dir_cache.rs +0 -132
  218. data/gems/scheduler/ext/itsi_server/src/server/tls.rs +0 -265
  219. data/gems/scheduler/ext/itsi_tracing/Cargo.lock +0 -274
  220. data/gems/scheduler/ext/itsi_tracing/Cargo.toml +0 -16
  221. data/gems/scheduler/ext/itsi_tracing/src/lib.rs +0 -58
  222. data/gems/server/ext/itsi_error/Cargo.lock +0 -368
  223. data/gems/server/ext/itsi_error/Cargo.toml +0 -11
  224. data/gems/server/ext/itsi_error/src/from.rs +0 -68
  225. data/gems/server/ext/itsi_error/src/lib.rs +0 -24
  226. data/gems/server/ext/itsi_instrument_entry/Cargo.toml +0 -15
  227. data/gems/server/ext/itsi_instrument_entry/src/lib.rs +0 -31
  228. data/gems/server/ext/itsi_rb_helpers/Cargo.lock +0 -355
  229. data/gems/server/ext/itsi_rb_helpers/Cargo.toml +0 -10
  230. data/gems/server/ext/itsi_rb_helpers/src/heap_value.rs +0 -121
  231. data/gems/server/ext/itsi_rb_helpers/src/lib.rs +0 -178
  232. data/gems/server/ext/itsi_scheduler/Cargo.toml +0 -24
  233. data/gems/server/ext/itsi_scheduler/extconf.rb +0 -6
  234. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +0 -56
  235. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +0 -44
  236. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +0 -44
  237. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler.rs +0 -308
  238. data/gems/server/ext/itsi_scheduler/src/lib.rs +0 -38
  239. data/gems/server/ext/itsi_server/Cargo.lock +0 -2956
  240. data/gems/server/ext/itsi_server/Cargo.toml +0 -47
  241. data/gems/server/ext/itsi_server/extconf.rb +0 -6
  242. data/gems/server/ext/itsi_server/src/body_proxy/big_bytes.rs +0 -104
  243. data/gems/server/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  244. data/gems/server/ext/itsi_server/src/env.rs +0 -43
  245. data/gems/server/ext/itsi_server/src/lib.rs +0 -112
  246. data/gems/server/ext/itsi_server/src/request/itsi_request.rs +0 -305
  247. data/gems/server/ext/itsi_server/src/request/mod.rs +0 -1
  248. data/gems/server/ext/itsi_server/src/response/itsi_response.rs +0 -357
  249. data/gems/server/ext/itsi_server/src/response/mod.rs +0 -1
  250. data/gems/server/ext/itsi_server/src/server/bind.rs +0 -170
  251. data/gems/server/ext/itsi_server/src/server/bind_protocol.rs +0 -37
  252. data/gems/server/ext/itsi_server/src/server/io_stream.rs +0 -104
  253. data/gems/server/ext/itsi_server/src/server/itsi_server.rs +0 -294
  254. data/gems/server/ext/itsi_server/src/server/lifecycle_event.rs +0 -9
  255. data/gems/server/ext/itsi_server/src/server/listener.rs +0 -318
  256. data/gems/server/ext/itsi_server/src/server/mod.rs +0 -11
  257. data/gems/server/ext/itsi_server/src/server/process_worker.rs +0 -196
  258. data/gems/server/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +0 -254
  259. data/gems/server/ext/itsi_server/src/server/serve_strategy/mod.rs +0 -27
  260. data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +0 -263
  261. data/gems/server/ext/itsi_server/src/server/signal.rs +0 -77
  262. data/gems/server/ext/itsi_server/src/server/thread_worker.rs +0 -367
  263. data/gems/server/ext/itsi_server/src/server/tls/locked_dir_cache.rs +0 -132
  264. data/gems/server/ext/itsi_server/src/server/tls.rs +0 -265
  265. data/gems/server/ext/itsi_tracing/Cargo.lock +0 -274
  266. data/gems/server/ext/itsi_tracing/Cargo.toml +0 -16
  267. data/gems/server/ext/itsi_tracing/src/lib.rs +0 -58
  268. data/gems/server/lib/itsi/stream_io.rb +0 -38
  269. data/sandbox/itsi_sandbox_rails/.kamal/hooks/docker-setup.sample +0 -3
  270. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-app-boot.sample +0 -3
  271. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-deploy.sample +0 -14
  272. data/sandbox/itsi_sandbox_rails/.kamal/hooks/post-proxy-reboot.sample +0 -3
  273. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-app-boot.sample +0 -3
  274. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-build.sample +0 -51
  275. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-connect.sample +0 -47
  276. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-deploy.sample +0 -109
  277. data/sandbox/itsi_sandbox_rails/.kamal/hooks/pre-proxy-reboot.sample +0 -3
  278. data/sandbox/itsi_sandbox_rails/.kamal/secrets +0 -17
  279. data/sandbox/itsi_sandbox_rails/bin/dev +0 -2
  280. data/sandbox/itsi_sandbox_rails/bin/jobs +0 -6
  281. data/sandbox/itsi_sandbox_rails/bin/kamal +0 -27
  282. data/sandbox/itsi_sandbox_rails/bin/thrust +0 -5
  283. data/sandbox/itsi_sandbox_rails/config/cache.yml +0 -16
  284. data/sandbox/itsi_sandbox_rails/config/deploy.yml +0 -116
  285. data/sandbox/itsi_sandbox_rails/config/queue.yml +0 -18
  286. data/sandbox/itsi_sandbox_rails/config/recurring.yml +0 -10
  287. data/sandbox/itsi_sandbox_rails/db/cable_schema.rb +0 -11
  288. data/sandbox/itsi_sandbox_rails/db/cache_schema.rb +0 -14
  289. data/sandbox/itsi_sandbox_rails/db/queue_schema.rb +0 -129
  290. data/sandbox/itsi_sandbox_rails/public/400.html +0 -114
  291. data/sandbox/itsi_sandbox_rails/test/fixtures/posts.yml +0 -9
  292. data/sandbox/itsi_sandbox_rails/test/models/post_test.rb +0 -7
  293. /data/{sandbox/itsi_sandbox_rails/script/.keep → crates/_index.md} +0 -0
  294. /data/gems/server/lib/itsi/{index.html → server/default_app/index.html} +0 -0
@@ -0,0 +1,321 @@
1
+ use super::{error_response::ErrorResponse, token_source::TokenSource, FromValue, MiddlewareLayer};
2
+ use crate::server::{
3
+ itsi_service::RequestContext,
4
+ types::{HttpRequest, HttpResponse, RequestExt},
5
+ };
6
+ use async_trait::async_trait;
7
+ use base64::{engine::general_purpose, Engine};
8
+ use either::Either;
9
+ use itsi_error::ItsiError;
10
+ use jwt_simple::{
11
+ claims::{self, JWTClaims, NoCustomClaims},
12
+ prelude::{
13
+ ECDSAP256PublicKeyLike, ECDSAP384PublicKeyLike, ES256PublicKey, ES384PublicKey, HS256Key,
14
+ HS384Key, HS512Key, MACLike, PS256PublicKey, PS384PublicKey, PS512PublicKey,
15
+ RS256PublicKey, RS384PublicKey, RS512PublicKey, RSAPublicKeyLike,
16
+ },
17
+ token::Token,
18
+ };
19
+ use magnus::error::Result;
20
+ use serde::Deserialize;
21
+ use std::str;
22
+ use std::{
23
+ collections::{HashMap, HashSet},
24
+ sync::OnceLock,
25
+ };
26
+
27
+ #[derive(Debug, Clone, Deserialize)]
28
+ pub struct AuthJwt {
29
+ pub token_source: TokenSource,
30
+ pub verifiers: HashMap<JwtAlgorithm, Vec<String>>,
31
+ #[serde(skip_deserializing)]
32
+ pub keys: OnceLock<HashMap<JwtAlgorithm, Vec<JwtKey>>>,
33
+ pub audiences: Option<HashSet<String>>,
34
+ pub subjects: Option<HashSet<String>>,
35
+ pub issuers: Option<HashSet<String>>,
36
+ pub leeway: Option<u64>,
37
+ pub error_response: ErrorResponse,
38
+ }
39
+
40
+ #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Hash)]
41
+ pub enum JwtAlgorithm {
42
+ #[serde(rename(deserialize = "hs256"))]
43
+ Hs256,
44
+ #[serde(rename(deserialize = "hs384"))]
45
+ Hs384,
46
+ #[serde(rename(deserialize = "hs512"))]
47
+ Hs512,
48
+ #[serde(rename(deserialize = "rs256"))]
49
+ Rs256,
50
+ #[serde(rename(deserialize = "rs384"))]
51
+ Rs384,
52
+ #[serde(rename(deserialize = "rs512"))]
53
+ Rs512,
54
+ #[serde(rename(deserialize = "es256"))]
55
+ Es256,
56
+ #[serde(rename(deserialize = "es384"))]
57
+ Es384,
58
+ #[serde(rename(deserialize = "ps256"))]
59
+ Ps256,
60
+ #[serde(rename(deserialize = "ps384"))]
61
+ Ps384,
62
+ #[serde(rename(deserialize = "ps512"))]
63
+ Ps512,
64
+ }
65
+
66
+ impl JwtAlgorithm {
67
+ pub fn key_from(&self, base64: &str) -> Result<JwtKey> {
68
+ let bytes = general_purpose::STANDARD
69
+ .decode(base64)
70
+ .map_err(ItsiError::default)?;
71
+
72
+ match self {
73
+ JwtAlgorithm::Hs256 => Ok(JwtKey::Hs256(HS256Key::from_bytes(&bytes))),
74
+ JwtAlgorithm::Hs384 => Ok(JwtKey::Hs384(HS384Key::from_bytes(&bytes))),
75
+ JwtAlgorithm::Hs512 => Ok(JwtKey::Hs512(HS512Key::from_bytes(&bytes))),
76
+ JwtAlgorithm::Rs256 => Ok(RS256PublicKey::from_der(&bytes)
77
+ .or_else(|_| {
78
+ RS256PublicKey::from_pem(
79
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
80
+ )
81
+ })
82
+ .map(JwtKey::Rs256)
83
+ .map_err(ItsiError::default)?),
84
+ JwtAlgorithm::Rs384 => Ok(RS384PublicKey::from_der(&bytes)
85
+ .or_else(|_| {
86
+ RS384PublicKey::from_pem(
87
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
88
+ )
89
+ })
90
+ .map(JwtKey::Rs384)
91
+ .map_err(ItsiError::default)?),
92
+ JwtAlgorithm::Rs512 => Ok(RS512PublicKey::from_der(&bytes)
93
+ .or_else(|_| {
94
+ RS512PublicKey::from_pem(
95
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
96
+ )
97
+ })
98
+ .map(JwtKey::Rs512)
99
+ .map_err(ItsiError::default)?),
100
+ JwtAlgorithm::Es256 => Ok(ES256PublicKey::from_der(&bytes)
101
+ .or_else(|_| {
102
+ ES256PublicKey::from_pem(
103
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
104
+ )
105
+ })
106
+ .map(JwtKey::Es256)
107
+ .map_err(ItsiError::default)?),
108
+ JwtAlgorithm::Es384 => Ok(ES384PublicKey::from_der(&bytes)
109
+ .or_else(|_| {
110
+ ES384PublicKey::from_pem(
111
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
112
+ )
113
+ })
114
+ .map(JwtKey::Es384)
115
+ .map_err(ItsiError::default)?),
116
+ JwtAlgorithm::Ps256 => Ok(PS256PublicKey::from_der(&bytes)
117
+ .or_else(|_| {
118
+ PS256PublicKey::from_pem(
119
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
120
+ )
121
+ })
122
+ .map(JwtKey::Ps256)
123
+ .map_err(ItsiError::default)?),
124
+ JwtAlgorithm::Ps384 => Ok(PS384PublicKey::from_der(&bytes)
125
+ .or_else(|_| {
126
+ PS384PublicKey::from_pem(
127
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
128
+ )
129
+ })
130
+ .map(JwtKey::Ps384)
131
+ .map_err(ItsiError::default)?),
132
+ JwtAlgorithm::Ps512 => Ok(PS512PublicKey::from_der(&bytes)
133
+ .or_else(|_| {
134
+ PS512PublicKey::from_pem(
135
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
136
+ )
137
+ })
138
+ .map(JwtKey::Ps512)
139
+ .map_err(ItsiError::default)?),
140
+ }
141
+ }
142
+ }
143
+
144
+ #[derive(Debug, Clone)]
145
+ pub enum JwtKey {
146
+ Hs256(HS256Key),
147
+ Hs384(HS384Key),
148
+ Hs512(HS512Key),
149
+ Rs256(RS256PublicKey),
150
+ Rs384(RS384PublicKey),
151
+ Rs512(RS512PublicKey),
152
+ Es256(ES256PublicKey),
153
+ Es384(ES384PublicKey),
154
+ Ps256(PS256PublicKey),
155
+ Ps384(PS384PublicKey),
156
+ Ps512(PS512PublicKey),
157
+ }
158
+
159
+ impl TryFrom<&str> for JwtAlgorithm {
160
+ type Error = itsi_error::ItsiError;
161
+
162
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
163
+ match value.to_ascii_lowercase().as_str() {
164
+ "hs256" => Ok(JwtAlgorithm::Hs256),
165
+ "hs384" => Ok(JwtAlgorithm::Hs384),
166
+ "hs512" => Ok(JwtAlgorithm::Hs512),
167
+ "rs256" => Ok(JwtAlgorithm::Rs256),
168
+ "rs384" => Ok(JwtAlgorithm::Rs384),
169
+ "rs512" => Ok(JwtAlgorithm::Rs512),
170
+ "es256" => Ok(JwtAlgorithm::Es256),
171
+ "es384" => Ok(JwtAlgorithm::Es384),
172
+ "ps256" => Ok(JwtAlgorithm::Ps256),
173
+ "ps384" => Ok(JwtAlgorithm::Ps384),
174
+ "ps512" => Ok(JwtAlgorithm::Ps512),
175
+ _ => Err(itsi_error::ItsiError::UnsupportedProtocol(
176
+ "Unsupported JWT Algorithm".to_string(),
177
+ )),
178
+ }
179
+ }
180
+ }
181
+
182
+ impl JwtKey {
183
+ pub fn verify(
184
+ &self,
185
+ token: &str,
186
+ ) -> std::result::Result<JWTClaims<claims::NoCustomClaims>, jwt_simple::Error> {
187
+ match self {
188
+ JwtKey::Hs256(key) => key.verify_token::<NoCustomClaims>(token, None),
189
+ JwtKey::Hs384(key) => key.verify_token::<NoCustomClaims>(token, None),
190
+ JwtKey::Hs512(key) => key.verify_token::<NoCustomClaims>(token, None),
191
+ JwtKey::Rs256(key) => key.verify_token::<NoCustomClaims>(token, None),
192
+ JwtKey::Rs384(key) => key.verify_token::<NoCustomClaims>(token, None),
193
+ JwtKey::Rs512(key) => key.verify_token::<NoCustomClaims>(token, None),
194
+ JwtKey::Es256(key) => key.verify_token::<NoCustomClaims>(token, None),
195
+ JwtKey::Es384(key) => key.verify_token::<NoCustomClaims>(token, None),
196
+ JwtKey::Ps256(key) => key.verify_token::<NoCustomClaims>(token, None),
197
+ JwtKey::Ps384(key) => key.verify_token::<NoCustomClaims>(token, None),
198
+ JwtKey::Ps512(key) => key.verify_token::<NoCustomClaims>(token, None),
199
+ }
200
+ }
201
+ }
202
+
203
+ #[async_trait]
204
+ impl MiddlewareLayer for AuthJwt {
205
+ async fn initialize(&self) -> Result<()> {
206
+ let keys: HashMap<JwtAlgorithm, Vec<JwtKey>> = self
207
+ .verifiers
208
+ .iter()
209
+ .map(|(algorithm, key_strings)| {
210
+ let algo = algorithm.clone();
211
+ let keys: Result<Vec<JwtKey>> = key_strings
212
+ .iter()
213
+ .map(|key_string| algorithm.key_from(key_string))
214
+ .collect();
215
+ keys.map(|keys| (algo, keys))
216
+ })
217
+ .collect::<Result<HashMap<JwtAlgorithm, Vec<JwtKey>>>>()?;
218
+ self.keys
219
+ .set(keys)
220
+ .map_err(|e| ItsiError::default(format!("Failed to set keys: {:?}", e)))?;
221
+ Ok(())
222
+ }
223
+
224
+ async fn before(
225
+ &self,
226
+ req: HttpRequest,
227
+ _context: &mut RequestContext,
228
+ ) -> Result<Either<HttpRequest, HttpResponse>> {
229
+ let token_str = match &self.token_source {
230
+ TokenSource::Header { name, prefix } => {
231
+ if let Some(header) = req.header(name) {
232
+ if let Some(prefix) = prefix {
233
+ Some(header.strip_prefix(prefix).unwrap_or("").trim_ascii())
234
+ } else {
235
+ Some(header.trim_ascii())
236
+ }
237
+ } else {
238
+ None
239
+ }
240
+ }
241
+ TokenSource::Query(query_name) => req.query_param(query_name),
242
+ };
243
+
244
+ if token_str.is_none() {
245
+ return Ok(Either::Right(
246
+ self.error_response.to_http_response(&req).await,
247
+ ));
248
+ }
249
+
250
+ let token_str = token_str.unwrap();
251
+ let token_meta = Token::decode_metadata(token_str);
252
+
253
+ if token_meta.is_err() {
254
+ return Ok(Either::Right(
255
+ self.error_response.to_http_response(&req).await,
256
+ ));
257
+ }
258
+ let token_meta: std::result::Result<JwtAlgorithm, ItsiError> =
259
+ token_meta.unwrap().algorithm().try_into();
260
+ if token_meta.is_err() {
261
+ return Ok(Either::Right(
262
+ self.error_response.to_http_response(&req).await,
263
+ ));
264
+ }
265
+ let algorithm = token_meta.unwrap();
266
+
267
+ if !self.verifiers.contains_key(&algorithm) {
268
+ return Ok(Either::Right(
269
+ self.error_response.to_http_response(&req).await,
270
+ ));
271
+ }
272
+
273
+ let keys = self.keys.get().unwrap().get(&algorithm).unwrap();
274
+
275
+ let verified_claims = keys.iter().find_map(|key| key.verify(token_str).ok());
276
+ if verified_claims.is_none() {
277
+ return Ok(Either::Right(
278
+ self.error_response.to_http_response(&req).await,
279
+ ));
280
+ }
281
+
282
+ let claims = verified_claims.unwrap();
283
+
284
+ if let Some(expected_audiences) = &self.audiences {
285
+ // The aud claim may be a string or an array.
286
+ if let Some(audiences) = &claims.audiences {
287
+ if !audiences.contains(expected_audiences) {
288
+ return Ok(Either::Right(
289
+ self.error_response.to_http_response(&req).await,
290
+ ));
291
+ }
292
+ }
293
+ }
294
+
295
+ if let Some(expected_subjects) = &self.subjects {
296
+ // The aud claim may be a string or an array.
297
+ if let Some(subject) = &claims.subject {
298
+ if !expected_subjects.contains(subject) {
299
+ return Ok(Either::Right(
300
+ self.error_response.to_http_response(&req).await,
301
+ ));
302
+ }
303
+ }
304
+ }
305
+
306
+ if let Some(expected_issuers) = &self.issuers {
307
+ // The aud claim may be a string or an array.
308
+ if let Some(issuer) = &claims.issuer {
309
+ if !expected_issuers.contains(issuer) {
310
+ return Ok(Either::Right(
311
+ self.error_response.to_http_response(&req).await,
312
+ ));
313
+ }
314
+ }
315
+ }
316
+
317
+ Ok(Either::Left(req))
318
+ }
319
+ }
320
+
321
+ impl FromValue for AuthJwt {}
@@ -0,0 +1,139 @@
1
+ use super::{FromValue, MiddlewareLayer};
2
+ use crate::server::{itsi_service::RequestContext, types::HttpResponse};
3
+ use async_trait::async_trait;
4
+ use http::{HeaderName, HeaderValue};
5
+ use magnus::error::Result;
6
+ use serde::Deserialize;
7
+ use std::{collections::HashMap, sync::OnceLock};
8
+
9
+ #[derive(Debug, Clone, Deserialize)]
10
+ pub struct CacheControl {
11
+ #[serde(default)]
12
+ pub max_age: Option<u64>,
13
+ #[serde(default)]
14
+ pub s_max_age: Option<u64>,
15
+ #[serde(default)]
16
+ pub stale_while_revalidate: Option<u64>,
17
+ #[serde(default)]
18
+ pub stale_if_error: Option<u64>,
19
+ #[serde(default)]
20
+ pub public: bool,
21
+ #[serde(default)]
22
+ pub private: bool,
23
+ #[serde(default)]
24
+ pub no_cache: bool,
25
+ #[serde(default)]
26
+ pub no_store: bool,
27
+ #[serde(default)]
28
+ pub must_revalidate: bool,
29
+ #[serde(default)]
30
+ pub proxy_revalidate: bool,
31
+ #[serde(default)]
32
+ pub immutable: bool,
33
+ #[serde(default)]
34
+ pub vary: Vec<String>,
35
+ #[serde(default)]
36
+ pub additional_headers: HashMap<String, String>,
37
+ #[serde(skip_deserializing)]
38
+ pub cache_control_str: OnceLock<String>,
39
+ }
40
+
41
+ #[async_trait]
42
+ impl MiddlewareLayer for CacheControl {
43
+ async fn initialize(&self) -> Result<()> {
44
+ let mut directives = Vec::new();
45
+
46
+ if self.public && !self.private {
47
+ directives.push("public".to_owned());
48
+ } else if self.private && !self.public {
49
+ directives.push("private".to_owned());
50
+ }
51
+ if self.no_cache {
52
+ directives.push("no-cache".to_owned());
53
+ }
54
+ if self.no_store {
55
+ directives.push("no-store".to_owned());
56
+ }
57
+ if self.must_revalidate {
58
+ directives.push("must-revalidate".to_owned());
59
+ }
60
+ if self.proxy_revalidate {
61
+ directives.push("proxy-revalidate".to_owned());
62
+ }
63
+ if self.immutable {
64
+ directives.push("immutable".to_owned());
65
+ }
66
+
67
+ // Add age parameters
68
+ if let Some(max_age) = self.max_age {
69
+ directives.push(format!("max-age={}", max_age));
70
+ }
71
+
72
+ if let Some(s_max_age) = self.s_max_age {
73
+ directives.push(format!("s-maxage={}", s_max_age));
74
+ }
75
+
76
+ if let Some(stale_while_revalidate) = self.stale_while_revalidate {
77
+ directives.push(format!("stale-while-revalidate={}", stale_while_revalidate));
78
+ }
79
+
80
+ if let Some(stale_if_error) = self.stale_if_error {
81
+ directives.push(format!("stale-if-error={}", stale_if_error));
82
+ }
83
+
84
+ // Set the Cache-Control header if we have directives
85
+ if !directives.is_empty() {
86
+ let cache_control_value = directives.join(", ");
87
+ self.cache_control_str.set(cache_control_value).unwrap();
88
+ }
89
+
90
+ Ok(())
91
+ }
92
+
93
+ async fn after(&self, mut resp: HttpResponse, _: &mut RequestContext) -> HttpResponse {
94
+ // Skip for statuses where caching doesn't make sense
95
+ let status = resp.status().as_u16();
96
+ if matches!(status, 401 | 403 | 500..=599) {
97
+ return resp;
98
+ }
99
+
100
+ // Set the Cache-Control header if we have directives
101
+ if let Some(cache_control_value) = self.cache_control_str.get() {
102
+ if let Ok(value) = HeaderValue::from_str(cache_control_value) {
103
+ resp.headers_mut().insert("Cache-Control", value);
104
+ }
105
+ }
106
+
107
+ // Set Expires header based on max-age if present
108
+ if let Some(max_age) = self.max_age {
109
+ // Set the Expires header based on max-age
110
+ // Use a helper to format the HTTP date correctly
111
+ let expires = chrono::Utc::now() + chrono::Duration::seconds(max_age as i64);
112
+ let expires_str = expires.format("%a, %d %b %Y %H:%M:%S GMT").to_string();
113
+ if let Ok(value) = HeaderValue::from_str(&expires_str) {
114
+ resp.headers_mut().insert("Expires", value);
115
+ }
116
+ }
117
+
118
+ // Set Vary header
119
+ if !self.vary.is_empty() {
120
+ let vary_value = self.vary.join(", ");
121
+ if let Ok(value) = HeaderValue::from_str(&vary_value) {
122
+ resp.headers_mut().insert("Vary", value);
123
+ }
124
+ }
125
+
126
+ // Set additional custom headers
127
+ for (name, value) in &self.additional_headers {
128
+ if let Ok(header_value) = HeaderValue::from_str(value) {
129
+ if let Ok(header_name) = name.parse::<HeaderName>() {
130
+ resp.headers_mut().insert(header_name, header_value);
131
+ }
132
+ }
133
+ }
134
+
135
+ resp
136
+ }
137
+ }
138
+
139
+ impl FromValue for CacheControl {}