itsi-server 0.1.1 → 0.1.9
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 +2917 -0
- data/Cargo.toml +7 -0
- data/Rakefile +8 -1
- data/exe/itsi +88 -28
- 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 +90 -10
- 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 +21 -3
- 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 +67 -7
- data/ext/itsi_server/src/request/itsi_request.rs +265 -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 +57 -25
- 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 +251 -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 +196 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +263 -0
- data/ext/itsi_server/src/server/signal.rs +77 -0
- data/ext/itsi_server/src/server/thread_worker.rs +367 -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/rack/handler/itsi.rb +24 -0
- data/lib/itsi/server/rack_interface.rb +79 -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 +24 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +75 -9
- data/lib/itsi/stream_io.rb +38 -0
- metadata +46 -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,56 +1,150 @@
|
|
1
1
|
use super::{
|
2
2
|
bind::Bind,
|
3
|
-
listener::
|
3
|
+
listener::Listener,
|
4
|
+
serve_strategy::{cluster_mode::ClusterMode, single_mode::SingleMode},
|
5
|
+
signal::{
|
6
|
+
clear_signal_handlers, reset_signal_handlers, send_shutdown_event, SIGNAL_HANDLER_CHANNEL,
|
7
|
+
},
|
4
8
|
};
|
5
|
-
use crate::{request::itsi_request::ItsiRequest,
|
6
|
-
use bytes::Bytes;
|
9
|
+
use crate::{request::itsi_request::ItsiRequest, server::serve_strategy::ServeStrategy};
|
7
10
|
use derive_more::Debug;
|
8
|
-
use
|
9
|
-
use
|
10
|
-
body::Incoming, header::HeaderName, service::service_fn, HeaderMap, Request, Response,
|
11
|
-
StatusCode,
|
12
|
-
};
|
13
|
-
use hyper_util::{rt::TokioExecutor, server::conn::auto::Builder};
|
14
|
-
use itsi_tracing::{error, info};
|
11
|
+
use itsi_rb_helpers::{call_without_gvl, HeapVal};
|
12
|
+
use itsi_tracing::{error, run_silently};
|
15
13
|
use magnus::{
|
14
|
+
block::Proc,
|
16
15
|
error::Result,
|
17
|
-
scan_args::{get_kwargs, scan_args, Args, KwArgs},
|
18
|
-
value::{Opaque, ReprValue},
|
19
|
-
RHash, Ruby, Value,
|
16
|
+
scan_args::{get_kwargs, scan_args, Args, KwArgs, ScanArgsKw, ScanArgsOpt, ScanArgsRequired},
|
17
|
+
value::{InnerValue, Opaque, ReprValue},
|
18
|
+
ArgList, RHash, Ruby, Symbol, Value,
|
20
19
|
};
|
21
|
-
use parking_lot::Mutex;
|
22
|
-
use std::{
|
23
|
-
use
|
24
|
-
|
20
|
+
use parking_lot::{Mutex, RwLock};
|
21
|
+
use std::{cmp::max, ops::Deref, sync::Arc};
|
22
|
+
use tracing::{info, instrument};
|
23
|
+
|
24
|
+
static DEFAULT_BIND: &str = "http://localhost:3000";
|
25
25
|
|
26
26
|
#[magnus::wrap(class = "Itsi::Server", free_immediately, size)]
|
27
|
-
#[derive(
|
27
|
+
#[derive(Clone)]
|
28
28
|
pub struct Server {
|
29
|
+
pub config: Arc<ServerConfig>,
|
30
|
+
}
|
31
|
+
|
32
|
+
impl Deref for Server {
|
33
|
+
type Target = ServerConfig;
|
34
|
+
|
35
|
+
fn deref(&self) -> &Self::Target {
|
36
|
+
&self.config
|
37
|
+
}
|
38
|
+
}
|
39
|
+
type AfterFork = Mutex<Arc<Option<Box<dyn Fn() + Send + Sync>>>>;
|
40
|
+
|
41
|
+
#[derive(Debug)]
|
42
|
+
pub struct ServerConfig {
|
29
43
|
#[debug(skip)]
|
30
|
-
app:
|
44
|
+
pub app: HeapVal,
|
31
45
|
#[allow(unused)]
|
32
|
-
workers:
|
46
|
+
pub workers: u8,
|
33
47
|
#[allow(unused)]
|
34
|
-
threads:
|
48
|
+
pub threads: u8,
|
35
49
|
#[allow(unused)]
|
36
|
-
shutdown_timeout: f64,
|
37
|
-
script_name: String,
|
50
|
+
pub shutdown_timeout: f64,
|
51
|
+
pub script_name: String,
|
38
52
|
pub(crate) binds: Mutex<Vec<Bind>>,
|
53
|
+
#[debug(skip)]
|
54
|
+
pub before_fork: Mutex<Option<Box<dyn FnOnce() + Send + Sync>>>,
|
55
|
+
#[debug(skip)]
|
56
|
+
pub after_fork: AfterFork,
|
57
|
+
pub scheduler_class: Option<String>,
|
58
|
+
pub stream_body: Option<bool>,
|
59
|
+
pub worker_memory_limit: Option<u64>,
|
60
|
+
#[debug(skip)]
|
61
|
+
pub(crate) strategy: RwLock<Option<ServeStrategy>>,
|
62
|
+
pub silence: bool,
|
63
|
+
}
|
64
|
+
|
65
|
+
#[derive(Debug)]
|
66
|
+
pub enum RequestJob {
|
67
|
+
ProcessRequest(ItsiRequest),
|
68
|
+
Shutdown,
|
69
|
+
}
|
70
|
+
|
71
|
+
// Define your helper function.
|
72
|
+
// Here P, A, C correspond to the types for the first tuple, second tuple, and extra parameters respectively.
|
73
|
+
fn extract_args<Req, Opt, Splat>(
|
74
|
+
scan_args: &Args<(), (), (), (), RHash, ()>,
|
75
|
+
primaries: &[&str],
|
76
|
+
rest: &[&str],
|
77
|
+
) -> Result<KwArgs<Req, Opt, Splat>>
|
78
|
+
where
|
79
|
+
Req: ScanArgsRequired,
|
80
|
+
Opt: ScanArgsOpt,
|
81
|
+
Splat: ScanArgsKw,
|
82
|
+
{
|
83
|
+
// Combine the primary and rest names into one Vec of Symbols.
|
84
|
+
let symbols: Vec<Symbol> = primaries
|
85
|
+
.iter()
|
86
|
+
.chain(rest.iter())
|
87
|
+
.map(|&name| Symbol::new(name))
|
88
|
+
.collect();
|
89
|
+
|
90
|
+
// Call the "slice" function with the combined symbols.
|
91
|
+
let hash = scan_args
|
92
|
+
.keywords
|
93
|
+
.funcall::<_, _, RHash>("slice", symbols.into_arg_list_with(&Ruby::get().unwrap()))
|
94
|
+
.unwrap();
|
95
|
+
|
96
|
+
// Finally, call get_kwargs with the original name slices.
|
97
|
+
get_kwargs(hash, primaries, rest)
|
39
98
|
}
|
40
99
|
|
41
100
|
impl Server {
|
101
|
+
#[instrument(
|
102
|
+
name = "Itsi",
|
103
|
+
parent=None,
|
104
|
+
skip(args),
|
105
|
+
fields(workers = 1, threads = 1, shutdown_timeout = 5)
|
106
|
+
)]
|
42
107
|
pub fn new(args: &[Value]) -> Result<Self> {
|
43
|
-
type OptionalArgs = (
|
44
|
-
Option<u16>,
|
45
|
-
Option<u16>,
|
46
|
-
Option<f64>,
|
47
|
-
Option<String>,
|
48
|
-
Option<Vec<String>>,
|
49
|
-
);
|
50
|
-
|
51
108
|
let scan_args: Args<(), (), (), (), RHash, ()> = scan_args(args)?;
|
52
|
-
|
53
|
-
|
109
|
+
|
110
|
+
type Args1 = KwArgs<
|
111
|
+
(Value,),
|
112
|
+
(
|
113
|
+
// Workers
|
114
|
+
Option<u8>,
|
115
|
+
// Threads
|
116
|
+
Option<u8>,
|
117
|
+
// Shutdown Timeout
|
118
|
+
Option<f64>,
|
119
|
+
// Script Name
|
120
|
+
Option<String>,
|
121
|
+
// Binds
|
122
|
+
Option<Vec<String>>,
|
123
|
+
// Stream Body
|
124
|
+
Option<bool>,
|
125
|
+
),
|
126
|
+
(),
|
127
|
+
>;
|
128
|
+
|
129
|
+
type Args2 = KwArgs<
|
130
|
+
(),
|
131
|
+
(
|
132
|
+
// Before Fork
|
133
|
+
Option<Proc>,
|
134
|
+
// After Fork
|
135
|
+
Option<Proc>,
|
136
|
+
// Scheduler Class
|
137
|
+
Option<String>,
|
138
|
+
// Worker Memory Limit
|
139
|
+
Option<u64>,
|
140
|
+
// Silence
|
141
|
+
Option<bool>,
|
142
|
+
),
|
143
|
+
(),
|
144
|
+
>;
|
145
|
+
|
146
|
+
let args1: Args1 = extract_args(
|
147
|
+
&scan_args,
|
54
148
|
&["app"],
|
55
149
|
&[
|
56
150
|
"workers",
|
@@ -58,125 +152,143 @@ impl Server {
|
|
58
152
|
"shutdown_timeout",
|
59
153
|
"script_name",
|
60
154
|
"binds",
|
155
|
+
"stream_body",
|
156
|
+
],
|
157
|
+
)?;
|
158
|
+
|
159
|
+
let args2: Args2 = extract_args(
|
160
|
+
&scan_args,
|
161
|
+
&[],
|
162
|
+
&[
|
163
|
+
"before_fork",
|
164
|
+
"after_fork",
|
165
|
+
"scheduler_class",
|
166
|
+
"worker_memory_limit",
|
167
|
+
"silence",
|
61
168
|
],
|
62
169
|
)?;
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
170
|
+
|
171
|
+
let config = ServerConfig {
|
172
|
+
app: HeapVal::from(args1.required.0),
|
173
|
+
workers: max(args1.optional.0.unwrap_or(1), 1),
|
174
|
+
threads: max(args1.optional.1.unwrap_or(1), 1),
|
175
|
+
shutdown_timeout: args1.optional.2.unwrap_or(5.0),
|
176
|
+
script_name: args1.optional.3.unwrap_or("".to_string()),
|
69
177
|
binds: Mutex::new(
|
70
|
-
|
178
|
+
args1
|
179
|
+
.optional
|
71
180
|
.4
|
72
|
-
.unwrap_or_else(|| vec![
|
181
|
+
.unwrap_or_else(|| vec![DEFAULT_BIND.to_string()])
|
73
182
|
.into_iter()
|
74
|
-
.map(|s| s.parse()
|
75
|
-
.collect()
|
183
|
+
.map(|s| s.parse())
|
184
|
+
.collect::<itsi_error::Result<Vec<Bind>>>()?,
|
76
185
|
),
|
186
|
+
stream_body: args1.optional.5,
|
187
|
+
before_fork: Mutex::new(args2.optional.0.map(|p| {
|
188
|
+
let opaque_proc = Opaque::from(p);
|
189
|
+
Box::new(move || {
|
190
|
+
opaque_proc
|
191
|
+
.get_inner_with(&Ruby::get().unwrap())
|
192
|
+
.call::<_, Value>(())
|
193
|
+
.unwrap();
|
194
|
+
}) as Box<dyn FnOnce() + Send + Sync>
|
195
|
+
})),
|
196
|
+
after_fork: Mutex::new(Arc::new(args2.optional.1.map(|p| {
|
197
|
+
let opaque_proc = Opaque::from(p);
|
198
|
+
Box::new(move || {
|
199
|
+
opaque_proc
|
200
|
+
.get_inner_with(&Ruby::get().unwrap())
|
201
|
+
.call::<_, Value>(())
|
202
|
+
.unwrap();
|
203
|
+
}) as Box<dyn Fn() + Send + Sync>
|
204
|
+
}))),
|
205
|
+
scheduler_class: args2.optional.2.clone(),
|
206
|
+
worker_memory_limit: args2.optional.3,
|
207
|
+
silence: args2.optional.4.is_some_and(|s| s),
|
208
|
+
strategy: RwLock::new(None),
|
77
209
|
};
|
78
|
-
Ok(server)
|
79
|
-
}
|
80
210
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
addr: SockAddr,
|
87
|
-
) -> itsi_error::Result<Response<BoxBody<Bytes, Infallible>>> {
|
88
|
-
let request = ItsiRequest::build_from(hyper_request, addr, script_name, listener).await;
|
89
|
-
let ruby = Ruby::get().unwrap();
|
90
|
-
let server = ruby.get_inner(&ITSI_SERVER);
|
91
|
-
let response: Result<(u16, HashMap<String, String>, Value)> =
|
92
|
-
server.funcall("call", (app, request));
|
93
|
-
if let Ok((status, headers_raw, body)) = response {
|
94
|
-
let mut body_buf = vec![];
|
95
|
-
for body_chunk in body.enumeratorize("each", ()) {
|
96
|
-
body_buf.push(body_chunk.unwrap().to_string())
|
211
|
+
if !config.silence {
|
212
|
+
if let Some(scheduler_class) = args2.optional.2 {
|
213
|
+
info!(scheduler_class, fiber_scheduler = true);
|
214
|
+
} else {
|
215
|
+
info!(fiber_scheduler = false);
|
97
216
|
}
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
217
|
+
}
|
218
|
+
|
219
|
+
Ok(Server {
|
220
|
+
config: Arc::new(config),
|
221
|
+
})
|
222
|
+
}
|
223
|
+
|
224
|
+
#[instrument(name = "Bind", skip_all, fields(binds=format!("{:?}", self.config.binds.lock())))]
|
225
|
+
pub(crate) fn build_listeners(&self) -> Result<Arc<Vec<Arc<Listener>>>> {
|
226
|
+
let listeners = self
|
227
|
+
.config
|
228
|
+
.binds
|
229
|
+
.lock()
|
230
|
+
.iter()
|
231
|
+
.cloned()
|
232
|
+
.map(Listener::try_from)
|
233
|
+
.collect::<std::result::Result<Vec<Listener>, _>>()?
|
234
|
+
.into_iter()
|
235
|
+
.map(Arc::new)
|
236
|
+
.collect::<Vec<_>>();
|
237
|
+
info!("Bound {:?} listeners", listeners.len());
|
238
|
+
Ok(Arc::new(listeners))
|
239
|
+
}
|
240
|
+
|
241
|
+
pub(crate) fn build_strategy(self, listeners: Arc<Vec<Arc<Listener>>>) -> Result<()> {
|
242
|
+
let server = Arc::new(self);
|
243
|
+
let server_clone = server.clone();
|
244
|
+
|
245
|
+
let strategy = if server.config.workers == 1 {
|
246
|
+
ServeStrategy::Single(Arc::new(SingleMode::new(
|
247
|
+
server,
|
248
|
+
listeners,
|
249
|
+
SIGNAL_HANDLER_CHANNEL.0.clone(),
|
250
|
+
)?))
|
251
|
+
} else {
|
252
|
+
ServeStrategy::Cluster(Arc::new(ClusterMode::new(
|
253
|
+
server,
|
254
|
+
listeners,
|
255
|
+
SIGNAL_HANDLER_CHANNEL.0.clone(),
|
256
|
+
)))
|
257
|
+
};
|
258
|
+
|
259
|
+
*server_clone.strategy.write() = Some(strategy);
|
260
|
+
Ok(())
|
261
|
+
}
|
262
|
+
|
263
|
+
pub fn stop(&self) -> Result<()> {
|
264
|
+
send_shutdown_event();
|
265
|
+
Ok(())
|
266
|
+
}
|
267
|
+
|
268
|
+
pub fn start(&self) -> Result<()> {
|
269
|
+
if self.silence {
|
270
|
+
run_silently(|| self.build_and_run_strategy())
|
109
271
|
} else {
|
110
|
-
|
111
|
-
*response.status_mut() = StatusCode::BAD_REQUEST;
|
112
|
-
Ok(response)
|
272
|
+
self.build_and_run_strategy()
|
113
273
|
}
|
114
274
|
}
|
115
275
|
|
116
|
-
|
117
|
-
|
118
|
-
let
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
.
|
123
|
-
.
|
124
|
-
|
125
|
-
|
126
|
-
runtime.block_on(async {
|
127
|
-
let server = Arc::new(Builder::new(TokioExecutor::new()));
|
128
|
-
let listeners: Vec<Listener> = self
|
129
|
-
.binds
|
130
|
-
.lock()
|
131
|
-
.iter()
|
132
|
-
.cloned()
|
133
|
-
.map(Listener::from)
|
134
|
-
.collect::<Vec<_>>();
|
135
|
-
|
136
|
-
let mut set = JoinSet::new();
|
137
|
-
|
138
|
-
for listener in listeners {
|
139
|
-
let app = self.app;
|
140
|
-
let server_clone = server.clone();
|
141
|
-
let listener_clone = Arc::new(listener);
|
142
|
-
let script_name = self.script_name.clone();
|
143
|
-
|
144
|
-
set.spawn(async move {
|
145
|
-
loop {
|
146
|
-
let server = server_clone.clone();
|
147
|
-
let listener = listener_clone.clone();
|
148
|
-
let script_name = script_name.clone();
|
149
|
-
let (stream, addr) = match listener.accept().await {
|
150
|
-
Ok(stream) => stream,
|
151
|
-
Err(e) => {
|
152
|
-
error!("Failed to accept connection: {:?}", e);
|
153
|
-
continue;
|
154
|
-
}
|
155
|
-
};
|
156
|
-
|
157
|
-
tokio::spawn(async move {
|
158
|
-
if let Err(e) = server
|
159
|
-
.serve_connection_with_upgrades(
|
160
|
-
stream,
|
161
|
-
service_fn(move |hyper_request: Request<Incoming>| {
|
162
|
-
Server::process_request(
|
163
|
-
hyper_request,
|
164
|
-
app,
|
165
|
-
script_name.clone(),
|
166
|
-
listener.clone(),
|
167
|
-
addr.clone(),
|
168
|
-
)
|
169
|
-
}),
|
170
|
-
)
|
171
|
-
.await
|
172
|
-
{
|
173
|
-
info!("Closed connection due to: {:?}", e);
|
174
|
-
}
|
175
|
-
});
|
176
|
-
}
|
177
|
-
});
|
276
|
+
fn build_and_run_strategy(&self) -> Result<()> {
|
277
|
+
reset_signal_handlers();
|
278
|
+
let rself = self.clone();
|
279
|
+
let listeners = self.build_listeners()?;
|
280
|
+
let listeners_clone = listeners.clone();
|
281
|
+
call_without_gvl(move || -> Result<()> {
|
282
|
+
rself.clone().build_strategy(listeners_clone)?;
|
283
|
+
if let Err(e) = rself.clone().strategy.read().as_ref().unwrap().run() {
|
284
|
+
error!("Error running server: {}", e);
|
285
|
+
rself.strategy.read().as_ref().unwrap().stop()?;
|
178
286
|
}
|
179
|
-
|
180
|
-
})
|
287
|
+
Ok(())
|
288
|
+
})?;
|
289
|
+
clear_signal_handlers();
|
290
|
+
self.strategy.write().take();
|
291
|
+
info!("Server stopped");
|
292
|
+
Ok(())
|
181
293
|
}
|
182
294
|
}
|