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
@@ -1,58 +1,49 @@
1
- use crate::{
2
- body_proxy::{
3
- big_bytes::BigBytes,
4
- itsi_body_proxy::{ItsiBody, ItsiBodyProxy},
5
- },
6
- response::itsi_response::ItsiResponse,
7
- server::{
8
- itsi_server::{RequestJob, Server},
9
- listener::{ListenerInfo, SockAddr},
10
- serve_strategy::single_mode::RunningPhase,
11
- },
12
- };
13
- use bytes::Bytes;
14
1
  use derive_more::Debug;
15
2
  use futures::StreamExt;
16
- use http::{request::Parts, HeaderValue, Response, StatusCode};
3
+ use http::{request::Parts, Response, StatusCode, Version};
17
4
  use http_body_util::{combinators::BoxBody, BodyExt, Empty};
18
- use hyper::{body::Incoming, Request};
19
5
  use itsi_error::from::CLIENT_CONNECTION_CLOSED;
20
- use itsi_rb_helpers::print_rb_backtrace;
6
+ use itsi_rb_helpers::{print_rb_backtrace, HeapValue};
21
7
  use itsi_tracing::{debug, error};
22
8
  use magnus::{
9
+ block::Proc,
23
10
  error::{ErrorType, Result as MagnusResult},
24
11
  Error,
25
12
  };
26
13
  use magnus::{
27
- value::{LazyId, Opaque, ReprValue},
28
- RClass, Ruby, Value,
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,
29
23
  };
