itsi-server 0.1.1 → 0.1.11
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-server might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Cargo.lock +2926 -0
- data/Cargo.toml +7 -0
- data/Rakefile +8 -1
- data/exe/itsi +119 -29
- data/ext/itsi_error/Cargo.toml +2 -0
- data/ext/itsi_error/src/from.rs +68 -0
- data/ext/itsi_error/src/lib.rs +13 -38
- data/ext/itsi_instrument_entry/Cargo.toml +15 -0
- data/ext/itsi_instrument_entry/src/lib.rs +31 -0
- data/ext/itsi_rb_helpers/Cargo.toml +2 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
- data/ext/itsi_rb_helpers/src/lib.rs +112 -9
- data/ext/itsi_scheduler/Cargo.toml +24 -0
- data/ext/itsi_scheduler/extconf.rb +6 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
- data/ext/itsi_scheduler/src/lib.rs +38 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +25 -4
- data/ext/itsi_server/extconf.rb +1 -1
- data/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
- data/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
- data/ext/itsi_server/src/body_proxy/mod.rs +2 -0
- data/ext/itsi_server/src/env.rs +43 -0
- data/ext/itsi_server/src/lib.rs +136 -8
- data/ext/itsi_server/src/request/itsi_request.rs +258 -103
- data/ext/itsi_server/src/response/itsi_response.rs +357 -0
- data/ext/itsi_server/src/response/mod.rs +1 -0
- data/ext/itsi_server/src/server/bind.rs +65 -29
- data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
- data/ext/itsi_server/src/server/io_stream.rs +104 -0
- data/ext/itsi_server/src/server/itsi_server.rs +245 -139
- data/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
- data/ext/itsi_server/src/server/listener.rs +237 -137
- data/ext/itsi_server/src/server/mod.rs +7 -1
- data/ext/itsi_server/src/server/process_worker.rs +203 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +260 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +276 -0
- data/ext/itsi_server/src/server/signal.rs +74 -0
- data/ext/itsi_server/src/server/thread_worker.rs +399 -0
- data/ext/itsi_server/src/server/tls/locked_dir_cache.rs +132 -0
- data/ext/itsi_server/src/server/tls.rs +187 -60
- data/ext/itsi_tracing/Cargo.toml +4 -0
- data/ext/itsi_tracing/src/lib.rs +53 -6
- data/lib/itsi/index.html +91 -0
- data/lib/itsi/request.rb +38 -14
- data/lib/itsi/server/Itsi.rb +127 -0
- data/lib/itsi/server/config.rb +36 -0
- data/lib/itsi/server/options_dsl.rb +401 -0
- data/lib/itsi/server/rack/handler/itsi.rb +36 -0
- data/lib/itsi/server/rack_interface.rb +75 -0
- data/lib/itsi/server/scheduler_interface.rb +21 -0
- data/lib/itsi/server/scheduler_mode.rb +6 -0
- data/lib/itsi/server/signal_trap.rb +23 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +79 -9
- data/lib/itsi/stream_io.rb +38 -0
- metadata +49 -27
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -32
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -52
- data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
- data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
@@ -1,143 +1,298 @@
|
|
1
|
-
use
|
2
|
-
|
3
|
-
|
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
|
+
};
|
4
13
|
use bytes::Bytes;
|
5
|
-
use
|
6
|
-
use
|
14
|
+
use derive_more::Debug;
|
15
|
+
use futures::StreamExt;
|
16
|
+
use http::{request::Parts, HeaderValue, Response, StatusCode};
|
17
|
+
use http_body_util::{combinators::BoxBody, BodyExt, Empty};
|
7
18
|
use hyper::{body::Incoming, Request};
|
8
|
-
use
|
19
|
+
use itsi_error::from::CLIENT_CONNECTION_CLOSED;
|
20
|
+
use itsi_rb_helpers::print_rb_backtrace;
|
21
|
+
use itsi_tracing::{debug, error};
|
22
|
+
use magnus::{
|
23
|
+
error::{ErrorType, Result as MagnusResult},
|
24
|
+
Error,
|
25
|
+
};
|
26
|
+
use magnus::{
|
27
|
+
value::{LazyId, Opaque, ReprValue},
|
28
|
+
RClass, Ruby, Value,
|
29
|
+
};
|
30
|
+
use std::{convert::Infallible, fmt, io::Write, sync::Arc, time::Instant};
|
31
|
+
use tokio::sync::{
|
32
|
+
mpsc::{self},
|
33
|
+
watch,
|
34
|
+
};
|
35
|
+
static ID_CALL: LazyId = LazyId::new("call");
|
36
|
+
static ID_MESSAGE: LazyId = LazyId::new("message");
|
9
37
|
|
10
|
-
#[magnus::wrap(class = "Itsi::Request", free_immediately, size)]
|
11
38
|
#[derive(Debug)]
|
39
|
+
#[magnus::wrap(class = "Itsi::Request", free_immediately, size)]
|
12
40
|
pub struct ItsiRequest {
|
13
|
-
pub path: String,
|
14
|
-
pub script_name: String,
|
15
|
-
pub query_string: String,
|
16
|
-
pub method: String,
|
17
|
-
pub version: String,
|
18
|
-
pub rack_protocol: Vec<String>,
|
19
|
-
pub host: String,
|
20
|
-
pub scheme: String,
|
21
|
-
pub headers: HashMap<String, String>,
|
22
|
-
pub remote_addr: String,
|
23
|
-
pub port: u16,
|
24
|
-
pub body: Bytes,
|
25
41
|
pub parts: Parts,
|
42
|
+
#[debug(skip)]
|
43
|
+
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,
|
51
|
+
pub start: Instant,
|
52
|
+
pub content_type: String,
|
53
|
+
}
|
54
|
+
|
55
|
+
impl fmt::Display for ItsiRequest {
|
56
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
57
|
+
write!(
|
58
|
+
f,
|
59
|
+
"{} {} {}",
|
60
|
+
self.version().unwrap(),
|
61
|
+
self.method().unwrap(),
|
62
|
+
self.path().unwrap()
|
63
|
+
)
|
64
|
+
}
|
26
65
|
}
|
27
66
|
|
28
67
|
impl ItsiRequest {
|
29
|
-
pub(
|
68
|
+
pub fn is_connection_closed_err(ruby: &Ruby, err: &Error) -> bool {
|
69
|
+
match err.error_type() {
|
70
|
+
ErrorType::Jump(_) => false,
|
71
|
+
ErrorType::Error(_, _) => false,
|
72
|
+
ErrorType::Exception(exception) => {
|
73
|
+
exception.is_kind_of(ruby.exception_eof_error())
|
74
|
+
&& err
|
75
|
+
.value()
|
76
|
+
.map(|v| {
|
77
|
+
v.funcall::<_, _, String>(*ID_MESSAGE, ())
|
78
|
+
.unwrap_or("".to_string())
|
79
|
+
.eq(CLIENT_CONNECTION_CLOSED)
|
80
|
+
})
|
81
|
+
.unwrap_or(false)
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
pub fn is_json(&self) -> bool {
|
87
|
+
self.content_type.eq("application/json")
|
88
|
+
}
|
89
|
+
|
90
|
+
pub fn is_html(&self) -> bool {
|
91
|
+
self.content_type.eq("text/html")
|
92
|
+
}
|
93
|
+
|
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);
|
101
|
+
let response = self.response.clone();
|
102
|
+
let start = self.start;
|
103
|
+
debug!("{} Started", req);
|
104
|
+
let result = server.funcall::<_, _, Value>(*ID_CALL, (app, self));
|
105
|
+
if let Err(err) = result {
|
106
|
+
Self::internal_error(ruby, response, err);
|
107
|
+
}
|
108
|
+
debug!("{} Finished in {:?}", req, start.elapsed());
|
109
|
+
|
110
|
+
Ok(())
|
111
|
+
}
|
112
|
+
|
113
|
+
pub fn internal_error(ruby: &Ruby, response: ItsiResponse, err: Error) {
|
114
|
+
if Self::is_connection_closed_err(ruby, &err) {
|
115
|
+
debug!("Connection closed by client");
|
116
|
+
response.close();
|
117
|
+
} else if let Some(rb_err) = err.value() {
|
118
|
+
print_rb_backtrace(rb_err);
|
119
|
+
response.internal_server_error(err.to_string());
|
120
|
+
} else {
|
121
|
+
response.internal_server_error(err.to_string());
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
pub fn error(self, message: String) {
|
126
|
+
self.response.internal_server_error(message);
|
127
|
+
}
|
128
|
+
|
129
|
+
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
|
+
|
139
|
+
let response = request.response.clone();
|
140
|
+
match sender.send(RequestJob::ProcessRequest(request)).await {
|
141
|
+
Err(err) => {
|
142
|
+
error!("Error occurred: {}", err);
|
143
|
+
let mut response = Response::new(BoxBody::new(Empty::new()));
|
144
|
+
*response.status_mut() = StatusCode::BAD_REQUEST;
|
145
|
+
Ok(response)
|
146
|
+
}
|
147
|
+
_ => 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),
|
150
|
+
},
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
pub(crate) async fn new(
|
30
155
|
request: Request<Incoming>,
|
31
156
|
sock_addr: SockAddr,
|
32
|
-
|
33
|
-
listener: Arc<
|
34
|
-
) ->
|
157
|
+
server: Arc<Server>,
|
158
|
+
listener: Arc<ListenerInfo>,
|
159
|
+
) -> (ItsiRequest, mpsc::Receiver<Option<Bytes>>) {
|
35
160
|
let (parts, body) = request.into_parts();
|
36
|
-
let
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
.
|
41
|
-
.
|
42
|
-
|
43
|
-
|
44
|
-
|
161
|
+
let body = if server.stream_body.is_some_and(|f| f) {
|
162
|
+
ItsiBody::Stream(ItsiBodyProxy::new(body))
|
163
|
+
} else {
|
164
|
+
let mut body_bytes = BigBytes::new();
|
165
|
+
let mut stream = body.into_data_stream();
|
166
|
+
while let Some(chunk) = stream.next().await {
|
167
|
+
let byte_array = chunk.unwrap().to_vec();
|
168
|
+
body_bytes.write_all(&byte_array).unwrap();
|
169
|
+
}
|
170
|
+
ItsiBody::Buffered(body_bytes)
|
171
|
+
};
|
172
|
+
let response_channel = mpsc::channel::<Option<Bytes>>(100);
|
173
|
+
(
|
174
|
+
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
|
+
),
|
191
|
+
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
|
+
))
|
45
198
|
.to_str()
|
46
|
-
.
|
47
|
-
.
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
let host = parts
|
54
|
-
.uri
|
55
|
-
.host()
|
56
|
-
.map(ToOwned::to_owned)
|
57
|
-
.unwrap_or_else(|| listener.host());
|
58
|
-
|
59
|
-
let scheme = parts
|
60
|
-
.uri
|
61
|
-
.scheme()
|
62
|
-
.map(|s| s.to_string())
|
63
|
-
.unwrap_or_else(|| listener.scheme());
|
64
|
-
|
65
|
-
let headers = parts
|
66
|
-
.headers
|
67
|
-
.iter()
|
68
|
-
.map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
|
69
|
-
.collect();
|
199
|
+
.unwrap()
|
200
|
+
.to_string(),
|
201
|
+
parts,
|
202
|
+
},
|
203
|
+
response_channel.1,
|
204
|
+
)
|
205
|
+
}
|
70
206
|
|
71
|
-
|
207
|
+
pub(crate) fn path(&self) -> MagnusResult<&str> {
|
208
|
+
Ok(self
|
209
|
+
.parts
|
72
210
|
.uri
|
73
211
|
.path()
|
74
|
-
.strip_prefix(&script_name)
|
75
|
-
.unwrap_or(parts.uri.path())
|
76
|
-
.to_string();
|
77
|
-
|
78
|
-
let version = format!("{:?}", parts.version);
|
79
|
-
let body = body.collect().await.unwrap().to_bytes();
|
80
|
-
|
81
|
-
Self {
|
82
|
-
remote_addr: sock_addr.to_string(),
|
83
|
-
body,
|
84
|
-
parts,
|
85
|
-
script_name,
|
86
|
-
query_string,
|
87
|
-
method,
|
88
|
-
headers,
|
89
|
-
path,
|
90
|
-
version,
|
91
|
-
rack_protocol,
|
92
|
-
host,
|
93
|
-
scheme,
|
94
|
-
port,
|
95
|
-
}
|
212
|
+
.strip_prefix(&self.server.script_name)
|
213
|
+
.unwrap_or(self.parts.uri.path()))
|
96
214
|
}
|
97
|
-
}
|
98
215
|
|
99
|
-
|
100
|
-
|
101
|
-
Ok(self.path.clone())
|
216
|
+
pub(crate) fn script_name(&self) -> MagnusResult<&str> {
|
217
|
+
Ok(&self.server.script_name)
|
102
218
|
}
|
103
219
|
|
104
|
-
pub(crate) fn
|
105
|
-
Ok(self.
|
220
|
+
pub(crate) fn query_string(&self) -> MagnusResult<&str> {
|
221
|
+
Ok(self.parts.uri.query().unwrap_or(""))
|
106
222
|
}
|
107
223
|
|
108
|
-
pub(crate) fn
|
109
|
-
Ok(self.
|
224
|
+
pub(crate) fn method(&self) -> MagnusResult<&str> {
|
225
|
+
Ok(self.parts.method.as_str())
|
110
226
|
}
|
111
227
|
|
112
|
-
pub(crate) fn
|
113
|
-
Ok(self.
|
228
|
+
pub(crate) fn version(&self) -> MagnusResult<&str> {
|
229
|
+
Ok(&self.version)
|
114
230
|
}
|
115
231
|
|
116
|
-
pub(crate) fn
|
117
|
-
Ok(self
|
232
|
+
pub(crate) fn rack_protocol(&self) -> MagnusResult<Vec<&str>> {
|
233
|
+
Ok(self
|
234
|
+
.parts
|
235
|
+
.headers
|
236
|
+
.get("upgrade")
|
237
|
+
.or_else(|| self.parts.headers.get("protocol"))
|
238
|
+
.map(|value| {
|
239
|
+
value
|
240
|
+
.to_str()
|
241
|
+
.unwrap_or("")
|
242
|
+
.split(',')
|
243
|
+
.map(|s| s.trim())
|
244
|
+
.collect::<Vec<&str>>()
|
245
|
+
})
|
246
|
+
.unwrap_or_else(|| vec!["http"]))
|
247
|
+
}
|
248
|
+
|
249
|
+
pub(crate) fn host(&self) -> MagnusResult<String> {
|
250
|
+
Ok(self
|
251
|
+
.parts
|
252
|
+
.uri
|
253
|
+
.host()
|
254
|
+
.map(|host| host.to_string())
|
255
|
+
.unwrap_or_else(|| self.listener.host.clone()))
|
118
256
|
}
|
119
257
|
|
120
|
-
pub(crate) fn
|
121
|
-
Ok(self
|
258
|
+
pub(crate) fn scheme(&self) -> MagnusResult<String> {
|
259
|
+
Ok(self
|
260
|
+
.parts
|
261
|
+
.uri
|
262
|
+
.scheme()
|
263
|
+
.map(|scheme| scheme.to_string())
|
264
|
+
.unwrap_or_else(|| self.listener.scheme.clone()))
|
122
265
|
}
|
123
266
|
|
124
|
-
pub(crate) fn
|
125
|
-
Ok(self
|
267
|
+
pub(crate) fn headers(&self) -> MagnusResult<Vec<(String, &str)>> {
|
268
|
+
Ok(self
|
269
|
+
.parts
|
270
|
+
.headers
|
271
|
+
.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())
|
126
281
|
}
|
127
282
|
|
128
|
-
pub(crate) fn
|
129
|
-
Ok(self.
|
283
|
+
pub(crate) fn remote_addr(&self) -> MagnusResult<&str> {
|
284
|
+
Ok(&self.remote_addr)
|
130
285
|
}
|
131
286
|
|
132
|
-
pub(crate) fn
|
133
|
-
Ok(self.
|
287
|
+
pub(crate) fn port(&self) -> MagnusResult<u16> {
|
288
|
+
Ok(self.parts.uri.port_u16().unwrap_or(self.listener.port))
|
134
289
|
}
|
135
290
|
|
136
|
-
pub(crate) fn
|
137
|
-
Ok(self.
|
291
|
+
pub(crate) fn body(&self) -> MagnusResult<Value> {
|
292
|
+
Ok(self.body.into_value())
|
138
293
|
}
|
139
294
|
|
140
|
-
pub(crate) fn
|
141
|
-
Ok(self.
|
295
|
+
pub(crate) fn response(&self) -> MagnusResult<ItsiResponse> {
|
296
|
+
Ok(self.response.clone())
|
142
297
|
}
|
143
298
|
}
|