itsi 0.2.16 → 0.2.18

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/.zed/settings.json +32 -0
  3. data/CHANGELOG.md +21 -0
  4. data/Cargo.lock +4 -2
  5. data/crates/itsi_acme/Cargo.toml +1 -1
  6. data/crates/itsi_scheduler/Cargo.toml +1 -1
  7. data/crates/itsi_server/Cargo.toml +3 -1
  8. data/crates/itsi_server/src/lib.rs +6 -1
  9. data/crates/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +2 -0
  10. data/crates/itsi_server/src/ruby_types/itsi_grpc_call.rs +4 -4
  11. data/crates/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +14 -13
  12. data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +64 -33
  13. data/crates/itsi_server/src/ruby_types/itsi_http_response.rs +151 -152
  14. data/crates/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +422 -110
  15. data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +62 -15
  16. data/crates/itsi_server/src/ruby_types/itsi_server.rs +1 -1
  17. data/crates/itsi_server/src/server/binds/listener.rs +45 -7
  18. data/crates/itsi_server/src/server/frame_stream.rs +142 -0
  19. data/crates/itsi_server/src/server/http_message_types.rs +142 -9
  20. data/crates/itsi_server/src/server/io_stream.rs +28 -5
  21. data/crates/itsi_server/src/server/lifecycle_event.rs +1 -1
  22. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +2 -3
  23. data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +8 -10
  24. data/crates/itsi_server/src/server/middleware_stack/middlewares/cors.rs +2 -3
  25. data/crates/itsi_server/src/server/middleware_stack/middlewares/csp.rs +3 -3
  26. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +54 -56
  27. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +5 -7
  28. data/crates/itsi_server/src/server/middleware_stack/middlewares/etag.rs +5 -5
  29. data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +7 -10
  30. data/crates/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +2 -3
  31. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +1 -2
  32. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +4 -6
  33. data/crates/itsi_server/src/server/mod.rs +1 -0
  34. data/crates/itsi_server/src/server/process_worker.rs +3 -4
  35. data/crates/itsi_server/src/server/serve_strategy/acceptor.rs +16 -12
  36. data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +83 -31
  37. data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +166 -142
  38. data/crates/itsi_server/src/server/signal.rs +37 -9
  39. data/crates/itsi_server/src/server/thread_worker.rs +84 -69
  40. data/crates/itsi_server/src/services/itsi_http_service.rs +43 -43
  41. data/crates/itsi_server/src/services/static_file_server.rs +28 -47
  42. data/docs/benchmark-dashboard/.gitignore +27 -0
  43. data/docs/benchmark-dashboard/app/api/benchmarks/route.ts +22 -0
  44. data/docs/benchmark-dashboard/app/globals.css +94 -0
  45. data/docs/benchmark-dashboard/app/layout.tsx +20 -0
  46. data/docs/benchmark-dashboard/app/page.tsx +252 -0
  47. data/docs/benchmark-dashboard/components/benchmark-dashboard.tsx +1663 -0
  48. data/docs/benchmark-dashboard/components/theme-provider.tsx +11 -0
  49. data/docs/benchmark-dashboard/components/ui/accordion.tsx +58 -0
  50. data/docs/benchmark-dashboard/components/ui/alert-dialog.tsx +141 -0
  51. data/docs/benchmark-dashboard/components/ui/alert.tsx +59 -0
  52. data/docs/benchmark-dashboard/components/ui/aspect-ratio.tsx +7 -0
  53. data/docs/benchmark-dashboard/components/ui/avatar.tsx +50 -0
  54. data/docs/benchmark-dashboard/components/ui/badge.tsx +36 -0
  55. data/docs/benchmark-dashboard/components/ui/breadcrumb.tsx +115 -0
  56. data/docs/benchmark-dashboard/components/ui/button.tsx +56 -0
  57. data/docs/benchmark-dashboard/components/ui/calendar.tsx +66 -0
  58. data/docs/benchmark-dashboard/components/ui/card.tsx +79 -0
  59. data/docs/benchmark-dashboard/components/ui/carousel.tsx +262 -0
  60. data/docs/benchmark-dashboard/components/ui/chart.tsx +365 -0
  61. data/docs/benchmark-dashboard/components/ui/checkbox.tsx +30 -0
  62. data/docs/benchmark-dashboard/components/ui/collapsible.tsx +11 -0
  63. data/docs/benchmark-dashboard/components/ui/command.tsx +153 -0
  64. data/docs/benchmark-dashboard/components/ui/context-menu.tsx +200 -0
  65. data/docs/benchmark-dashboard/components/ui/dialog.tsx +122 -0
  66. data/docs/benchmark-dashboard/components/ui/drawer.tsx +118 -0
  67. data/docs/benchmark-dashboard/components/ui/dropdown-menu.tsx +200 -0
  68. data/docs/benchmark-dashboard/components/ui/form.tsx +178 -0
  69. data/docs/benchmark-dashboard/components/ui/hover-card.tsx +29 -0
  70. data/docs/benchmark-dashboard/components/ui/input-otp.tsx +71 -0
  71. data/docs/benchmark-dashboard/components/ui/input.tsx +22 -0
  72. data/docs/benchmark-dashboard/components/ui/label.tsx +26 -0
  73. data/docs/benchmark-dashboard/components/ui/loading-spinner.tsx +12 -0
  74. data/docs/benchmark-dashboard/components/ui/menubar.tsx +236 -0
  75. data/docs/benchmark-dashboard/components/ui/navigation-menu.tsx +128 -0
  76. data/docs/benchmark-dashboard/components/ui/pagination.tsx +117 -0
  77. data/docs/benchmark-dashboard/components/ui/popover.tsx +31 -0
  78. data/docs/benchmark-dashboard/components/ui/progress.tsx +28 -0
  79. data/docs/benchmark-dashboard/components/ui/radio-group.tsx +44 -0
  80. data/docs/benchmark-dashboard/components/ui/resizable.tsx +45 -0
  81. data/docs/benchmark-dashboard/components/ui/scroll-area.tsx +48 -0
  82. data/docs/benchmark-dashboard/components/ui/select.tsx +160 -0
  83. data/docs/benchmark-dashboard/components/ui/separator.tsx +31 -0
  84. data/docs/benchmark-dashboard/components/ui/sheet.tsx +140 -0
  85. data/docs/benchmark-dashboard/components/ui/sidebar.tsx +763 -0
  86. data/docs/benchmark-dashboard/components/ui/skeleton.tsx +15 -0
  87. data/docs/benchmark-dashboard/components/ui/slider.tsx +28 -0
  88. data/docs/benchmark-dashboard/components/ui/sonner.tsx +31 -0
  89. data/docs/benchmark-dashboard/components/ui/switch.tsx +29 -0
  90. data/docs/benchmark-dashboard/components/ui/table.tsx +117 -0
  91. data/docs/benchmark-dashboard/components/ui/tabs.tsx +55 -0
  92. data/docs/benchmark-dashboard/components/ui/textarea.tsx +22 -0
  93. data/docs/benchmark-dashboard/components/ui/toast.tsx +129 -0
  94. data/docs/benchmark-dashboard/components/ui/toaster.tsx +35 -0
  95. data/docs/benchmark-dashboard/components/ui/toggle-group.tsx +61 -0
  96. data/docs/benchmark-dashboard/components/ui/toggle.tsx +45 -0
  97. data/docs/benchmark-dashboard/components/ui/tooltip.tsx +30 -0
  98. data/docs/benchmark-dashboard/components/ui/use-mobile.tsx +19 -0
  99. data/docs/benchmark-dashboard/components/ui/use-toast.ts +194 -0
  100. data/docs/benchmark-dashboard/components.json +21 -0
  101. data/docs/benchmark-dashboard/dist/benchmark-dashboard.css +1 -0
  102. data/docs/benchmark-dashboard/dist/benchmark-dashboard.iife.js +211 -0
  103. data/docs/benchmark-dashboard/dist/placeholder-logo.png +0 -0
  104. data/docs/benchmark-dashboard/dist/placeholder-logo.svg +1 -0
  105. data/docs/benchmark-dashboard/dist/placeholder-user.jpg +0 -0
  106. data/docs/benchmark-dashboard/dist/placeholder.jpg +0 -0
  107. data/docs/benchmark-dashboard/dist/placeholder.svg +1 -0
  108. data/docs/benchmark-dashboard/embed.tsx +13 -0
  109. data/docs/benchmark-dashboard/hooks/use-mobile.tsx +19 -0
  110. data/docs/benchmark-dashboard/hooks/use-toast.ts +194 -0
  111. data/docs/benchmark-dashboard/lib/benchmark-utils.ts +54 -0
  112. data/docs/benchmark-dashboard/lib/utils.ts +6 -0
  113. data/docs/benchmark-dashboard/next.config.mjs +14 -0
  114. data/docs/benchmark-dashboard/package-lock.json +5859 -0
  115. data/docs/benchmark-dashboard/package.json +72 -0
  116. data/docs/benchmark-dashboard/pnpm-lock.yaml +5 -0
  117. data/docs/benchmark-dashboard/postcss.config.mjs +8 -0
  118. data/docs/benchmark-dashboard/styles/globals.css +94 -0
  119. data/docs/benchmark-dashboard/tailwind.config.ts +96 -0
  120. data/docs/benchmark-dashboard/tsconfig.json +27 -0
  121. data/docs/benchmark-dashboard/vite.config.ts +24 -0
  122. data/docs/build.rb +52 -0
  123. data/docs/content/acknowledgements/_index.md +1 -1
  124. data/docs/content/benchmarks/index.md +96 -0
  125. data/docs/content/configuration/_index.md +2 -2
  126. data/docs/content/getting_started/_index.md +76 -46
  127. data/docs/content/itsi_scheduler/_index.md +2 -2
  128. data/docs/hugo.yaml +10 -1
  129. data/docs/static/results.json +1 -0
  130. data/docs/static/scripts/benchmark-dashboard.iife.js +211 -0
  131. data/docs/static/styles/benchmark-dashboard.css +1 -0
  132. data/examples/api_with_schema_and_controllers/README.md +1 -1
  133. data/gems/scheduler/Cargo.lock +1 -1
  134. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  135. data/gems/server/Cargo.lock +3 -1
  136. data/gems/server/exe/itsi +8 -5
  137. data/gems/server/lib/itsi/http_request.rb +31 -39
  138. data/gems/server/lib/itsi/http_response.rb +5 -0
  139. data/gems/server/lib/itsi/rack_env_pool.rb +59 -0
  140. data/gems/server/lib/itsi/server/config/dsl.rb +6 -4
  141. data/gems/server/lib/itsi/server/config/middleware/compression.md +3 -3
  142. data/gems/server/lib/itsi/server/config/middleware/endpoint/controller.md +1 -1
  143. data/gems/server/lib/itsi/server/config/middleware/proxy.md +2 -2
  144. data/gems/server/lib/itsi/server/config/middleware/proxy.rb +1 -1
  145. data/gems/server/lib/itsi/server/config/middleware/rackup_file.rb +2 -2
  146. data/gems/server/lib/itsi/server/config/options/auto_reload_config.rb +11 -6
  147. data/gems/server/lib/itsi/server/config/options/include.md +1 -0
  148. data/gems/server/lib/itsi/server/config/options/include.rb +13 -8
  149. data/gems/server/lib/itsi/server/config/options/pipeline_flush.md +16 -0
  150. data/gems/server/lib/itsi/server/config/options/pipeline_flush.rb +19 -0
  151. data/gems/server/lib/itsi/server/config/options/reuse_port.rb +2 -4
  152. data/gems/server/lib/itsi/server/config/options/writev.md +25 -0
  153. data/gems/server/lib/itsi/server/config/options/writev.rb +19 -0
  154. data/gems/server/lib/itsi/server/config.rb +22 -9
  155. data/gems/server/lib/itsi/server/default_config/Itsi.rb +9 -8
  156. data/gems/server/lib/itsi/server/grpc/grpc_call.rb +2 -0
  157. data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +2 -2
  158. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +3 -1
  159. data/gems/server/lib/itsi/server/rack_interface.rb +17 -12
  160. data/gems/server/lib/itsi/server/scheduler_interface.rb +2 -0
  161. data/gems/server/lib/itsi/server/version.rb +1 -1
  162. data/gems/server/lib/itsi/server.rb +1 -0
  163. data/gems/server/lib/ruby_lsp/itsi/addon.rb +12 -13
  164. data/gems/server/test/helpers/test_helper.rb +12 -13
  165. data/gems/server/test/middleware/grpc/grpc.rb +13 -14
  166. data/gems/server/test/middleware/grpc/test_service_impl.rb +3 -3
  167. data/gems/server/test/middleware/proxy.rb +262 -268
  168. data/lib/itsi/version.rb +1 -1
  169. metadata +97 -6
  170. data/tasks.txt +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d26838019381b65a1bec973d72b93cb32a377dba836951cb3e43fed1f7eb3737
