itsi-server 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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +7 -0
  4. data/Cargo.lock +1536 -45
  5. data/README.md +4 -0
  6. data/_index.md +6 -0
  7. data/exe/itsi +33 -74
  8. data/ext/itsi_error/src/lib.rs +9 -0
  9. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  10. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  11. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  12. data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  13. data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
  14. data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
  15. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
  16. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
  17. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
  18. data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
  19. data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
  20. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
  21. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
  22. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
  23. data/ext/itsi_rb_helpers/Cargo.toml +1 -0
  24. data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
  25. data/ext/itsi_rb_helpers/src/lib.rs +34 -7
  26. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  27. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  28. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  29. data/ext/itsi_rb_helpers/target/debug/build/rb-sys-eb9ed4ff3a60f995/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  30. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
  31. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
  32. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
  33. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
  34. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
  35. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
  36. data/ext/itsi_server/Cargo.toml +69 -30
  37. data/ext/itsi_server/src/lib.rs +79 -147
  38. data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  39. data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +22 -3
  40. data/ext/itsi_server/src/ruby_types/itsi_grpc_request.rs +147 -0
  41. data/ext/itsi_server/src/ruby_types/itsi_grpc_response.rs +19 -0
  42. data/ext/itsi_server/src/ruby_types/itsi_grpc_stream/mod.rs +216 -0
  43. data/ext/itsi_server/src/{request/itsi_request.rs → ruby_types/itsi_http_request.rs} +101 -117
  44. data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +72 -41
  45. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
  46. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +355 -0
  47. data/ext/itsi_server/src/ruby_types/itsi_server.rs +82 -0
  48. data/ext/itsi_server/src/ruby_types/mod.rs +55 -0
  49. data/ext/itsi_server/src/server/bind.rs +13 -5
  50. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  51. data/ext/itsi_server/src/server/cache_store.rs +74 -0
  52. data/ext/itsi_server/src/server/itsi_service.rs +172 -0
  53. data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
  54. data/ext/itsi_server/src/server/listener.rs +102 -2
  55. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +153 -0
  56. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +47 -0
  57. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +58 -0
  58. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +82 -0
  59. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +321 -0
  60. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +139 -0
  61. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +300 -0
  62. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +287 -0
  63. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +48 -0
  64. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +127 -0
  65. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +191 -0
  66. data/ext/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +72 -0
  67. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +85 -0
  68. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +195 -0
  69. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  70. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +82 -0
  71. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +216 -0
  72. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +124 -0
  73. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
  74. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +43 -0
  75. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +34 -0
  76. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +93 -0
  77. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +162 -0
  78. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +158 -0
  79. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
  80. data/ext/itsi_server/src/server/middleware_stack/mod.rs +315 -0
  81. data/ext/itsi_server/src/server/mod.rs +8 -1
  82. data/ext/itsi_server/src/server/process_worker.rs +38 -12
  83. data/ext/itsi_server/src/server/rate_limiter.rs +565 -0
  84. data/ext/itsi_server/src/server/request_job.rs +11 -0
  85. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +119 -42
  86. data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
  87. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +256 -111
  88. data/ext/itsi_server/src/server/signal.rs +19 -0
  89. data/ext/itsi_server/src/server/static_file_server.rs +984 -0
  90. data/ext/itsi_server/src/server/thread_worker.rs +139 -94
  91. data/ext/itsi_server/src/server/types.rs +43 -0
  92. data/ext/itsi_tracing/Cargo.toml +1 -0
  93. data/ext/itsi_tracing/src/lib.rs +216 -45
  94. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
  95. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
  96. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
  97. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
  98. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
  99. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
  100. data/lib/itsi/{request.rb → http_request.rb} +29 -5
  101. data/lib/itsi/http_response.rb +39 -0
  102. data/lib/itsi/server/Itsi.rb +11 -19
  103. data/lib/itsi/server/config/dsl.rb +506 -0
  104. data/lib/itsi/server/config.rb +103 -8
  105. data/lib/itsi/server/default_app/default_app.rb +38 -0
  106. data/lib/itsi/server/grpc_interface.rb +213 -0
  107. data/lib/itsi/server/rack/handler/itsi.rb +8 -17
  108. data/lib/itsi/server/rack_interface.rb +23 -4
  109. data/lib/itsi/server/scheduler_interface.rb +1 -1
  110. data/lib/itsi/server/scheduler_mode.rb +4 -0
  111. data/lib/itsi/server/signal_trap.rb +7 -1
  112. data/lib/itsi/server/version.rb +1 -1
  113. data/lib/itsi/server.rb +74 -63
  114. data/lib/itsi/standard_headers.rb +86 -0
  115. metadata +84 -15
  116. data/ext/itsi_scheduler/extconf.rb +0 -6
  117. data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  118. data/ext/itsi_server/src/request/mod.rs +0 -1
  119. data/ext/itsi_server/src/response/mod.rs +0 -1
  120. data/ext/itsi_server/src/server/itsi_server.rs +0 -288
  121. data/lib/itsi/server/options_dsl.rb +0 -401
  122. data/lib/itsi/stream_io.rb +0 -38
  123. /data/lib/itsi/{index.html → server/default_app/index.html} +0 -0
@@ -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
+ }
@@ -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
  }