30
- use std::{convert::Infallible, fmt, io::Write, sync::Arc, time::Instant};
31
- use tokio::sync::{
32
- mpsc::{self},
33
- watch,
24
+ use crate::server::{
25
+ byte_frame::ByteFrame,
26
+ itsi_service::RequestContext,
27
+ request_job::RequestJob,
28
+ types::{HttpRequest, HttpResponse},
34
29
  };
35
- static ID_CALL: LazyId = LazyId::new("call");
30
+
36
31
  static ID_MESSAGE: LazyId = LazyId::new("message");
37
32
 
38
33
  #[derive(Debug)]
39
- #[magnus::wrap(class = "Itsi::Request", free_immediately, size)]
40
- pub struct ItsiRequest {
34
+ #[magnus::wrap(class = "Itsi::HttpRequest", free_immediately, size)]
35
+ pub struct ItsiHttpRequest {
41
36
  pub parts: Parts,
42
37
  #[debug(skip)]
43
38
  pub body: ItsiBody,
44
- pub remote_addr: String,
45
- pub version: String,
46
- #[debug(skip)]
47
- pub(crate) listener: Arc<ListenerInfo>,
48
- #[debug(skip)]
49
- pub server: Arc<Server>,
50
- pub response: ItsiResponse,
39
+ pub version: Version,
40
+ pub response: ItsiHttpResponse,
51
41
  pub start: Instant,
52
- pub content_type: String,
42
+ #[debug(skip)]
43
+ pub context: RequestContext,
53
44
  }
54
45
 
55
- impl fmt::Display for ItsiRequest {
46
+ impl fmt::Display for ItsiHttpRequest {
56
47
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57
48
  write!(
58
49
  f,
@@ -64,7 +55,7 @@ impl fmt::Display for ItsiRequest {
64
55
  }
65
56
  }
66
57
 
67
- impl ItsiRequest {
58
+ impl ItsiHttpRequest {
68
59
  pub fn is_connection_closed_err(ruby: &Ruby, err: &Error) -> bool {
69
60
  match err.error_type() {
70
61
  ErrorType::Jump(_) => false,
@@ -82,35 +73,32 @@ impl ItsiRequest {
82
73
  }
83
74
  }
84
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
+ }
85
83
 
86
84
  pub fn is_json(&self) -> bool {
87
- self.content_type.eq("application/json")
85
+ self.content_type_str() == "application/json"
88
86
  }
89
87
 
90
88
  pub fn is_html(&self) -> bool {
91
- self.content_type.eq("text/html")
89
+ self.content_type_str() == "text/html"
92
90
  }
93
91
 
94
- pub fn process(
95
- self,
96
- ruby: &Ruby,
97
- server: RClass,
98
- app: Opaque<Value>,
99
- ) -> magnus::error::Result<()> {
100
- let req = format!("{}", self);
92
+ pub fn process(self, ruby: &Ruby, app_proc: Arc<HeapValue<Proc>>) -> magnus::error::Result<()> {
101
93
  let response = self.response.clone();
102
- let start = self.start;
103
- debug!("{} Started", req);
104
- let result = server.funcall::<_, _, Value>(*ID_CALL, (app, self));
94
+ let result = app_proc.call::<_, Value>((self,));
105
95
  if let Err(err) = result {
106
96
  Self::internal_error(ruby, response, err);
107
97
  }
108
- debug!("{} Finished in {:?}", req, start.elapsed());
109
-
110
98
  Ok(())
111
99
  }
112
100
 
113
- pub fn internal_error(ruby: &Ruby, response: ItsiResponse, err: Error) {
101
+ pub fn internal_error(ruby: &Ruby, response: ItsiHttpResponse, err: Error) {
114
102
  if Self::is_connection_closed_err(ruby, &err) {
115
103
  debug!("Connection closed by client");
116
104
  response.close();
@@ -127,17 +115,18 @@ impl ItsiRequest {
127
115
  }
128
116
 
129
117
  pub(crate) async fn process_request(
130
- hyper_request: Request<Incoming>,
131
- sender: async_channel::Sender<RequestJob>,
132
- server: Arc<Server>,
133
- listener: Arc<ListenerInfo>,
134
- addr: SockAddr,
135
- shutdown_rx: watch::Receiver<RunningPhase>,
136
- ) -> itsi_error::Result<Response<BoxBody<Bytes, Infallible>>> {
137
- let (request, mut receiver) = ItsiRequest::new(hyper_request, addr, server, listener).await;
138
-
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();
139
124
  let response = request.response.clone();
140
- match sender.send(RequestJob::ProcessRequest(request)).await {
125
+ match context
126
+ .sender
127
+ .send(RequestJob::ProcessHttpRequest(request, app))
128
+ .await
129
+ {
141
130
  Err(err) => {
142
131
  error!("Error occurred: {}", err);
143
132
  let mut response = Response::new(BoxBody::new(Empty::new()));
@@ -145,20 +134,22 @@ impl ItsiRequest {
145
134
  Ok(response)
146
135
  }
147
136
  _ => match receiver.recv().await {
148
- Some(first_frame) => Ok(response.build(first_frame, receiver, shutdown_rx).await),
149
- None => Ok(response.build(None, receiver, shutdown_rx).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),
150
143
  },
151
144
  }
152
145
  }
153
146
 
154
147
  pub(crate) async fn new(
155
- request: Request<Incoming>,
156
- sock_addr: SockAddr,
157
- server: Arc<Server>,
158
- listener: Arc<ListenerInfo>,
159
- ) -> (ItsiRequest, mpsc::Receiver<Option<Bytes>>) {
148
+ request: HttpRequest,
149
+ context: &RequestContext,
150
+ ) -> (ItsiHttpRequest, mpsc::Receiver<ByteFrame>) {
160
151
  let (parts, body) = request.into_parts();
161
- let body = if server.stream_body.is_some_and(|f| f) {
152
+ let body = if context.server_params.streamable_body {
162
153
  ItsiBody::Stream(ItsiBodyProxy::new(body))
163
154
  } else {
164
155
  let mut body_bytes = BigBytes::new();
@@ -169,35 +160,14 @@ impl ItsiRequest {
169
160
  }
170
161
  ItsiBody::Buffered(body_bytes)
171
162
  };
172
- let response_channel = mpsc::channel::<Option<Bytes>>(100);
163
+ let response_channel = mpsc::channel::<ByteFrame>(100);
173
164
  (
174
165
  Self {
175
- remote_addr: sock_addr.to_string(),
176
- body,
177
- server,
178
- listener,
179
- version: format!("{:?}", &parts.version),
180
- response: ItsiResponse::new(
181
- parts.clone(),
182
- response_channel.0,
183
- parts
184
- .headers
185
- .get("Accept")
186
- .unwrap_or(&HeaderValue::from_static("text/html"))
187
- .to_str()
188
- .unwrap()
189
- .to_string(),
190
- ),
166
+ context: context.clone(),
167
+ version: parts.version,
168
+ response: ItsiHttpResponse::new(parts.clone(), response_channel.0),
191
169
  start: Instant::now(),
192
- content_type: parts
193
- .headers
194
- .get("Content-Type")
195
- .unwrap_or(&HeaderValue::from_static(
196
- "application/x-www-form-urlencoded",
197
- ))
198
- .to_str()
199
- .unwrap()
200
- .to_string(),
170
+ body,
201
171
  parts,
202
172
  },
203
173
  response_channel.1,
@@ -209,12 +179,12 @@ impl ItsiRequest {
209
179
  .parts
210
180
  .uri
211
181
  .path()
212
- .strip_prefix(&self.server.script_name)
182
+ .strip_prefix(&self.context.server_params.script_name)
213
183
  .unwrap_or(self.parts.uri.path()))
214
184
  }
215
185
 
216
186
  pub(crate) fn script_name(&self) -> MagnusResult<&str> {
217
- Ok(&self.server.script_name)
187
+ Ok(&self.context.server_params.script_name)
218
188
  }
219
189
 
220
190
  pub(crate) fn query_string(&self) -> MagnusResult<&str> {
@@ -226,7 +196,14 @@ impl ItsiRequest {
226
196
  }
227
197
 
228
198
  pub(crate) fn version(&self) -> MagnusResult<&str> {
229
- Ok(&self.version)
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
+ })
230
207
  }
231
208
 
232
209
  pub(crate) fn rack_protocol(&self) -> MagnusResult<Vec<&str>> {
@@ -246,53 +223,60 @@ impl ItsiRequest {
246
223
  .unwrap_or_else(|| vec!["http"]))
247
224
  }
248
225
 
249
- pub(crate) fn host(&self) -> MagnusResult<String> {
226
+ pub(crate) fn host(&self) -> MagnusResult<&str> {
250
227
  Ok(self
251
228
  .parts
252
229
  .uri
253
230
  .host()
254
- .map(|host| host.to_string())
255
- .unwrap_or_else(|| self.listener.host.clone()))
231
+ .unwrap_or_else(|| &self.context.listener.host))
256
232
  }
257
233
 
258
- pub(crate) fn scheme(&self) -> MagnusResult<String> {
234
+ pub(crate) fn scheme(&self) -> MagnusResult<&str> {
259
235
  Ok(self
260
236
  .parts
261
237
  .uri
262
238
  .scheme()
263
- .map(|scheme| scheme.to_string())
264
- .unwrap_or_else(|| self.listener.scheme.clone()))
239
+ .map(|scheme| scheme.as_str())
240
+ .unwrap_or_else(|| &self.context.listener.scheme))
265
241
  }
266
242
 
267
- pub(crate) fn headers(&self) -> MagnusResult<Vec<(String, &str)>> {
243
+ pub(crate) fn headers(&self) -> MagnusResult<Vec<(&str, &str)>> {
268
244
  Ok(self
269
245
  .parts
270
246
  .headers
271
247
  .iter()
272
- .map(|(hn, hv)| {
273
- let key = match hn.as_str() {
274
- "content-length" => "CONTENT_LENGTH".to_string(),
275
- "content-type" => "CONTENT_TYPE".to_string(),
276
- _ => format!("HTTP_{}", hn.as_str().to_uppercase().replace("-", "_")),
277
- };
278
- (key, hv.to_str().unwrap_or(""))
279
- })
280
- .collect())
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))
281
261
  }
282
262
 
283
263
  pub(crate) fn remote_addr(&self) -> MagnusResult<&str> {
284
- Ok(&self.remote_addr)
264
+ Ok(&self.context.addr)
285
265
  }
286
266
 
287
267
  pub(crate) fn port(&self) -> MagnusResult<u16> {
288
- Ok(self.parts.uri.port_u16().unwrap_or(self.listener.port))
268
+ Ok(self
269
+ .parts
270
+ .uri
271
+ .port_u16()
272
+ .unwrap_or(self.context.listener.port))
289
273
  }
290
274
 
291
- pub(crate) fn body(&self) -> MagnusResult<Value> {
275
+ pub(crate) fn body(&self) -> MagnusResult<Option<Value>> {
292
276
  Ok(self.body.into_value())
293
277
  }
294
278
 
295
- pub(crate) fn response(&self) -> MagnusResult<ItsiResponse> {
279
+ pub(crate) fn response(&self) -> MagnusResult<ItsiHttpResponse> {
296
280
  Ok(self.response.clone())
297
281
  }
298
282
  }
@@ -13,7 +13,7 @@ use itsi_tracing::error;
13
13
  use magnus::error::Result as MagnusResult;
14
14
  use parking_lot::RwLock;
15
15
  use std::{
16
- convert::Infallible,
16
+ collections::HashMap,
17
17
  io,
18
18
  os::{fd::FromRawFd, unix::net::UnixStream},
19
19
  str::FromStr,
@@ -31,31 +31,32 @@ use tokio_stream::wrappers::ReceiverStream;
31
31
  use tokio_util::io::ReaderStream;
32
32
  use tracing::warn;
33
33
 
34
- use crate::server::serve_strategy::single_mode::RunningPhase;
34
+ use crate::server::{
35
+ byte_frame::ByteFrame, serve_strategy::single_mode::RunningPhase, types::HttpResponse,
36
+ };
35
37
 
36
- #[magnus::wrap(class = "Itsi::Response", free_immediately, size)]
38
+ #[magnus::wrap(class = "Itsi::HttpResponse", free_immediately, size)]
37
39
  #[derive(Debug, Clone)]
38
- pub struct ItsiResponse {
40
+ pub struct ItsiHttpResponse {
39
41
  pub data: Arc<ResponseData>,
40
- pub accept: String,
41
42
  }
42
43
 
43
44
  #[derive(Debug)]
44
45
  pub struct ResponseData {
45
- pub response: RwLock<Option<Response<BoxBody<Bytes, Infallible>>>>,
46
- pub response_writer: RwLock<Option<mpsc::Sender<Option<Bytes>>>>,
46
+ pub response: RwLock<Option<HttpResponse>>,
47
+ pub response_writer: RwLock<Option<mpsc::Sender<ByteFrame>>>,
47
48
  pub response_buffer: RwLock<BytesMut>,
48
49
  pub hijacked_socket: RwLock<Option<UnixStream>>,
49
50
  pub parts: Parts,
50
51
  }
51
52
 
52
- impl ItsiResponse {
53
+ impl ItsiHttpResponse {
53
54
  pub async fn build(
54
55
  &self,
55
- first_frame: Option<Bytes>,
56
- receiver: mpsc::Receiver<Option<Bytes>>,
56
+ first_frame: ByteFrame,
57
+ receiver: mpsc::Receiver<ByteFrame>,
57
58
  shutdown_rx: watch::Receiver<RunningPhase>,
58
- ) -> Response<BoxBody<Bytes, Infallible>> {
59
+ ) -> HttpResponse {
59
60
  if self.is_hijacked() {
60
61
  return match self.process_hijacked_response().await {
61
62
  Ok(result) => result,
@@ -65,14 +66,13 @@ impl ItsiResponse {
65
66
  }
66
67
  };
67
68
  }
68
-
69
69
  let mut response = self.data.response.write().take().unwrap();
70
- *response.body_mut() = if first_frame.is_none() {
70
+ *response.body_mut() = if matches!(first_frame, ByteFrame::Empty) {
71
71
  BoxBody::new(Empty::new())
72
- } else if receiver.is_closed() && receiver.is_empty() {
73
- BoxBody::new(Full::new(first_frame.unwrap()))
72
+ } else if matches!(first_frame, ByteFrame::End(_)) {
73
+ BoxBody::new(Full::new(first_frame.into()))
74
74
  } else {
75
- let initial_frame = tokio_stream::once(Ok(Frame::data(first_frame.unwrap())));
75
+ let initial_frame = tokio_stream::once(Ok(Frame::data(Bytes::from(first_frame))));
76
76
  let frame_stream = unfold(
77
77
  (ReceiverStream::new(receiver), shutdown_rx),
78
78
  |(mut receiver, mut shutdown_rx)| async move {
@@ -82,13 +82,14 @@ impl ItsiResponse {
82
82
  loop {
83
83
  tokio::select! {
84
84
  maybe_bytes = receiver.next() => {
85
- if let Some(bytes) = maybe_bytes {
86
- // We assume `bytes` is Some(Bytes) here.
87
- return Some((Ok(Frame::data(bytes.unwrap())), (receiver, shutdown_rx)));
88
- } else {
89
- // Receiver closed, end the stream.
90
- return None;
85
+ match maybe_bytes {
86
+ Some(ByteFrame::Data(bytes)) | Some(ByteFrame::End(bytes)) => {
87
+ return Some((Ok(Frame::data(bytes)), (receiver, shutdown_rx)));
91
88
  }
89
+ _ => {
90
+ return None;
91
+ }
92
+ }
92
93
  },
93
94
  _ = shutdown_rx.changed() => {
94
95
  match *shutdown_rx.borrow() {
@@ -184,7 +185,7 @@ impl ItsiResponse {
184
185
  Ok((headers, status, requires_upgrade, reader))
185
186
  }
186
187
 
187
- pub async fn process_hijacked_response(&self) -> Result<Response<BoxBody<Bytes, Infallible>>> {
188
+ pub async fn process_hijacked_response(&self) -> Result<HttpResponse> {
188
189
  let (headers, status, requires_upgrade, reader) = self.read_hijacked_headers().await?;
189
190
  let mut response = if requires_upgrade {
190
191
  let parts = self.data.parts.clone();
@@ -261,28 +262,31 @@ impl ItsiResponse {
261
262
  }
262
263
  }
263
264
 
264
- pub fn send_frame(&self, frame: Bytes) -> MagnusResult<usize> {
265
- self.send_frame_into(frame, &self.data.response_writer)
265
+ pub fn send_frame(&self, frame: Bytes) -> MagnusResult<()> {
266
+ self.send_frame_into(ByteFrame::Data(frame), &self.data.response_writer)
266
267
  }
267
268
 
268
- pub fn send_and_close(&self, frame: Bytes) -> MagnusResult<usize> {
269
- let result = self.send_frame_into(frame, &self.data.response_writer);
269
+ pub fn recv_frame(&self) {
270
+ // not implemented
271
+ }
272
+
273
+ pub fn send_and_close(&self, frame: Bytes) -> MagnusResult<()> {
274
+ let result = self.send_frame_into(ByteFrame::End(frame), &self.data.response_writer);
270
275
  self.data.response_writer.write().take();
271
276
  result
272
277
  }
273
278
 
274
279
  pub fn send_frame_into(
275
280
  &self,
276
- frame: Bytes,
277
- writer: &RwLock<Option<mpsc::Sender<Option<Bytes>>>>,
278
- ) -> MagnusResult<usize> {
281
+ frame: ByteFrame,
282
+ writer: &RwLock<Option<mpsc::Sender<ByteFrame>>>,
283
+ ) -> MagnusResult<()> {
279
284
  if let Some(writer) = writer.write().as_ref() {
280
- writer
281
- .blocking_send(Some(frame))
282
- .map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)
283
- .ok();
285
+ return Ok(writer
286
+ .blocking_send(frame)
287
+ .map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)?);
284
288
  }
285
- Ok(0)
289
+ Ok(())
286
290
  }
287
291
 
288
292
  pub fn is_hijacked(&self) -> bool {
@@ -293,20 +297,28 @@ impl ItsiResponse {
293
297
  self.data.response_writer.write().take();
294
298
  Ok(true)
295
299
  }
300
+ fn accept_str(&self) -> &str {
301
+ self.data
302
+ .parts
303
+ .headers
304
+ .get("Content-Type")
305
+ .and_then(|hv| hv.to_str().ok()) // handle invalid utf-8
306
+ .unwrap_or("application/x-www-form-urlencoded")
307
+ }
296
308
 
297
309
  pub fn is_html(&self) -> bool {
298
- self.accept.starts_with("text/html")
310
+ self.accept_str().starts_with("text/html")
299
311
  }
300
312
 
301
313
  pub fn is_json(&self) -> bool {
302
- self.accept.starts_with("application/json")
314
+ self.accept_str().starts_with("application/json")
303
315
  }
304
316
 
305
317
  pub fn close_read(&self) -> MagnusResult<bool> {
306
318
  Ok(true)
307
319
  }
308
320
 
309
- pub fn new(parts: Parts, response_writer: mpsc::Sender<Option<Bytes>>, accept: String) -> Self {
321
+ pub fn new(parts: Parts, response_writer: mpsc::Sender<ByteFrame>) -> Self {
310
322
  Self {
311
323
  data: Arc::new(ResponseData {
312
324
  response: RwLock::new(Some(Response::new(BoxBody::new(Empty::new())))),
@@ -315,7 +327,6 @@ impl ItsiResponse {
315
327
  hijacked_socket: RwLock::new(None),
316
328
  parts,
317
329
  }),
318
- accept,
319
330
  }
320
331
  }
321
332
 
@@ -330,6 +341,26 @@ impl ItsiResponse {
330
341
  Ok(())
331
342
  }
332
343
 
344
+ pub fn add_headers(&self, headers: HashMap<Bytes, Vec<Bytes>>) -> MagnusResult<()> {
345
+ if let Some(ref mut resp) = *self.data.response.write() {
346
+ let headers_mut = resp.headers_mut();
347
+ for (name, values) in headers {
348
+ let header_name = HeaderName::from_bytes(&name).map_err(|e| {
349
+ itsi_error::ItsiError::InvalidInput(format!(
350
+ "Invalid header name {:?}: {:?}",
351
+ name, e
352
+ ))
353
+ })?;
354
+ for value in values {
355
+ let header_value = unsafe { HeaderValue::from_maybe_shared_unchecked(value) };
356
+ headers_mut.insert(&header_name, header_value);
357
+ }
358
+ }
359
+ }
360
+
361
+ Ok(())
362
+ }
363
+
333
364
  pub fn set_status(&self, status: u16) -> MagnusResult<()> {
334
365
  if let Some(ref mut resp) = *self.data.response.write() {
335
366
  *resp.status_mut() = StatusCode::from_u16(status).map_err(|e| {
@@ -348,8 +379,8 @@ impl ItsiResponse {
348
379
  *self.data.hijacked_socket.write() = Some(stream);
349
380
  if let Some(writer) = self.data.response_writer.write().as_ref() {
350
381
  writer
351
- .blocking_send(None)
352
- .map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)?;
382
+ .blocking_send(ByteFrame::Empty)
383
+ .map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)?
353
384
  }
354
385
  self.close();
355
386
  Ok(())