4
- data.tar.gz: 1e95bfc6b703baca4813252c6cbd6dd5838d7690db71e4d49fdb3988ffc5058c
3
+ metadata.gz: d643690ccc0157ece254b12b72bd4eff904a907db970639df8567c43693b2c7b
4
+ data.tar.gz: 5202c977ca30f83ddc09fc9bbe31966b06ffb5beda308568a684e0242d3898f7
5
5
  SHA512:
6
- metadata.gz: f666599892fb63930aaee662395015f46d91e15bbc054878d759de084938d7402de8c2d7b9644b554dfe779b346d99f4d6e6b0114fa3296f1dc21b49b375ff99
7
- data.tar.gz: aee5a3016644f6a97c02d0445b14e984a9af751c7f7bac2e437c15622f3a45300e5a69d305354f601260ed45585a88fdde37c66a60fa34d61d1645ae72e862c6
6
+ metadata.gz: 07567a207915ae2f6d4dc0c6827e5b9008574f1e8b9864b0969657a06d5f96f4d638b0532187da82511b7f71b06c7bc1499fd1cd32cb7e85fa13c3cb0d76e34a
7
+ data.tar.gz: 0f7bfcc95d1a79deae127f6899122c8ffd3d3042332ad33bbc2cf0e490abb9392dfbce76f4cbcfa878b58ef8e6ee8b3694853994c9c78dbc2b4165fe76bf6be5
@@ -0,0 +1,32 @@
1
+ // Folder-specific settings
2
+ //
3
+ // For a full list of overridable settings, and general information on folder-specific settings,
4
+ // see the documentation: https://zed.dev/docs/configuring-zed#settings-files
5
+ {
6
+ "file_scan_exclusions": [
7
+ ".data",
8
+ ".git$",
9
+ ".hg",
10
+ ".rsync_cache",
11
+ "assets/webpack",
12
+ "build/static",
13
+ "client/build",
14
+ "CVS",
15
+ "node_modules",
16
+ "public/system/*",
17
+ "tmp/cache",
18
+ "**/.classpath",
19
+ "**/.DS_Store",
20
+ "**/.git",
21
+ "**/.hg",
22
+ "**/.settings",
23
+ "**/.svn",
24
+ "**/CVS",
25
+ "**/Thumbs.db",
26
+ ".svn",
27
+ "gems/scheduler/ext/*",
28
+ "gems/server/ext/*",
29
+ "benchmarks/results/*",
30
+ "docs/static/*"
31
+ ]
32
+ }
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [0.2.18] - 2025-XX-XX
2
+ ### WIP
3
+ -- Fixing error in auto-reload on Linux when reuse_port is false
4
+ -- Fix breaking of auto-reload on config file errors
5
+ -- include directive is relative (equivalent to require_relative)
6
+ -- Fixing preload gem group logic
7
+ -- Fix errors in interrupt handling during some debug flows
8
+
9
+ ## [0.2.17] - 2025-05-31
10
+ - Enabled vectorized writes in IoSteam
11
+ - Replaced all usage of heap-allocated BoxBody with HttpBody enums
12
+ - Add 5 threads as default for rack/handler
13
+ - Reserve header size ahead of time in rack interface
14
+ - Avoid intermediate array allocation when populating Rack env headers.
15
+ - Rewrite synchronous thread worker to avoid excessive GVL acquisition
16
+ - Revert to default write_ev behaviour for http1
17
+ - Switch to service_fn from service struct to avoid one additional pinned future
18
+ - Worker pinning accepts ruby workers too
19
+ - Fixed ordering incomaptibility in etag forwarding from static file server
20
+ - Added embedded benchmark suite
21
+
1
22
  ## [0.2.16] - 2025-05-02
