itsi-scheduler 0.1.5 → 0.1.14
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.
Potentially problematic release.
This version of itsi-scheduler might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +7 -0
- data/Cargo.lock +83 -22
- data/README.md +5 -0
- data/_index.md +7 -0
- data/ext/itsi_error/src/from.rs +26 -29
- data/ext/itsi_error/src/lib.rs +10 -1
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
- data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
- data/ext/itsi_rb_helpers/Cargo.toml +1 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
- data/ext/itsi_rb_helpers/src/lib.rs +59 -9
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
- 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
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +69 -26
- data/ext/itsi_server/src/env.rs +43 -0
- data/ext/itsi_server/src/lib.rs +81 -75
- data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
- data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +22 -3
- data/ext/itsi_server/src/ruby_types/itsi_grpc_request.rs +147 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_response.rs +19 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_stream/mod.rs +216 -0
- data/ext/itsi_server/src/{request/itsi_request.rs → ruby_types/itsi_http_request.rs} +108 -103
- data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +79 -38
- data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +355 -0
- data/ext/itsi_server/src/ruby_types/itsi_server.rs +82 -0
- data/ext/itsi_server/src/ruby_types/mod.rs +55 -0
- data/ext/itsi_server/src/server/bind.rs +33 -20
- data/ext/itsi_server/src/server/byte_frame.rs +32 -0
- data/ext/itsi_server/src/server/cache_store.rs +74 -0
- data/ext/itsi_server/src/server/itsi_service.rs +172 -0
- data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
- data/ext/itsi_server/src/server/listener.rs +197 -106
- data/ext/itsi_server/src/server/middleware_stack/middleware.rs +153 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +47 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +58 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +264 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +139 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +300 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +287 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +48 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +127 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +191 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +72 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +85 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +195 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +216 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +124 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +43 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +34 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +93 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +162 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +158 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
- data/ext/itsi_server/src/server/middleware_stack/mod.rs +315 -0
- data/ext/itsi_server/src/server/mod.rs +8 -1
- data/ext/itsi_server/src/server/process_worker.rs +44 -11
- data/ext/itsi_server/src/server/rate_limiter.rs +565 -0
- data/ext/itsi_server/src/server/request_job.rs +11 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +129 -46
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +337 -163
- data/ext/itsi_server/src/server/signal.rs +25 -2
- data/ext/itsi_server/src/server/static_file_server.rs +984 -0
- data/ext/itsi_server/src/server/thread_worker.rs +164 -88
- data/ext/itsi_server/src/server/tls/locked_dir_cache.rs +55 -17
- data/ext/itsi_server/src/server/tls.rs +104 -28
- data/ext/itsi_server/src/server/types.rs +43 -0
- data/ext/itsi_tracing/Cargo.toml +1 -0
- data/ext/itsi_tracing/src/lib.rs +222 -34
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
- data/lib/itsi/scheduler/version.rb +1 -1
- data/lib/itsi/scheduler.rb +2 -2
- metadata +79 -14
- data/ext/itsi_server/extconf.rb +0 -6
- data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
- data/ext/itsi_server/src/request/mod.rs +0 -1
- data/ext/itsi_server/src/response/mod.rs +0 -1
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -13
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -5
- data/ext/itsi_server/src/server/itsi_server.rs +0 -244
@@ -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,57 +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::{SockAddr, TokioListener},
|
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, 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;
|
6
|
+
use itsi_rb_helpers::{print_rb_backtrace, HeapValue};
|
20
7
|
use itsi_tracing::{debug, error};
|
21
8
|
use magnus::{
|
9
|
+
block::Proc,
|
22
10
|
error::{ErrorType, Result as MagnusResult},
|
23
11
|
Error,
|
24
12
|
};
|
25
13
|
use magnus::{
|
26
|
-
value::{LazyId,
|
27
|
-
|
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,
|
28
23
|
};
|
29
|
-
use
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
use crate::server::{
|
25
|
+
byte_frame::ByteFrame,
|
26
|
+
itsi_service::RequestContext,
|
27
|
+
request_job::RequestJob,
|
28
|
+
types::{HttpRequest, HttpResponse},
|
33
29
|
};
|
34
|
-
|
30
|
+
|
35
31
|
static ID_MESSAGE: LazyId = LazyId::new("message");
|
36
|
-
static ID_BACKTRACE: LazyId = LazyId::new("backtrace");
|
37
32
|
|
38
33
|
#[derive(Debug)]
|
39
|
-
#[magnus::wrap(class = "Itsi::
|
40
|
-
pub struct
|
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
|
45
|
-
pub
|
46
|
-
#[debug(skip)]
|
47
|
-
pub(crate) listener: Arc<TokioListener>,
|
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,
|
42
|
+
#[debug(skip)]
|
43
|
+
pub context: RequestContext,
|
52
44
|
}
|
53
45
|
|
54
|
-
impl fmt::Display for
|
46
|
+
impl fmt::Display for ItsiHttpRequest {
|
55
47
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
56
48
|
write!(
|
57
49
|
f,
|
@@ -63,7 +55,7 @@ impl fmt::Display for ItsiRequest {
|
|
63
55
|
}
|
64
56
|
}
|
65
57
|
|
66
|
-
impl
|
58
|
+
impl ItsiHttpRequest {
|
67
59
|
pub fn is_connection_closed_err(ruby: &Ruby, err: &Error) -> bool {
|
68
60
|
match err.error_type() {
|
69
61
|
ErrorType::Jump(_) => false,
|
@@ -81,39 +73,37 @@ impl ItsiRequest {
|
|
81
73
|
}
|
82
74
|
}
|
83
75
|
}
|
76
|
+
fn content_type_str(&self) -> &str {
|
77
|
+
self.parts
|
78
|
+
.headers
|
79
|
+
.get("Content-Type")
|
80
|
+
.and_then(|hv| hv.to_str().ok())
|
81
|
+
.unwrap_or("application/x-www-form-urlencoded")
|
82
|
+
}
|
83
|
+
|
84
|
+
pub fn is_json(&self) -> bool {
|
85
|
+
self.content_type_str() == "application/json"
|
86
|
+
}
|
84
87
|
|
85
|
-
pub fn
|
86
|
-
self
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
) -> magnus::error::Result<()> {
|
91
|
-
let req = format!("{}", self);
|
88
|
+
pub fn is_html(&self) -> bool {
|
89
|
+
self.content_type_str() == "text/html"
|
90
|
+
}
|
91
|
+
|
92
|
+
pub fn process(self, ruby: &Ruby, app_proc: Arc<HeapValue<Proc>>) -> magnus::error::Result<()> {
|
92
93
|
let response = self.response.clone();
|
93
|
-
let
|
94
|
-
debug!("{} Started", req);
|
95
|
-
let result = server.funcall::<_, _, Value>(*ID_CALL, (app, self));
|
94
|
+
let result = app_proc.call::<_, Value>((self,));
|
96
95
|
if let Err(err) = result {
|
97
96
|
Self::internal_error(ruby, response, err);
|
98
97
|
}
|
99
|
-
debug!("{} Finished in {:?}", req, start.elapsed());
|
100
|
-
|
101
98
|
Ok(())
|
102
99
|
}
|
103
100
|
|
104
|
-
pub fn internal_error(ruby: &Ruby, response:
|
101
|
+
pub fn internal_error(ruby: &Ruby, response: ItsiHttpResponse, err: Error) {
|
105
102
|
if Self::is_connection_closed_err(ruby, &err) {
|
106
103
|
debug!("Connection closed by client");
|
107
104
|
response.close();
|
108
105
|
} else if let Some(rb_err) = err.value() {
|
109
|
-
|
110
|
-
.funcall::<_, _, Vec<String>>(*ID_BACKTRACE, ())
|
111
|
-
.unwrap_or_default();
|
112
|
-
|
113
|
-
error!("Error occurred in Handler: {:?}", rb_err);
|
114
|
-
for line in backtrace {
|
115
|
-
error!("{}", line);
|
116
|
-
}
|
106
|
+
print_rb_backtrace(rb_err);
|
117
107
|
response.internal_server_error(err.to_string());
|
118
108
|
} else {
|
119
109
|
response.internal_server_error(err.to_string());
|
@@ -125,17 +115,18 @@ impl ItsiRequest {
|
|
125
115
|
}
|
126
116
|
|
127
117
|
pub(crate) async fn process_request(
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
) -> itsi_error::Result<Response<BoxBody<Bytes, Infallible>>> {
|
135
|
-
let (request, mut receiver) = ItsiRequest::new(hyper_request, addr, server, listener).await;
|
136
|
-
|
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();
|
137
124
|
let response = request.response.clone();
|
138
|
-
match
|
125
|
+
match context
|
126
|
+
.sender
|
127
|
+
.send(RequestJob::ProcessHttpRequest(request, app))
|
128
|
+
.await
|
129
|
+
{
|
139
130
|
Err(err) => {
|
140
131
|
error!("Error occurred: {}", err);
|
141
132
|
let mut response = Response::new(BoxBody::new(Empty::new()));
|
@@ -143,20 +134,22 @@ impl ItsiRequest {
|
|
143
134
|
Ok(response)
|
144
135
|
}
|
145
136
|
_ => match receiver.recv().await {
|
146
|
-
Some(first_frame) => Ok(response
|
147
|
-
|
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),
|
148
143
|
},
|
149
144
|
}
|
150
145
|
}
|
151
146
|
|
152
147
|
pub(crate) async fn new(
|
153
|
-
request:
|
154
|
-
|
155
|
-
|
156
|
-
listener: Arc<TokioListener>,
|
157
|
-
) -> (ItsiRequest, mpsc::Receiver<Option<Bytes>>) {
|
148
|
+
request: HttpRequest,
|
149
|
+
context: &RequestContext,
|
150
|
+
) -> (ItsiHttpRequest, mpsc::Receiver<ByteFrame>) {
|
158
151
|
let (parts, body) = request.into_parts();
|
159
|
-
let body = if
|
152
|
+
let body = if context.server_params.streamable_body {
|
160
153
|
ItsiBody::Stream(ItsiBodyProxy::new(body))
|
161
154
|
} else {
|
162
155
|
let mut body_bytes = BigBytes::new();
|
@@ -167,16 +160,14 @@ impl ItsiRequest {
|
|
167
160
|
}
|
168
161
|
ItsiBody::Buffered(body_bytes)
|
169
162
|
};
|
170
|
-
let response_channel = mpsc::channel::<
|
163
|
+
let response_channel = mpsc::channel::<ByteFrame>(100);
|
171
164
|
(
|
172
165
|
Self {
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
listener,
|
177
|
-
version: format!("{:?}", &parts.version),
|
178
|
-
response: ItsiResponse::new(parts.clone(), response_channel.0),
|
166
|
+
context: context.clone(),
|
167
|
+
version: parts.version,
|
168
|
+
response: ItsiHttpResponse::new(parts.clone(), response_channel.0),
|
179
169
|
start: Instant::now(),
|
170
|
+
body,
|
180
171
|
parts,
|
181
172
|
},
|
182
173
|
response_channel.1,
|
@@ -188,12 +179,12 @@ impl ItsiRequest {
|
|
188
179
|
.parts
|
189
180
|
.uri
|
190
181
|
.path()
|
191
|
-
.strip_prefix(&self.
|
182
|
+
.strip_prefix(&self.context.server_params.script_name)
|
192
183
|
.unwrap_or(self.parts.uri.path()))
|
193
184
|
}
|
194
185
|
|
195
186
|
pub(crate) fn script_name(&self) -> MagnusResult<&str> {
|
196
|
-
Ok(&self.
|
187
|
+
Ok(&self.context.server_params.script_name)
|
197
188
|
}
|
198
189
|
|
199
190
|
pub(crate) fn query_string(&self) -> MagnusResult<&str> {
|
@@ -205,7 +196,14 @@ impl ItsiRequest {
|
|
205
196
|
}
|
206
197
|
|
207
198
|
pub(crate) fn version(&self) -> MagnusResult<&str> {
|
208
|
-
Ok(
|
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
|
+
})
|
209
207
|
}
|
210
208
|
|
211
209
|
pub(crate) fn rack_protocol(&self) -> MagnusResult<Vec<&str>> {
|
@@ -225,53 +223,60 @@ impl ItsiRequest {
|
|
225
223
|
.unwrap_or_else(|| vec!["http"]))
|
226
224
|
}
|
227
225
|
|
228
|
-
pub(crate) fn host(&self) -> MagnusResult
|
226
|
+
pub(crate) fn host(&self) -> MagnusResult<&str> {
|
229
227
|
Ok(self
|
230
228
|
.parts
|
231
229
|
.uri
|
232
230
|
.host()
|
233
|
-
.
|
234
|
-
.unwrap_or_else(|| self.listener.host()))
|
231
|
+
.unwrap_or_else(|| &self.context.listener.host))
|
235
232
|
}
|
236
233
|
|
237
|
-
pub(crate) fn scheme(&self) -> MagnusResult
|
234
|
+
pub(crate) fn scheme(&self) -> MagnusResult<&str> {
|
238
235
|
Ok(self
|
239
236
|
.parts
|
240
237
|
.uri
|
241
238
|
.scheme()
|
242
|
-
.map(|scheme| scheme.
|
243
|
-
.unwrap_or_else(|| self.listener.scheme
|
239
|
+
.map(|scheme| scheme.as_str())
|
240
|
+
.unwrap_or_else(|| &self.context.listener.scheme))
|
244
241
|
}
|
245
242
|
|
246
|
-
pub(crate) fn headers(&self) -> MagnusResult<Vec<(
|
243
|
+
pub(crate) fn headers(&self) -> MagnusResult<Vec<(&str, &str)>> {
|
247
244
|
Ok(self
|
248
245
|
.parts
|
249
246
|
.headers
|
250
247
|
.iter()
|
251
|
-
.map(|(hn, hv)|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
.
|
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))
|
260
261
|
}
|
261
262
|
|
262
263
|
pub(crate) fn remote_addr(&self) -> MagnusResult<&str> {
|
263
|
-
Ok(&self.
|
264
|
+
Ok(&self.context.addr)
|
264
265
|
}
|
265
266
|
|
266
267
|
pub(crate) fn port(&self) -> MagnusResult<u16> {
|
267
|
-
Ok(self
|
268
|
+
Ok(self
|
269
|
+
.parts
|
270
|
+
.uri
|
271
|
+
.port_u16()
|
272
|
+
.unwrap_or(self.context.listener.port))
|
268
273
|
}
|
269
274
|
|
270
|
-
pub(crate) fn body(&self) -> MagnusResult<Value
|
275
|
+
pub(crate) fn body(&self) -> MagnusResult<Option<Value>> {
|
271
276
|
Ok(self.body.into_value())
|
272
277
|
}
|
273
278
|
|
274
|
-
pub(crate) fn response(&self) -> MagnusResult<
|
279
|
+
pub(crate) fn response(&self) -> MagnusResult<ItsiHttpResponse> {
|
275
280
|
Ok(self.response.clone())
|
276
281
|
}
|
277
282
|
}
|