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
@@ -1,4 +1,5 @@
1
- use super::big_bytes::BigBytes;
1
+ pub mod big_bytes;
2
+ use big_bytes::BigBytes;
2
3
  use bytes::Bytes;
3
4
  use futures::executor::block_on;
4
5
  use http_body_util::{BodyDataStream, BodyExt};
@@ -25,10 +26,10 @@ pub enum ItsiBody {
25
26
  }
26
27
 
27
28
  impl ItsiBody {
28
- pub fn into_value(&self) -> Value {
29
+ pub fn into_value(&self) -> Option<Value> {
29
30
  match self {
30
31
  ItsiBody::Buffered(bytes) => bytes.as_value(),
31
- ItsiBody::Stream(proxy) => proxy.clone().into_value(),
32
+ ItsiBody::Stream(proxy) => Some(proxy.clone().into_value()),
32
33
  }
33
34
  }
34
35
  }
@@ -98,6 +99,24 @@ impl ItsiBodyProxy {
98
99
  Ok(Some(output_string))
99
100
  }
100
101
 
102
+ pub fn to_bytes(&self) -> MagnusResult<Vec<u8>> {
103
+ self.verify_open()?;
104
+ let mut stream = self.incoming.lock();
105
+ let mut buf = self.buf.lock();
106
+
107
+ while let Some(chunk) = block_on(stream.next()) {
108
+ let chunk = chunk.map_err(|err| {
109
+ magnus::Error::new(
110
+ magnus::exception::exception(),
111
+ format!("Error reading body {:?}", err),
112
+ )
113
+ })?;
114
+ buf.extend_from_slice(&chunk);
115
+ }
116
+
117
+ Ok(buf.clone())
118
+ }
119
+
101
120
  /// Equivalent to calling gets and yielding it, until we reach EOF
102
121
  pub fn each(ruby: &Ruby, rbself: &Self) -> MagnusResult<()> {
103
122
  let proc = ruby.block_proc()?;
@@ -0,0 +1,147 @@
1
+ use derive_more::Debug;
2
+ use http::{request::Parts, Response, StatusCode};
3
+ use http_body_util::{combinators::BoxBody, BodyExt, Empty};
4
+ use itsi_error::from::CLIENT_CONNECTION_CLOSED;
5
+ use itsi_rb_helpers::{print_rb_backtrace, HeapValue};
6
+ use itsi_tracing::debug;
7
+ use magnus::{
8
+ block::Proc,
9
+ error::{ErrorType, Result as MagnusResult},
10
+ Error,
11
+ };
12
+ use magnus::{
13
+ value::{LazyId, ReprValue},
14
+ Ruby, Value,
15
+ };
16
+ use std::{sync::Arc, time::Instant};
17
+ use tokio::sync::mpsc::{self};
18
+ use tracing::error;
19
+
20
+ use super::itsi_grpc_stream::ItsiGrpcStream;
21
+ use crate::server::{
22
+ byte_frame::ByteFrame,
23
+ itsi_service::RequestContext,
24
+ request_job::RequestJob,
25
+ types::{HttpRequest, HttpResponse},
26
+ };
27
+
28
+ static ID_MESSAGE: LazyId = LazyId::new("message");
29
+
30
+ #[derive(Debug)]
31
+ #[magnus::wrap(class = "Itsi::GrpcRequest", free_immediately, size)]
32
+ pub struct ItsiGrpcRequest {
33
+ pub parts: Parts,
34
+ pub start: Instant,
35
+ #[debug(skip)]
36
+ pub context: RequestContext,
37
+ #[debug(skip)]
38
+ pub stream: ItsiGrpcStream,
39
+ }
40
+
41
+ impl ItsiGrpcRequest {
42
+ pub fn service_name(&self) -> MagnusResult<String> {
43
+ let path = self.parts.uri.path();
44
+ Ok(path.split('/').nth_back(1).unwrap().to_string())
45
+ }
46
+
47
+ pub fn method_name(&self) -> MagnusResult<String> {
48
+ let path = self.parts.uri.path();
49
+ Ok(path.split('/').nth_back(0).unwrap().to_string())
50
+ }
51
+
52
+ pub fn stream(&self) -> MagnusResult<ItsiGrpcStream> {
53
+ Ok(self.stream.clone())
54
+ }
55
+
56
+ pub fn content_type_str(&self) -> &str {
57
+ self.parts
58
+ .headers
59
+ .get("Content-Type")
60
+ .and_then(|hv| hv.to_str().ok())
61
+ .unwrap_or("application/x-www-form-urlencoded")
62
+ }
63
+
64
+ pub fn is_json(&self) -> bool {
65
+ self.content_type_str() == "application/json"
66
+ }
67
+
68
+ pub fn process(self, ruby: &Ruby, app_proc: Arc<HeapValue<Proc>>) -> magnus::error::Result<()> {
69
+ let response = self.stream.clone();
70
+ let result = app_proc.call::<_, Value>((self,));
71
+ if let Err(err) = result {
72
+ Self::internal_error(ruby, response, err);
73
+ }
74
+ Ok(())
75
+ }
76
+
77
+ pub fn internal_error(_ruby: &Ruby, stream: ItsiGrpcStream, err: Error) {
78
+ if let Some(rb_err) = err.value() {
79
+ print_rb_backtrace(rb_err);
80
+ stream.internal_server_error(err.to_string());
81
+ } else {
82
+ stream.internal_server_error(err.to_string());
83
+ }
84
+ }
85
+
86
+ pub(crate) async fn process_request(
87
+ app: Arc<HeapValue<Proc>>,
88
+ hyper_request: HttpRequest,
89
+ context: &RequestContext,
90
+ ) -> itsi_error::Result<HttpResponse> {
91
+ let (request, mut receiver) = ItsiGrpcRequest::new(hyper_request, context).await;
92
+ let shutdown_channel = context.service.shutdown_channel.clone();
93
+ let response_stream = request.stream.clone();
94
+ match context
95
+ .sender
96
+ .send(RequestJob::ProcessGrpcRequest(request, app))
97
+ .await
98
+ {
99
+ Err(err) => {
100
+ error!("Error occurred: {}", err);
101
+ let mut response = Response::new(BoxBody::new(Empty::new()));
102
+ *response.status_mut() = StatusCode::BAD_REQUEST;
103
+ Ok(response)
104
+ }
105
+ _ => match receiver.recv().await {
106
+ Some(first_frame) => Ok(response_stream
107
+ .build_response(first_frame, receiver, shutdown_channel)
108
+ .await),
109
+ None => Ok(Response::new(BoxBody::new(Empty::new()))),
110
+ },
111
+ }
112
+ }
113
+ pub fn is_connection_closed_err(ruby: &Ruby, err: &Error) -> bool {
114
+ match err.error_type() {
115
+ ErrorType::Jump(_) => false,
116
+ ErrorType::Error(_, _) => false,
117
+ ErrorType::Exception(exception) => {
118
+ exception.is_kind_of(ruby.exception_eof_error())
119
+ && err
120
+ .value()
121
+ .map(|v| {
122
+ v.funcall::<_, _, String>(*ID_MESSAGE, ())
123
+ .unwrap_or("".to_string())
124
+ .eq(CLIENT_CONNECTION_CLOSED)
125
+ })
126
+ .unwrap_or(false)
127
+ }
128
+ }
129
+ }
130
+
131
+ pub(crate) async fn new(
132
+ request: HttpRequest,
133
+ context: &RequestContext,
134
+ ) -> (ItsiGrpcRequest, mpsc::Receiver<ByteFrame>) {
135
+ let (parts, body) = request.into_parts();
136
+ let response_channel = mpsc::channel::<ByteFrame>(100);
137
+ (
138
+ Self {
139
+ context: context.clone(),
140
+ start: Instant::now(),
141
+ parts,
142
+ stream: ItsiGrpcStream::new(response_channel.0, body.into_data_stream()),
143
+ },
144
+ response_channel.1,
145
+ )
146
+ }
147
+ }
@@ -0,0 +1,19 @@
1
+ use derive_more::Debug;
2
+ use http::request::Parts;
3
+ use tokio::sync::mpsc::Sender;
4
+
5
+ use crate::server::byte_frame::ByteFrame;
6
+
7
+ #[derive(Debug, Clone)]
8
+ #[magnus::wrap(class = "Itsi::GrpcResponse", free_immediately, size)]
9
+ pub struct ItsiGrpcResponse {
10
+ pub parts: Parts,
11
+ #[debug(skip)]
12
+ pub sender: Sender<ByteFrame>,
13
+ }
14
+
15
+ impl ItsiGrpcResponse {
16
+ pub fn new(parts: Parts, sender: Sender<ByteFrame>) -> Self {
17
+ Self { parts, sender }
18
+ }
19
+ }
@@ -0,0 +1,216 @@
1
+ use std::{collections::HashMap, sync::Arc};
2
+
3
+ use crate::server::{
4
+ byte_frame::ByteFrame, serve_strategy::single_mode::RunningPhase, types::HttpResponse,
5
+ };
6
+ use bytes::Bytes;
7
+ use derive_more::Debug;
8
+ use futures::{executor::block_on, stream::unfold};
9
+ use http::{
10
+ header::{HeaderName, HeaderValue, CONTENT_TYPE},
11
+ HeaderMap, Response,
12
+ };
13
+ use http_body_util::{combinators::BoxBody, BodyDataStream, BodyExt, Empty, Full, StreamBody};
14
+ use hyper::body::{Frame, Incoming};
15
+ use magnus::error::Result as MagnusResult;
16
+ use parking_lot::Mutex;
17
+ use tokio::sync::{
18
+ mpsc::{Receiver, Sender},
19
+ oneshot, watch,
20
+ };
21
+ use tokio_stream::{wrappers::ReceiverStream, StreamExt};
22
+ use tracing::{error, info, warn};
23
+
24
+ #[derive(Debug, Clone)]
25
+ #[magnus::wrap(class = "Itsi::GrpcStream", free_immediately, size)]
26
+ pub struct ItsiGrpcStream {
27
+ pub inner: Arc<Mutex<ItsiGrpcStreamInner>>,
28
+ }
29
+
30
+ #[derive(Debug)]
31
+ pub struct ItsiGrpcStreamInner {
32
+ pub body: BodyDataStream<Incoming>,
33
+ pub buf: Vec<u8>,
34
+ pub response_sender: Sender<ByteFrame>,
35
+ pub response: Option<HttpResponse>,
36
+ trailer_tx: oneshot::Sender<HeaderMap>,
37
+ trailer_rx: Option<oneshot::Receiver<HeaderMap>>,
38
+ }
39
+
40
+ impl ItsiGrpcStreamInner {
41
+ pub fn read(&mut self, bytes: usize) -> MagnusResult<Bytes> {
42
+ let stream = &mut self.body;
43
+ let buf = &mut self.buf;
44
+ let mut result = Vec::with_capacity(bytes);
45
+
46
+ info!("Entering read with {:?}. Current buf is {:?}", bytes, buf);
47
+
48
+ // First, use any data already in the buffer
49
+ if !buf.is_empty() {
50
+ let remaining = bytes.min(buf.len());
51
+ result.extend_from_slice(&buf[..remaining]);
52
+ buf.drain(..remaining);
53
+ }
54
+
55
+ while result.len() < bytes {
56
+ if let Some(chunk) = block_on(stream.next()) {
57
+ let chunk = chunk.map_err(|err| {
58
+ magnus::Error::new(
59
+ magnus::exception::exception(),
60
+ format!("Error reading body {:?}", err),
61
+ )
62
+ })?;
63
+ let remaining = bytes - result.len();
64
+ if chunk.len() > remaining {
65
+ result.extend_from_slice(&chunk[..remaining]);
66
+ buf.extend_from_slice(&chunk[remaining..]);
67
+ } else {
68
+ result.extend_from_slice(&chunk);
69
+ }
70
+ } else {
71
+ break;
72
+ }
73
+ }
74
+
75
+ Ok(result.into())
76
+ }
77
+
78
+ pub fn write(&mut self, bytes: Bytes) -> MagnusResult<()> {
79
+ self.response_sender
80
+ .blocking_send(ByteFrame::Data(bytes))
81
+ .map_err(|err| {
82
+ magnus::Error::new(
83
+ magnus::exception::exception(),
84
+ format!("Error writing body {:?}", err),
85
+ )
86
+ })?;
87
+ Ok(())
88
+ }
89
+
90
+ pub fn flush(&mut self) -> MagnusResult<()> {
91
+ Ok(())
92
+ }
93
+
94
+ pub fn send_trailers(&mut self, trailers: HashMap<String, String>) -> MagnusResult<()> {
95
+ let mut header_map = HeaderMap::new();
96
+ for (key, value) in trailers {
97
+ if let (Ok(hn), Ok(hv)) = (key.parse::<HeaderName>(), value.parse::<HeaderValue>()) {
98
+ header_map.insert(hn, hv);
99
+ }
100
+ }
101
+ let trailer_tx = std::mem::replace(&mut self.trailer_tx, oneshot::channel().0);
102
+ trailer_tx.send(header_map).map_err(|err| {
103
+ magnus::Error::new(
104
+ magnus::exception::exception(),
105
+ format!("Error sending trailers {:?}", err),
106
+ )
107
+ })?;
108
+ self.response_sender
109
+ .blocking_send(ByteFrame::Empty)
110
+ .map_err(|err| {
111
+ magnus::Error::new(
112
+ magnus::exception::exception(),
113
+ format!("Error flushing {:?}", err),
114
+ )
115
+ })?;
116
+ Ok(())
117
+ }
118
+ }
119
+
120
+ impl ItsiGrpcStream {
121
+ pub fn new(response_sender: Sender<ByteFrame>, body: BodyDataStream<Incoming>) -> Self {
122
+ let (trailer_tx, trailer_rx) = oneshot::channel::<HeaderMap>();
123
+ ItsiGrpcStream {
124
+ inner: Arc::new(Mutex::new(ItsiGrpcStreamInner {
125
+ body,
126
+ buf: Vec::new(),
127
+ response_sender,
128
+ response: Some(Response::new(BoxBody::new(Empty::new()))),
129
+ trailer_tx,
130
+ trailer_rx: Some(trailer_rx),
131
+ })),
132
+ }
133
+ }
134
+
135
+ pub fn read(&self, bytes: usize) -> MagnusResult<Bytes> {
136
+ self.inner.lock().read(bytes)
137
+ }
138
+
139
+ pub fn write(&self, bytes: Bytes) -> MagnusResult<()> {
140
+ self.inner.lock().write(bytes)
141
+ }
142
+
143
+ pub fn flush(&self) -> MagnusResult<()> {
144
+ self.inner.lock().flush()
145
+ }
146
+
147
+ pub fn send_trailers(&self, trailers: HashMap<String, String>) -> MagnusResult<()> {
148
+ self.inner.lock().send_trailers(trailers)
149
+ }
150
+
151
+ pub async fn build_response(
152
+ &self,
153
+ first_frame: ByteFrame,
154
+ receiver: Receiver<ByteFrame>,
155
+ shutdown_rx: watch::Receiver<RunningPhase>,
156
+ ) -> HttpResponse {
157
+ let mut response = self.inner.lock().response.take().unwrap();
158
+ let rx = self.inner.lock().trailer_rx.take().unwrap();
159
+ response
160
+ .headers_mut()
161
+ .append(CONTENT_TYPE, "application/grpc".parse().unwrap());
162
+ *response.body_mut() = if matches!(first_frame, ByteFrame::Empty) {
163
+ BoxBody::new(Empty::new())
164
+ } else if matches!(first_frame, ByteFrame::End(_)) {
165
+ BoxBody::new(Full::new(first_frame.into()))
166
+ } else {
167
+ let initial_frame = tokio_stream::once(Ok(Frame::data(Bytes::from(first_frame))));
168
+ let frame_stream = unfold(
169
+ (ReceiverStream::new(receiver), shutdown_rx),
170
+ |(mut receiver, mut shutdown_rx)| async move {
171
+ if let RunningPhase::ShutdownPending = *shutdown_rx.borrow() {
172
+ return None;
173
+ }
174
+ loop {
175
+ tokio::select! {
176
+ maybe_bytes = receiver.next() => {
177
+ match maybe_bytes {
178
+ Some(ByteFrame::Data(bytes)) | Some(ByteFrame::End(bytes)) => {
179
+ return Some((Ok(Frame::data(bytes)), (receiver, shutdown_rx)));
180
+ }
181
+ _ => {
182
+ return None;
183
+ }
184
+ }
185
+ },
186
+ _ = shutdown_rx.changed() => {
187
+ match *shutdown_rx.borrow() {
188
+ RunningPhase::ShutdownPending => {
189
+ warn!("Disconnecting streaming client.");
190
+ return None;
191
+ },
192
+ _ => continue,
193
+ }
194
+ }
195
+ }
196
+ }
197
+ },
198
+ );
199
+
200
+ let combined_stream = initial_frame.chain(frame_stream);
201
+ BoxBody::new(StreamBody::new(combined_stream))
202
+ }
203
+ .with_trailers(async move {
204
+ match rx.await {
205
+ Ok(trailers) => Some(Ok(trailers)),
206
+ Err(_err) => None,
207
+ }
208
+ })
209
+ .boxed();
210
+ response
211
+ }
212
+
213
+ pub fn internal_server_error(&self, message: String) {
214
+ error!(message);
215
+ }
216
+ }
@@ -0,0 +1,282 @@
1
+ use derive_more::Debug;
2
+ use futures::StreamExt;
3
+ use http::{request::Parts, Response, StatusCode, Version};
4
+ use http_body_util::{combinators::BoxBody, BodyExt, Empty};
5
+ use itsi_error::from::CLIENT_CONNECTION_CLOSED;
6
+ use itsi_rb_helpers::{print_rb_backtrace, HeapValue};
7
+ use itsi_tracing::{debug, error};
8
+ use magnus::{
9
+ block::Proc,
10
+ error::{ErrorType, Result as MagnusResult},
11
+ Error,
12
+ };
13
+ use magnus::{
14
+ value::{LazyId, ReprValue},
15
+ Ruby, Value,
16
+ };
17
+ use std::{fmt, io::Write, sync::Arc, time::Instant};
18
+ use tokio::sync::mpsc::{self};
19
+
20
+ use super::{
21
+ itsi_body_proxy::{big_bytes::BigBytes, ItsiBody, ItsiBodyProxy},
22
+ itsi_http_response::ItsiHttpResponse,
23
+ };
24
+ use crate::server::{
25
+ byte_frame::ByteFrame,
26
+ itsi_service::RequestContext,
27
+ request_job::RequestJob,
28
+ types::{HttpRequest, HttpResponse},
29
+ };
30
+
31
+ static ID_MESSAGE: LazyId = LazyId::new("message");
32
+
33
+ #[derive(Debug)]
34
+ #[magnus::wrap(class = "Itsi::HttpRequest", free_immediately, size)]
35
+ pub struct ItsiHttpRequest {
36
+ pub parts: Parts,
37
+ #[debug(skip)]
38
+ pub body: ItsiBody,
39
+ pub version: Version,
40
+ pub response: ItsiHttpResponse,
41
+ pub start: Instant,
42
+ #[debug(skip)]
43
+ pub context: RequestContext,
44
+ }
45
+
46
+ impl fmt::Display for ItsiHttpRequest {
47
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48
+ write!(
49
+ f,
50
+ "{} {} {}",
51
+ self.version().unwrap(),
52
+ self.method().unwrap(),
53
+ self.path().unwrap()
54
+ )
55
+ }
56
+ }
57
+
58
+ impl ItsiHttpRequest {
59
+ pub fn is_connection_closed_err(ruby: &Ruby, err: &Error) -> bool {
60
+ match err.error_type() {
61
+ ErrorType::Jump(_) => false,
62
+ ErrorType::Error(_, _) => false,
63
+ ErrorType::Exception(exception) => {
64
+ exception.is_kind_of(ruby.exception_eof_error())
65
+ && err
66
+ .value()
67
+ .map(|v| {
68
+ v.funcall::<_, _, String>(*ID_MESSAGE, ())
69
+ .unwrap_or("".to_string())
70
+ .eq(CLIENT_CONNECTION_CLOSED)
71
+ })
72
+ .unwrap_or(false)
73
+ }
74
+ }
75
+ }
76
+ fn content_type_str(&self) -> &str {
77
+ self.parts
78
+ .headers
79
+ .get("Content-Type")
80
+ .and_then(|hv| hv.to_str().ok())
81
+ .unwrap_or("application/x-www-form-urlencoded")
82
+ }
83
+
84
+ pub fn is_json(&self) -> bool {
85
+ self.content_type_str() == "application/json"
86
+ }
87
+
88
+ pub fn is_html(&self) -> bool {
89
+ self.content_type_str() == "text/html"
90
+ }
91
+
92
+ pub fn process(self, ruby: &Ruby, app_proc: Arc<HeapValue<Proc>>) -> magnus::error::Result<()> {
93
+ let response = self.response.clone();
94
+ let result = app_proc.call::<_, Value>((self,));
95
+ if let Err(err) = result {
96
+ Self::internal_error(ruby, response, err);
97
+ }
98
+ Ok(())
99
+ }
100
+
101
+ pub fn internal_error(ruby: &Ruby, response: ItsiHttpResponse, err: Error) {
102
+ if Self::is_connection_closed_err(ruby, &err) {
103
+ debug!("Connection closed by client");
104
+ response.close();
105
+ } else if let Some(rb_err) = err.value() {
106
+ print_rb_backtrace(rb_err);
107
+ response.internal_server_error(err.to_string());
108
+ } else {
109
+ response.internal_server_error(err.to_string());
110
+ }
111
+ }
112
+
113
+ pub fn error(self, message: String) {
114
+ self.response.internal_server_error(message);
115
+ }
116
+
117
+ pub(crate) async fn process_request(
118
+ app: Arc<HeapValue<Proc>>,
119
+ hyper_request: HttpRequest,
120
+ context: &RequestContext,
121
+ ) -> itsi_error::Result<HttpResponse> {
122
+ let (request, mut receiver) = ItsiHttpRequest::new(hyper_request, context).await;
123
+ let shutdown_channel = context.service.shutdown_channel.clone();
124
+ let response = request.response.clone();
125
+ match context
126
+ .sender
127
+ .send(RequestJob::ProcessHttpRequest(request, app))
128
+ .await
129
+ {
130
+ Err(err) => {
131
+ error!("Error occurred: {}", err);
132
+ let mut response = Response::new(BoxBody::new(Empty::new()));
133
+ *response.status_mut() = StatusCode::BAD_REQUEST;
134
+ Ok(response)
135
+ }
136
+ _ => match receiver.recv().await {
137
+ Some(first_frame) => Ok(response
138
+ .build(first_frame, receiver, shutdown_channel)
139
+ .await),
140
+ None => Ok(response
141
+ .build(ByteFrame::Empty, receiver, shutdown_channel)
142
+ .await),
143
+ },
144
+ }
145
+ }
146
+
147
+ pub(crate) async fn new(
148
+ request: HttpRequest,
149
+ context: &RequestContext,
150
+ ) -> (ItsiHttpRequest, mpsc::Receiver<ByteFrame>) {
151
+ let (parts, body) = request.into_parts();
152
+ let body = if context.server_params.streamable_body {
153
+ ItsiBody::Stream(ItsiBodyProxy::new(body))
154
+ } else {
155
+ let mut body_bytes = BigBytes::new();
156
+ let mut stream = body.into_data_stream();
157
+ while let Some(chunk) = stream.next().await {
158
+ let byte_array = chunk.unwrap().to_vec();
159
+ body_bytes.write_all(&byte_array).unwrap();
160
+ }
161
+ ItsiBody::Buffered(body_bytes)
162
+ };
163
+ let response_channel = mpsc::channel::<ByteFrame>(100);
164
+ (
165
+ Self {
166
+ context: context.clone(),
167
+ version: parts.version,
168
+ response: ItsiHttpResponse::new(parts.clone(), response_channel.0),
169
+ start: Instant::now(),
170
+ body,
171
+ parts,
172
+ },
173
+ response_channel.1,
174
+ )
175
+ }
176
+
177
+ pub(crate) fn path(&self) -> MagnusResult<&str> {
178
+ Ok(self
179
+ .parts
180
+ .uri
181
+ .path()
182
+ .strip_prefix(&self.context.server_params.script_name)
183
+ .unwrap_or(self.parts.uri.path()))
184
+ }
185
+
186
+ pub(crate) fn script_name(&self) -> MagnusResult<&str> {
187
+ Ok(&self.context.server_params.script_name)
188
+ }
189
+
190
+ pub(crate) fn query_string(&self) -> MagnusResult<&str> {
191
+ Ok(self.parts.uri.query().unwrap_or(""))
192
+ }
193
+
194
+ pub(crate) fn method(&self) -> MagnusResult<&str> {
195
+ Ok(self.parts.method.as_str())
196
+ }
197
+
198
+ pub(crate) fn version(&self) -> MagnusResult<&str> {
199
+ Ok(match self.version {
200
+ Version::HTTP_09 => "HTTP/0.9",
201
+ Version::HTTP_10 => "HTTP/1.0",
202
+ Version::HTTP_11 => "HTTP/1.1",
203
+ Version::HTTP_2 => "HTTP/2.0",
204
+ Version::HTTP_3 => "HTTP/3.0",
205
+ _ => "HTTP/Unknown",
206
+ })
207
+ }
208
+
209
+ pub(crate) fn rack_protocol(&self) -> MagnusResult<Vec<&str>> {
210
+ Ok(self
211
+ .parts
212
+ .headers
213
+ .get("upgrade")
214
+ .or_else(|| self.parts.headers.get("protocol"))
215
+ .map(|value| {
216
+ value
217
+ .to_str()
218
+ .unwrap_or("")
219
+ .split(',')
220
+ .map(|s| s.trim())
221
+ .collect::<Vec<&str>>()
222
+ })
223
+ .unwrap_or_else(|| vec!["http"]))
224
+ }
225
+
226
+ pub(crate) fn host(&self) -> MagnusResult<&str> {
227
+ Ok(self
228
+ .parts
229
+ .uri
230
+ .host()
231
+ .unwrap_or_else(|| &self.context.listener.host))
232
+ }
233
+
234
+ pub(crate) fn scheme(&self) -> MagnusResult<&str> {
235
+ Ok(self
236
+ .parts
237
+ .uri
238
+ .scheme()
239
+ .map(|scheme| scheme.as_str())
240
+ .unwrap_or_else(|| &self.context.listener.scheme))
241
+ }
242
+
243
+ pub(crate) fn headers(&self) -> MagnusResult<Vec<(&str, &str)>> {
244
+ Ok(self
245
+ .parts
246
+ .headers
247
+ .iter()
248
+ .map(|(hn, hv)| (hn.as_str(), hv.to_str().unwrap_or("")))
249
+ .collect::<Vec<(&str, &str)>>())
250
+ }
251
+
252
+ pub fn header(&self, name: String) -> MagnusResult<Option<Vec<&str>>> {
253
+ let result: Vec<&str> = self
254
+ .parts
255
+ .headers
256
+ .get_all(&name)
257
+ .iter()
258
+ .filter_map(|value| value.to_str().ok())
259
+ .collect();
260
+ Ok(Some(result))
261
+ }
262
+
263
+ pub(crate) fn remote_addr(&self) -> MagnusResult<&str> {
264
+ Ok(&self.context.addr)
265
+ }
266
+
267
+ pub(crate) fn port(&self) -> MagnusResult<u16> {
268
+ Ok(self
269
+ .parts
270
+ .uri
271
+ .port_u16()
272
+ .unwrap_or(self.context.listener.port))
273
+ }
274
+
275
+ pub(crate) fn body(&self) -> MagnusResult<Option<Value>> {
276
+ Ok(self.body.into_value())
277
+ }
278
+
279
+ pub(crate) fn response(&self) -> MagnusResult<ItsiHttpResponse> {
280
+ Ok(self.response.clone())
281
+ }
282
+ }