2
23
  - Optimized static error responses
3
24
  - Optimized rate limit middleware
data/Cargo.lock CHANGED
@@ -1644,7 +1644,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1644
1644
 
1645
1645
  [[package]]
1646
1646
  name = "itsi-scheduler"
1647
- version = "0.2.16"
1647
+ version = "0.2.18"
1648
1648
  dependencies = [
1649
1649
  "bytes",
1650
1650
  "derive_more",
@@ -1662,7 +1662,7 @@ dependencies = [
1662
1662
 
1663
1663
  [[package]]
1664
1664
  name = "itsi-server"
1665
- version = "0.2.16"
1665
+ version = "0.2.18"
1666
1666
  dependencies = [
1667
1667
  "argon2",
1668
1668
  "async-channel",
@@ -1678,6 +1678,7 @@ dependencies = [
1678
1678
  "either",
1679
1679
  "fs2",
1680
1680
  "futures",
1681
+ "futures-util",
1681
1682
  "globset",
1682
1683
  "http 1.3.1",
1683
1684
  "http-body-util",
@@ -1713,6 +1714,7 @@ dependencies = [
1713
1714
  "serde_magnus",
1714
1715
  "sha-crypt",
1715
1716
  "sha2",
1717
+ "smallvec",
1716
1718
  "socket2",
1717
1719
  "sysinfo",
1718
1720
  "tempfile",
@@ -2,7 +2,7 @@
2
2
  name = "itsi_acme"
3
3
  version = "0.1.0"
4
4
  authors = [
5
- "wouterkem <wc@pico.net.nz>",
5
+ "wouterken <wc@pico.net.nz>",
6
6
  "dignifiedquire <me@dignifiedquire.com>",
7
7
  "Florian Uekermann <florian@uekermann.me>",
8
8
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-scheduler"
3
- version = "0.2.16"
3
+ version = "0.2.18"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-server"
3
- version = "0.2.16"
3
+ version = "0.2.18"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -90,3 +90,5 @@ argon2 = "0.5.3"
90
90
  core_affinity = "0.8.3"
91
91
  memchr = "2.7.4"
92
92
  quick_cache = "0.6.13"
93
+ smallvec = "1.15.0"
94
+ futures-util = "0.3.31"
@@ -58,6 +58,7 @@ fn init(ruby: &Ruby) -> Result<()> {
58
58
  request.define_method("rack_protocol", method!(ItsiHttpRequest::rack_protocol, 0))?;
59
59
  request.define_method("host", method!(ItsiHttpRequest::host, 0))?;
60
60
  request.define_method("headers", method!(ItsiHttpRequest::headers, 0))?;
61
+ request.define_method("each_header", method!(ItsiHttpRequest::each_header, 0))?;
61
62
  request.define_method("uri", method!(ItsiHttpRequest::uri, 0))?;
62
63
  request.define_method("header", method!(ItsiHttpRequest::header, 1))?;
63
64
  request.define_method("[]", method!(ItsiHttpRequest::header, 1))?;
@@ -71,6 +72,7 @@ fn init(ruby: &Ruby) -> Result<()> {
71
72
  request.define_method("url_encoded?", method!(ItsiHttpRequest::is_url_encoded, 0))?;
72
73
  request.define_method("multipart?", method!(ItsiHttpRequest::is_multipart, 0))?;
73
74
  request.define_method("url_params", method!(ItsiHttpRequest::url_params, 0))?;
75
+ request.define_method("server_error", method!(ItsiHttpRequest::error, 1))?;
74
76
 
75
77
  let body_proxy = ruby.get_inner(&ITSI_BODY_PROXY);
76
78
  body_proxy.define_method("gets", method!(ItsiBodyProxy::gets, 0))?;
@@ -80,6 +82,10 @@ fn init(ruby: &Ruby) -> Result<()> {
80
82
 
81
83
  let response = ruby.get_inner(&ITSI_RESPONSE);
82
84
  response.define_method("[]=", method!(ItsiHttpResponse::add_header, 2))?;
85
+ response.define_method(
86
+ "reserve_headers",
87
+ method!(ItsiHttpResponse::reserve_headers, 1),
88
+ )?;
83
89
  response.define_method("add_header", method!(ItsiHttpResponse::add_header, 2))?;
84
90
  response.define_method("add_headers", method!(ItsiHttpResponse::add_headers, 1))?;
85
91
  response.define_method("status=", method!(ItsiHttpResponse::set_status, 1))?;
@@ -87,7 +93,6 @@ fn init(ruby: &Ruby) -> Result<()> {
87
93
  response.define_method("<<", method!(ItsiHttpResponse::send_frame, 1))?;
88
94
  response.define_method("write", method!(ItsiHttpResponse::send_frame, 1))?;
89
95
  response.define_method("read", method!(ItsiHttpResponse::recv_frame, 0))?;
90
- response.define_method("flush", method!(ItsiHttpResponse::flush, 0))?;
91
96
  response.define_method("closed?", method!(ItsiHttpResponse::is_closed, 0))?;
92
97
  response.define_method(
93
98
  "send_and_close",
@@ -25,6 +25,7 @@ pub struct ItsiBodyProxy {
25
25
  pub enum ItsiBody {
26
26
  Buffered(BigBytes),
27
27
  Stream(ItsiBodyProxy),
28
+ Empty,
28
29
  }
29
30
 
30
31
  impl ItsiBody {
@@ -32,6 +33,7 @@ impl ItsiBody {
32
33
  match self {
33
34
  ItsiBody::Buffered(bytes) => bytes.as_value(),
34
35
  ItsiBody::Stream(proxy) => Some(proxy.clone().into_value()),
36
+ ItsiBody::Empty => None,
35
37
  }
36
38
  }
37
39
  }
@@ -1,6 +1,6 @@
1
1
  use super::itsi_grpc_response_stream::ItsiGrpcResponseStream;
2
2
  use crate::prelude::*;
3
- use crate::server::http_message_types::{HttpRequest, HttpResponse};
3
+ use crate::server::http_message_types::{HttpBody, HttpRequest, HttpResponse};
4
4
  use crate::server::{byte_frame::ByteFrame, request_job::RequestJob};
5
5
  use crate::services::itsi_http_service::HttpRequestContext;
6
6
  use async_compression::futures::bufread::{GzipDecoder, GzipEncoder, ZlibDecoder, ZlibEncoder};
@@ -8,7 +8,7 @@ use bytes::Bytes;
8
8
  use derive_more::Debug;
9
9
  use futures::{executor::block_on, io::Cursor, AsyncReadExt};
10
10
  use http::{request::Parts, Response, StatusCode};
11
- use http_body_util::{combinators::BoxBody, BodyExt, Empty};
11
+ use http_body_util::BodyExt;
12
12
  use itsi_error::CLIENT_CONNECTION_CLOSED;
13
13
  use itsi_rb_helpers::{print_rb_backtrace, HeapValue};
14
14
  use itsi_tracing::debug;
@@ -139,7 +139,7 @@ impl ItsiGrpcCall {
139
139
  {
140
140
  Err(err) => {
141
141
  error!("Error occurred: {}", err);
142
- let mut response = Response::new(BoxBody::new(Empty::new()));
142
+ let mut response = Response::new(HttpBody::empty());
143
143
  *response.status_mut() = StatusCode::BAD_REQUEST;
144
144
  Ok(response)
145
145
  }
@@ -147,7 +147,7 @@ impl ItsiGrpcCall {
147
147
  Some(first_frame) => Ok(response_stream
148
148
  .build_response(first_frame, receiver, shutdown_channel)
149
149
  .await),
150
- None => Ok(Response::new(BoxBody::new(Empty::new()))),
150
+ None => Ok(Response::new(HttpBody::empty())),
151
151
  },
152
152
  }
153
153
  }
@@ -1,6 +1,6 @@
1
1
  use super::itsi_grpc_call::CompressionAlgorithm;
2
2
  use crate::prelude::*;
3
- use crate::server::http_message_types::HttpResponse;
3
+ use crate::server::http_message_types::{HttpBody, HttpResponse};
4
4
  use crate::server::size_limited_incoming::SizeLimitedIncoming;
5
5
  use crate::server::{byte_frame::ByteFrame, serve_strategy::single_mode::RunningPhase};
6
6
  use bytes::Bytes;
@@ -11,8 +11,8 @@ use http::{
11
11
  header::{HeaderName, HeaderValue},
12
12
  HeaderMap, Response,
13
13
  };
14
- use http_body_util::{combinators::BoxBody, BodyDataStream, BodyExt, Empty, Full, StreamBody};
15
- use hyper::body::{Frame, Incoming};
14
+ use http_body_util::BodyDataStream;
15
+ use hyper::body::Incoming;
16
16
  use magnus::error::Result as MagnusResult;
17
17
  use nix::unistd::pipe;
18
18
  use parking_lot::Mutex;
@@ -161,7 +161,7 @@ impl ItsiGrpcResponseStream {
161
161
  response_headers,
162
162
  incoming_reader: Some(pipe_read),
163
163
  response_sender,
164
- response: Some(Response::new(BoxBody::new(Empty::new()))),
164
+ response: Some(Response::new(HttpBody::empty())),
165
165
  trailer_tx,
166
166
  trailer_rx: Some(trailer_rx),
167
167
  })),
@@ -207,12 +207,12 @@ impl ItsiGrpcResponseStream {
207
207
  let rx = self.inner.lock().trailer_rx.take().unwrap();
208
208
  *response.version_mut() = Version::HTTP_2;
209
209
  *response.headers_mut() = self.inner.lock().response_headers.clone();
210
- *response.body_mut() = if matches!(first_frame, ByteFrame::Empty) {
211
- BoxBody::new(Empty::new())
210
+ let body_with_trailers = if matches!(first_frame, ByteFrame::Empty) {
211
+ HttpBody::empty()
212
212
  } else if matches!(first_frame, ByteFrame::End(_)) {
213
- BoxBody::new(Full::new(first_frame.into()))
213
+ HttpBody::full(first_frame.into())
214
214
  } else {
215
- let initial_frame = tokio_stream::once(Ok(Frame::data(Bytes::from(first_frame))));
215
+ let initial_frame = tokio_stream::once(Ok(Bytes::from(first_frame)));
216
216
  let frame_stream = unfold(
217
217
  (ReceiverStream::new(receiver), shutdown_rx),
218
218
  |(mut receiver, mut shutdown_rx)| async move {
@@ -224,7 +224,7 @@ impl ItsiGrpcResponseStream {
224
224
  maybe_bytes = receiver.next() => {
225
225
  match maybe_bytes {
226
226
  Some(ByteFrame::Data(bytes)) | Some(ByteFrame::End(bytes)) => {
227
- return Some((Ok(Frame::data(bytes)), (receiver, shutdown_rx)));
227
+ return Some((Ok(bytes), (receiver, shutdown_rx)));
228
228
  }
229
229
  _ => {
230
230
  return None;
@@ -234,7 +234,7 @@ impl ItsiGrpcResponseStream {
234
234
  _ = shutdown_rx.changed() => {
235
235
  match *shutdown_rx.borrow() {
236
236
  RunningPhase::ShutdownPending => {
237
- warn!("Disconnecting streaming client.");
237
+ debug!("Disconnecting streaming client.");
238
238
  return None;
239
239
  },
240
240
  _ => continue,
@@ -246,15 +246,16 @@ impl ItsiGrpcResponseStream {
246
246
  );
247
247
 
248
248
  let combined_stream = initial_frame.chain(frame_stream);
249
- BoxBody::new(StreamBody::new(combined_stream))
249
+ HttpBody::stream(combined_stream)
250
250
  }
251
251
  .with_trailers(async move {
252
252
  match rx.await {
253
253
  Ok(trailers) => Some(Ok(trailers)),
254
254
  Err(_err) => None,
255
255
  }
256
- })
257
- .boxed();
256
+ });
257
+
258
+ *response.body_mut() = body_with_trailers;
258
259
  response
259
260
  }
260
261
 
@@ -1,31 +1,30 @@
1
1
  use derive_more::Debug;
2
2
  use futures::StreamExt;
3
- use http::{header::CONTENT_LENGTH, request::Parts, Response, StatusCode, Version};
4
- use http_body_util::{combinators::BoxBody, BodyExt, Empty};
3
+ use http::{header::CONTENT_LENGTH, request::Parts, HeaderValue, Response, StatusCode, Version};
4
+ use http_body_util::BodyExt;
5
5
  use itsi_error::CLIENT_CONNECTION_CLOSED;
6
- use itsi_rb_helpers::{print_rb_backtrace, HeapValue};
7
- use itsi_tracing::{debug, error};
6
+ use itsi_rb_helpers::{funcall_no_ret, print_rb_backtrace, HeapValue};
7
+ use itsi_tracing::debug;
8
8
  use magnus::{
9
- block::Proc,
9
+ block::{yield_values, Proc},
10
10
  error::{ErrorType, Result as MagnusResult},
11
- Error, RHash, Symbol,
11
+ Error, IntoValue, RHash, Symbol,
12
12
  };
13
13
  use magnus::{
14
14
  value::{LazyId, ReprValue},
15
15
  Ruby, Value,
16
16
  };
17
17
  use std::{fmt, io::Write, sync::Arc, time::Instant};
18
- use tokio::sync::mpsc::{self};
18
+ use tracing::error;
19
19
 
20
20
  use super::{
21
21
  itsi_body_proxy::{big_bytes::BigBytes, ItsiBody, ItsiBodyProxy},
22
- itsi_http_response::ItsiHttpResponse,
22
+ itsi_http_response::{ItsiHttpResponse, ResponseFrame},
23
23
  };
24
24
  use crate::{
25
25
  default_responses::{INTERNAL_SERVER_ERROR_RESPONSE, SERVICE_UNAVAILABLE_RESPONSE},
26
26
  server::{
27
- byte_frame::ByteFrame,
28
- http_message_types::{HttpRequest, HttpResponse},
27
+ http_message_types::{HttpBody, HttpRequest, HttpResponse},
29
28
  request_job::RequestJob,
30
29
  size_limited_incoming::MaxBodySizeReached,
31
30
  },
@@ -33,11 +32,13 @@ use crate::{
33
32
  };
34
33
 
35
34
  static ID_MESSAGE: LazyId = LazyId::new("message");
35
+ static ID_CALL: LazyId = LazyId::new("call");
36
+ static ZERO_HEADER_VALUE: HeaderValue = HeaderValue::from_static("0");
36
37
 
37
38
  #[derive(Debug)]
38
39
  #[magnus::wrap(class = "Itsi::HttpRequest", free_immediately, size)]
39
40
  pub struct ItsiHttpRequest {
40
- pub parts: Parts,
41
+ pub parts: Arc<Parts>,
41
42
  #[debug(skip)]
42
43
  pub body: ItsiBody,
43
44
  pub version: Version,
@@ -148,8 +149,10 @@ impl ItsiHttpRequest {
148
149
 
149
150
  pub fn process(self, ruby: &Ruby, app_proc: Arc<HeapValue<Proc>>) -> magnus::error::Result<()> {
150
151
  let response = self.response.clone();
151
- let result = app_proc.call::<_, Value>((self,));
152
- if let Err(err) = result {
152
+
153
+ if let Err(err) =
154
+ funcall_no_ret(app_proc.as_value(), *ID_CALL, [self.into_value_with(ruby)])
155
+ {
153
156
  Self::internal_error(ruby, response, err);
154
157
  }
155
158
  Ok(())
@@ -158,7 +161,7 @@ impl ItsiHttpRequest {
158
161
  pub fn internal_error(ruby: &Ruby, response: ItsiHttpResponse, err: Error) {
159
162
  if Self::is_connection_closed_err(ruby, &err) {
160
163
  debug!("Connection closed by client");
161
- response.close();
164
+ response.close().ok();
162
165
  } else if let Some(rb_err) = err.value() {
163
166
  print_rb_backtrace(rb_err);
164
167
  response.internal_server_error(err.to_string());
@@ -167,7 +170,7 @@ impl ItsiHttpRequest {
167
170
  }
168
171
  }
169
172
 
170
- pub fn error(self, message: String) {
173
+ pub fn error(&self, message: String) {
171
174
  self.response.internal_server_error(message);
172
175
  }
173
176
 
@@ -179,9 +182,7 @@ impl ItsiHttpRequest {
179
182
  nonblocking: bool,
180
183
  ) -> itsi_error::Result<HttpResponse> {
181
184
  match ItsiHttpRequest::new(hyper_request, context, script_name).await {
182
- Ok((request, mut receiver)) => {
183
- let shutdown_channel = context.service.shutdown_receiver.clone();
184
- let response = request.response.clone();
185
+ Ok((request, receiver)) => {
185
186
  let sender = if nonblocking {
186
187
  &context.nonblocking_sender
187
188
  } else {
@@ -192,20 +193,30 @@ impl ItsiHttpRequest {
192
193
  async_channel::TrySendError::Full(_) => Ok(SERVICE_UNAVAILABLE_RESPONSE
193
194
  .to_http_response(context.accept)
194
195
  .await),
195
- async_channel::TrySendError::Closed(err) => {
196
- error!("Error occurred: {:?}", err);
196
+ async_channel::TrySendError::Closed(_) => {
197
+ error!("Channel closed while sending request job");
197
198
  Ok(INTERNAL_SERVER_ERROR_RESPONSE
198
199
  .to_http_response(context.accept)
199
200
  .await)
200
201
  }
201
202
  },
202
- _ => match receiver.recv().await {
203
- Some(first_frame) => Ok(response
204
- .build(first_frame, receiver, shutdown_channel)
205
- .await),
206
- None => Ok(response
207
- .build(ByteFrame::Empty, receiver, shutdown_channel)
208
- .await),
203
+ Ok(_) => match receiver.await {
204
+ Ok(ResponseFrame::HttpResponse(response)) => Ok(response),
205
+ Ok(ResponseFrame::HijackedResponse(response)) => {
206
+ match response.process_hijacked_response().await {
207
+ Ok(result) => Ok(result),
208
+ Err(e) => {
209
+ error!("Error processing hijacked response: {}", e);
210
+ Ok(Response::new(HttpBody::empty()))
211
+ }
212
+ }
213
+ }
214
+ Err(_) => {
215
+ error!("Failed to receive response from receiver");
216
+ Ok(INTERNAL_SERVER_ERROR_RESPONSE
217
+ .to_http_response(context.accept)
218
+ .await)
219
+ }
209
220
  },
210
221
  }
211
222
  }
@@ -217,9 +228,18 @@ impl ItsiHttpRequest {
217
228
  request: HttpRequest,
218
229
  context: &HttpRequestContext,
219
230
  script_name: String,
220
- ) -> Result<(ItsiHttpRequest, mpsc::Receiver<ByteFrame>), HttpResponse> {
231
+ ) -> Result<
232
+ (
233
+ ItsiHttpRequest,
234
+ tokio::sync::oneshot::Receiver<ResponseFrame>,
235
+ ),
236
+ HttpResponse,
237
+ > {
221
238
  let (parts, body) = request.into_parts();
222
- let body = if context.server_params.streamable_body {
239
+ let parts = Arc::new(parts);
240
+ let body = if parts.headers.get(CONTENT_LENGTH) == Some(&ZERO_HEADER_VALUE) {
241
+ ItsiBody::Empty
242
+ } else if context.server_params.streamable_body {
223
243
  ItsiBody::Stream(ItsiBodyProxy::new(body))
224
244
  } else {
225
245
  let mut body_bytes = BigBytes::new();
@@ -228,7 +248,7 @@ impl ItsiHttpRequest {
228
248
  match chunk {
229
249
  Ok(byte_array) => body_bytes.write_all(&byte_array).unwrap(),
230
250
  Err(e) => {
231
- let mut err_resp = Response::new(BoxBody::new(Empty::new()));
251
+ let mut err_resp = Response::new(HttpBody::empty());
232
252
  if e.downcast_ref::<MaxBodySizeReached>().is_some() {
233
253
  *err_resp.status_mut() = StatusCode::PAYLOAD_TOO_LARGE;
234
254
  }
@@ -238,18 +258,22 @@ impl ItsiHttpRequest {
238
258
  }
239
259
  ItsiBody::Buffered(body_bytes)
240
260
  };
241
- let response_channel = mpsc::channel::<ByteFrame>(100);
261
+ let (sender, receiver) = tokio::sync::oneshot::channel::<ResponseFrame>();
242
262
  Ok((
243
263
  Self {
244
264
  context: context.clone(),
245
265
  version: parts.version,
246
- response: ItsiHttpResponse::new(parts.clone(), response_channel.0),
266
+ response: ItsiHttpResponse::new(
267
+ parts.clone(),
268
+ sender,
269
+ context.service.shutdown_receiver.clone(),
270
+ ),
247
271
  start: Instant::now(),
248
272
  script_name,
249
273
  body,
250
274
  parts,
251
275
  },
252
- response_channel.1,
276
+ receiver,
253
277
  ))
254
278
  }
255
279
 
@@ -328,6 +352,13 @@ impl ItsiHttpRequest {
328
352
  .collect::<Vec<(&str, &str)>>())
329
353
  }
330
354
 
355
+ pub(crate) fn each_header(&self) -> MagnusResult<()> {
356
+ self.parts.headers.iter().for_each(|(hn, hv)| {
357
+ yield_values::<_, Value>((hn.as_str(), hv.to_str().unwrap_or(""))).ok();
358
+ });
359
+ Ok(())
360
+ }
361
+
331
362
  pub(crate) fn uri(&self) -> MagnusResult<String> {
332
363
  Ok(self.parts.uri.to_string())
333
364
  }