itsi-server 0.1.1 → 0.1.3
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.
- checksums.yaml +4 -4
- data/exe/itsi +88 -28
- data/ext/itsi_error/Cargo.toml +2 -0
- data/ext/itsi_error/src/from.rs +70 -0
- data/ext/itsi_error/src/lib.rs +10 -37
- 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.toml +14 -2
- 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/lib.rs +58 -7
- data/ext/itsi_server/src/request/itsi_request.rs +238 -104
- data/ext/itsi_server/src/response/itsi_response.rs +347 -0
- data/ext/itsi_server/src/response/mod.rs +1 -0
- data/ext/itsi_server/src/server/bind.rs +50 -20
- 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_ca/itsi_ca.crt +11 -30
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +3 -50
- data/ext/itsi_server/src/server/itsi_server.rs +196 -134
- data/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
- data/ext/itsi_server/src/server/listener.rs +184 -127
- 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 +241 -0
- data/ext/itsi_server/src/server/signal.rs +70 -0
- data/ext/itsi_server/src/server/thread_worker.rs +368 -0
- data/ext/itsi_server/src/server/tls.rs +42 -28
- data/ext/itsi_tracing/Cargo.toml +4 -0
- data/ext/itsi_tracing/src/lib.rs +36 -6
- data/lib/itsi/request.rb +30 -14
- data/lib/itsi/server/rack/handler/itsi.rb +25 -0
- data/lib/itsi/server/scheduler_mode.rb +6 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +82 -2
- data/lib/itsi/signals.rb +23 -0
- data/lib/itsi/stream_io.rb +38 -0
- metadata +38 -25
- data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
- data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
@@ -1,22 +1,17 @@
|
|
1
1
|
use super::bind::{Bind, BindAddress};
|
2
|
-
use super::
|
3
|
-
use
|
2
|
+
use super::bind_protocol::BindProtocol;
|
3
|
+
use super::io_stream::IoStream;
|
4
4
|
use itsi_error::Result;
|
5
5
|
use itsi_tracing::info;
|
6
6
|
use socket2::{Domain, Protocol, Socket, Type};
|
7
|
-
use std::net::{IpAddr, SocketAddr, TcpListener
|
8
|
-
use std::pin::Pin;
|
7
|
+
use std::net::{IpAddr, SocketAddr, TcpListener};
|
9
8
|
use std::sync::Arc;
|
10
|
-
use std::{os::unix::net::UnixListener
|
11
|
-
use tokio::net::
|
9
|
+
use std::{os::unix::net::UnixListener, path::PathBuf};
|
10
|
+
use tokio::net::TcpListener as TokioTcpListener;
|
11
|
+
use tokio::net::UnixListener as TokioUnixListener;
|
12
|
+
use tokio::net::{unix, TcpStream, UnixStream};
|
12
13
|
use tokio_rustls::TlsAcceptor;
|
13
14
|
|
14
|
-
pub(crate) trait IoStream:
|
15
|
-
tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin
|
16
|
-
{
|
17
|
-
}
|
18
|
-
impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin> IoStream for T {}
|
19
|
-
|
20
15
|
pub(crate) enum Listener {
|
21
16
|
Tcp(TcpListener),
|
22
17
|
TcpTls((TcpListener, TlsAcceptor)),
|
@@ -24,149 +19,216 @@ pub(crate) enum Listener {
|
|
24
19
|
UnixTls((UnixListener, TlsAcceptor)),
|
25
20
|
}
|
26
21
|
|
27
|
-
enum
|
28
|
-
|
29
|
-
|
22
|
+
pub(crate) enum TokioListener {
|
23
|
+
Tcp {
|
24
|
+
listener: TokioTcpListener,
|
25
|
+
host: String,
|
26
|
+
port: u16,
|
27
|
+
},
|
28
|
+
TcpTls {
|
29
|
+
listener: TokioTcpListener,
|
30
|
+
acceptor: TlsAcceptor,
|
31
|
+
host: String,
|
32
|
+
port: u16,
|
33
|
+
},
|
34
|
+
Unix {
|
35
|
+
listener: TokioUnixListener,
|
36
|
+
},
|
37
|
+
UnixTls {
|
38
|
+
listener: TokioUnixListener,
|
39
|
+
acceptor: TlsAcceptor,
|
40
|
+
},
|
30
41
|
}
|
31
42
|
|
32
|
-
|
33
|
-
pub
|
34
|
-
Tcp(Arc<SocketAddr>),
|
35
|
-
Unix(Arc<unix::SocketAddr>),
|
36
|
-
}
|
37
|
-
impl std::fmt::Display for SockAddr {
|
38
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
43
|
+
impl TokioListener {
|
44
|
+
pub fn unbind(self) {
|
39
45
|
match self {
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
}
|
46
|
+
TokioListener::Tcp { listener, .. } => drop(listener.into_std().unwrap()),
|
47
|
+
TokioListener::TcpTls { listener, .. } => drop(listener.into_std().unwrap()),
|
48
|
+
TokioListener::Unix { listener } => drop(listener.into_std().unwrap()),
|
49
|
+
TokioListener::UnixTls { listener, .. } => drop(listener.into_std().unwrap()),
|
50
|
+
};
|
46
51
|
}
|
47
|
-
|
48
|
-
|
49
|
-
impl Listener {
|
50
|
-
pub(crate) async fn accept(&self) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
|
52
|
+
pub(crate) async fn accept(&self) -> Result<IoStream> {
|
51
53
|
match self {
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
}
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
}
|
54
|
+
TokioListener::Tcp { listener, .. } => TokioListener::accept_tcp(listener).await,
|
55
|
+
TokioListener::TcpTls {
|
56
|
+
listener, acceptor, ..
|
57
|
+
} => TokioListener::accept_tls(listener, acceptor).await,
|
58
|
+
TokioListener::Unix { listener, .. } => TokioListener::accept_unix(listener).await,
|
59
|
+
TokioListener::UnixTls {
|
60
|
+
listener, acceptor, ..
|
61
|
+
} => TokioListener::accept_unix_tls(listener, acceptor).await,
|
60
62
|
}
|
61
63
|
}
|
62
64
|
|
65
|
+
async fn accept_tcp(listener: &TokioTcpListener) -> Result<IoStream> {
|
66
|
+
let tcp_stream = listener.accept().await?;
|
67
|
+
Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
|
68
|
+
}
|
69
|
+
|
70
|
+
async fn accept_tls(listener: &TokioTcpListener, acceptor: &TlsAcceptor) -> Result<IoStream> {
|
71
|
+
let tcp_stream = listener.accept().await?;
|
72
|
+
Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(acceptor)).await
|
73
|
+
}
|
74
|
+
|
75
|
+
async fn accept_unix(listener: &TokioUnixListener) -> Result<IoStream> {
|
76
|
+
let unix_stream = listener.accept().await?;
|
77
|
+
Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
|
78
|
+
}
|
79
|
+
|
80
|
+
async fn accept_unix_tls(
|
81
|
+
listener: &TokioUnixListener,
|
82
|
+
acceptor: &TlsAcceptor,
|
83
|
+
) -> Result<IoStream> {
|
84
|
+
let unix_stream = listener.accept().await?;
|
85
|
+
Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(acceptor)).await
|
86
|
+
}
|
87
|
+
|
63
88
|
async fn to_tokio_io(
|
64
89
|
input_stream: Stream,
|
65
90
|
tls_acceptor: Option<&TlsAcceptor>,
|
66
|
-
) -> Result<
|
91
|
+
) -> Result<IoStream> {
|
67
92
|
match tls_acceptor {
|
68
93
|
Some(acceptor) => match input_stream {
|
69
94
|
Stream::TcpStream((tcp_stream, socket_address)) => {
|
70
95
|
match acceptor.accept(tcp_stream).await {
|
71
|
-
Ok(tls_stream) => Ok(
|
72
|
-
|
73
|
-
SockAddr::Tcp(Arc::new(socket_address)),
|
74
|
-
)
|
96
|
+
Ok(tls_stream) => Ok(IoStream::TcpTls {
|
97
|
+
stream: tls_stream,
|
98
|
+
addr: SockAddr::Tcp(Arc::new(socket_address)),
|
99
|
+
}),
|
75
100
|
Err(err) => Err(err.into()),
|
76
101
|
}
|
77
102
|
}
|
78
103
|
Stream::UnixStream((unix_stream, socket_address)) => {
|
79
104
|
match acceptor.accept(unix_stream).await {
|
80
|
-
Ok(tls_stream) => Ok(
|
81
|
-
|
82
|
-
SockAddr::Unix(Arc::new(socket_address)),
|
83
|
-
)
|
105
|
+
Ok(tls_stream) => Ok(IoStream::UnixTls {
|
106
|
+
stream: tls_stream,
|
107
|
+
addr: SockAddr::Unix(Arc::new(socket_address)),
|
108
|
+
}),
|
84
109
|
Err(err) => Err(err.into()),
|
85
110
|
}
|
86
111
|
}
|
87
112
|
},
|
88
113
|
None => match input_stream {
|
89
|
-
Stream::TcpStream((tcp_stream, socket_address)) => Ok(
|
90
|
-
|
91
|
-
SockAddr::Tcp(Arc::new(socket_address)),
|
92
|
-
)
|
93
|
-
Stream::UnixStream((unix_stream, socket_address)) => Ok(
|
94
|
-
|
95
|
-
SockAddr::Unix(Arc::new(socket_address)),
|
96
|
-
)
|
114
|
+
Stream::TcpStream((tcp_stream, socket_address)) => Ok(IoStream::Tcp {
|
115
|
+
stream: tcp_stream,
|
116
|
+
addr: SockAddr::Tcp(Arc::new(socket_address)),
|
117
|
+
}),
|
118
|
+
Stream::UnixStream((unix_stream, socket_address)) => Ok(IoStream::Unix {
|
119
|
+
stream: unix_stream,
|
120
|
+
addr: SockAddr::Unix(Arc::new(socket_address)),
|
121
|
+
}),
|
97
122
|
},
|
98
123
|
}
|
99
124
|
}
|
100
125
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
126
|
+
pub(crate) fn scheme(&self) -> String {
|
127
|
+
match self {
|
128
|
+
TokioListener::Tcp { .. } => "http".to_string(),
|
129
|
+
TokioListener::TcpTls { .. } => "https".to_string(),
|
130
|
+
TokioListener::Unix { .. } => "http".to_string(),
|
131
|
+
TokioListener::UnixTls { .. } => "https".to_string(),
|
132
|
+
}
|
106
133
|
}
|
107
134
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
135
|
+
pub(crate) fn port(&self) -> u16 {
|
136
|
+
match self {
|
137
|
+
TokioListener::Tcp { port, .. } => *port,
|
138
|
+
TokioListener::TcpTls { port, .. } => *port,
|
139
|
+
TokioListener::Unix { .. } => 0,
|
140
|
+
TokioListener::UnixTls { .. } => 0,
|
141
|
+
}
|
114
142
|
}
|
115
143
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
144
|
+
pub(crate) fn host(&self) -> String {
|
145
|
+
match self {
|
146
|
+
TokioListener::Tcp { host, .. } => host.to_string(),
|
147
|
+
TokioListener::TcpTls { host, .. } => host.to_string(),
|
148
|
+
TokioListener::Unix { .. } => "unix".to_string(),
|
149
|
+
TokioListener::UnixTls { .. } => "unix".to_string(),
|
150
|
+
}
|
121
151
|
}
|
152
|
+
}
|
122
153
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
let unix_stream = listener.accept().await?;
|
128
|
-
Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(acceptor)).await
|
129
|
-
}
|
154
|
+
enum Stream {
|
155
|
+
TcpStream((TcpStream, SocketAddr)),
|
156
|
+
UnixStream((UnixStream, unix::SocketAddr)),
|
157
|
+
}
|
130
158
|
|
131
|
-
|
159
|
+
#[derive(Clone, Debug)]
|
160
|
+
pub enum SockAddr {
|
161
|
+
Tcp(Arc<SocketAddr>),
|
162
|
+
Unix(Arc<unix::SocketAddr>),
|
163
|
+
}
|
164
|
+
impl std::fmt::Display for SockAddr {
|
165
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
132
166
|
match self {
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
167
|
+
SockAddr::Tcp(socket_addr) => write!(f, "{}", socket_addr.ip().to_canonical()),
|
168
|
+
SockAddr::Unix(socket_addr) => match socket_addr.as_pathname() {
|
169
|
+
Some(path) => write!(f, "{:?}", path),
|
170
|
+
None => write!(f, ""),
|
171
|
+
},
|
137
172
|
}
|
138
173
|
}
|
174
|
+
}
|
139
175
|
|
140
|
-
|
176
|
+
impl Listener {
|
177
|
+
pub fn unbind(self) {
|
141
178
|
match self {
|
142
|
-
Listener::Tcp(listener) => listener
|
143
|
-
Listener::TcpTls((listener, _)) => listener
|
144
|
-
Listener::Unix(
|
145
|
-
Listener::UnixTls(_) =>
|
146
|
-
}
|
179
|
+
Listener::Tcp(listener) => drop(listener),
|
180
|
+
Listener::TcpTls((listener, _)) => drop(listener),
|
181
|
+
Listener::Unix(listener) => drop(listener),
|
182
|
+
Listener::UnixTls((listener, _)) => drop(listener),
|
183
|
+
};
|
147
184
|
}
|
148
|
-
|
149
|
-
pub(crate) fn host(&self) -> String {
|
185
|
+
pub fn to_tokio_listener(&self) -> TokioListener {
|
150
186
|
match self {
|
151
|
-
Listener::Tcp(listener) =>
|
152
|
-
|
153
|
-
|
154
|
-
|
187
|
+
Listener::Tcp(listener) => TokioListener::Tcp {
|
188
|
+
listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
|
189
|
+
.unwrap(),
|
190
|
+
host: listener
|
191
|
+
.local_addr()
|
192
|
+
.unwrap()
|
193
|
+
.ip()
|
194
|
+
.to_canonical()
|
195
|
+
.to_string(),
|
196
|
+
port: listener.local_addr().unwrap().port(),
|
197
|
+
},
|
198
|
+
Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls {
|
199
|
+
listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
|
200
|
+
.unwrap(),
|
201
|
+
acceptor: acceptor.clone(),
|
202
|
+
host: listener
|
203
|
+
.local_addr()
|
204
|
+
.unwrap()
|
205
|
+
.ip()
|
206
|
+
.to_canonical()
|
207
|
+
.to_string(),
|
208
|
+
port: listener.local_addr().unwrap().port(),
|
209
|
+
},
|
210
|
+
Listener::Unix(listener) => TokioListener::Unix {
|
211
|
+
listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
|
212
|
+
.unwrap(),
|
213
|
+
},
|
214
|
+
Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls {
|
215
|
+
listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
|
216
|
+
.unwrap(),
|
217
|
+
acceptor: acceptor.clone(),
|
218
|
+
},
|
155
219
|
}
|
156
220
|
}
|
157
221
|
}
|
158
222
|
|
159
|
-
impl
|
160
|
-
|
161
|
-
|
223
|
+
impl TryFrom<Bind> for Listener {
|
224
|
+
type Error = itsi_error::ItsiError;
|
225
|
+
|
226
|
+
fn try_from(bind: Bind) -> std::result::Result<Self, Self::Error> {
|
227
|
+
let bound = match bind.address {
|
162
228
|
BindAddress::Ip(addr) => match bind.protocol {
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
TransferProtocol::Https => {
|
167
|
-
let tcp_listener =
|
168
|
-
TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap()))
|
169
|
-
.unwrap();
|
229
|
+
BindProtocol::Http => Listener::Tcp(connect_tcp_socket(addr, bind.port.unwrap())?),
|
230
|
+
BindProtocol::Https => {
|
231
|
+
let tcp_listener = connect_tcp_socket(addr, bind.port.unwrap())?;
|
170
232
|
let tls_acceptor = TlsAcceptor::from(Arc::new(bind.tls_config.unwrap()));
|
171
233
|
Listener::TcpTls((tcp_listener, tls_acceptor))
|
172
234
|
}
|
@@ -175,44 +237,39 @@ impl From<Bind> for Listener {
|
|
175
237
|
BindAddress::UnixSocket(path) => match bind.tls_config {
|
176
238
|
Some(tls_config) => {
|
177
239
|
let tls_acceptor = TlsAcceptor::from(Arc::new(tls_config));
|
178
|
-
Listener::UnixTls((
|
179
|
-
UnixListener::from_std(connect_unix_socket(&path)).unwrap(),
|
180
|
-
tls_acceptor,
|
181
|
-
))
|
240
|
+
Listener::UnixTls((connect_unix_socket(&path)?, tls_acceptor))
|
182
241
|
}
|
183
|
-
None => Listener::Unix(
|
242
|
+
None => Listener::Unix(connect_unix_socket(&path)?),
|
184
243
|
},
|
185
|
-
}
|
244
|
+
};
|
245
|
+
Ok(bound)
|
186
246
|
}
|
187
247
|
}
|
188
248
|
|
189
|
-
fn connect_tcp_socket(addr: IpAddr, port: u16) ->
|
249
|
+
fn connect_tcp_socket(addr: IpAddr, port: u16) -> Result<TcpListener> {
|
190
250
|
let domain = match addr {
|
191
251
|
IpAddr::V4(_) => Domain::IPV4,
|
192
252
|
IpAddr::V6(_) => Domain::IPV6,
|
193
253
|
};
|
194
|
-
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
|
254
|
+
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
|
195
255
|
let socket_address: SocketAddr = SocketAddr::new(addr, port);
|
196
|
-
socket.set_reuse_address(true).ok();
|
197
|
-
socket.set_reuse_port(true).ok();
|
198
256
|
socket.set_nonblocking(true).ok();
|
199
257
|
socket.set_nodelay(true).ok();
|
200
258
|
socket.set_recv_buffer_size(1_048_576).ok();
|
201
|
-
|
202
|
-
socket.
|
203
|
-
socket.
|
204
|
-
socket.into()
|
259
|
+
socket.bind(&socket_address.into())?;
|
260
|
+
socket.listen(1024)?;
|
261
|
+
Ok(socket.into())
|
205
262
|
}
|
206
263
|
|
207
|
-
fn connect_unix_socket(path: &PathBuf) ->
|
264
|
+
fn connect_unix_socket(path: &PathBuf) -> Result<UnixListener> {
|
208
265
|
let _ = std::fs::remove_file(path);
|
209
|
-
let socket = Socket::new(Domain::UNIX, Type::STREAM, None)
|
266
|
+
let socket = Socket::new(Domain::UNIX, Type::STREAM, None)?;
|
210
267
|
socket.set_nonblocking(true).ok();
|
211
|
-
let socket_address = socket2::SockAddr::unix(path)
|
268
|
+
let socket_address = socket2::SockAddr::unix(path)?;
|
212
269
|
|
213
270
|
info!("Binding to {:?}", path);
|
214
|
-
socket.bind(&socket_address)
|
215
|
-
socket.listen(1024)
|
271
|
+
socket.bind(&socket_address)?;
|
272
|
+
socket.listen(1024)?;
|
216
273
|
|
217
|
-
socket.into()
|
274
|
+
Ok(socket.into())
|
218
275
|
}
|
@@ -1,5 +1,11 @@
|
|
1
1
|
pub mod bind;
|
2
|
+
pub mod bind_protocol;
|
3
|
+
pub mod io_stream;
|
2
4
|
pub mod itsi_server;
|
5
|
+
pub mod lifecycle_event;
|
3
6
|
pub mod listener;
|
7
|
+
pub mod process_worker;
|
8
|
+
pub mod serve_strategy;
|
9
|
+
pub mod signal;
|
10
|
+
pub mod thread_worker;
|
4
11
|
pub mod tls;
|
5
|
-
pub mod transfer_protocol;
|
@@ -0,0 +1,196 @@
|
|
1
|
+
use super::serve_strategy::{cluster_mode::ClusterMode, single_mode::SingleMode};
|
2
|
+
use itsi_error::{ItsiError, Result};
|
3
|
+
use itsi_rb_helpers::{call_with_gvl, call_without_gvl, create_ruby_thread, fork};
|
4
|
+
use itsi_tracing::error;
|
5
|
+
use nix::{
|
6
|
+
errno::Errno,
|
7
|
+
sys::{
|
8
|
+
signal::{
|
9
|
+
kill,
|
10
|
+
Signal::{SIGKILL, SIGTERM},
|
11
|
+
},
|
12
|
+
wait::{waitpid, WaitPidFlag, WaitStatus},
|
13
|
+
},
|
14
|
+
unistd::{setpgid, Pid},
|
15
|
+
};
|
16
|
+
use parking_lot::Mutex;
|
17
|
+
use std::{
|
18
|
+
process::{self, exit},
|
19
|
+
sync::Arc,
|
20
|
+
time::{Duration, Instant},
|
21
|
+
};
|
22
|
+
use sysinfo::System;
|
23
|
+
|
24
|
+
use tokio::{sync::watch, time::sleep};
|
25
|
+
use tracing::{info, instrument, warn};
|
26
|
+
|
27
|
+
#[derive(Clone, Debug)]
|
28
|
+
pub struct ProcessWorker {
|
29
|
+
pub worker_id: usize,
|
30
|
+
pub child_pid: Arc<Mutex<Option<Pid>>>,
|
31
|
+
pub started_at: Instant,
|
32
|
+
}
|
33
|
+
|
34
|
+
impl Default for ProcessWorker {
|
35
|
+
fn default() -> Self {
|
36
|
+
Self {
|
37
|
+
worker_id: 0,
|
38
|
+
child_pid: Arc::new(Mutex::new(None)),
|
39
|
+
started_at: Instant::now(),
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
impl ProcessWorker {
|
45
|
+
#[instrument(skip(self, cluster_template), fields(self.worker_id = %self.worker_id))]
|
46
|
+
pub(crate) fn boot(&self, cluster_template: Arc<ClusterMode>) -> Result<()> {
|
47
|
+
let child_pid = *self.child_pid.lock();
|
48
|
+
if let Some(pid) = child_pid {
|
49
|
+
if self.is_alive() {
|
50
|
+
if let Err(e) = kill(pid, SIGTERM) {
|
51
|
+
info!("Failed to send SIGTERM to process {}: {}", pid, e);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
*self.child_pid.lock() = None;
|
55
|
+
}
|
56
|
+
|
57
|
+
match call_with_gvl(|_ruby| fork(cluster_template.server.after_fork.lock().clone())) {
|
58
|
+
Some(pid) => {
|
59
|
+
*self.child_pid.lock() = Some(Pid::from_raw(pid));
|
60
|
+
}
|
61
|
+
None => {
|
62
|
+
if let Err(e) = setpgid(
|
63
|
+
Pid::from_raw(process::id() as i32),
|
64
|
+
Pid::from_raw(process::id() as i32),
|
65
|
+
) {
|
66
|
+
error!("Failed to set process group ID: {}", e);
|
67
|
+
}
|
68
|
+
match SingleMode::new(
|
69
|
+
cluster_template.server.clone(),
|
70
|
+
cluster_template.listeners.clone(),
|
71
|
+
cluster_template.lifecycle_channel.clone(),
|
72
|
+
) {
|
73
|
+
Ok(single_mode) => {
|
74
|
+
Arc::new(single_mode).run().ok();
|
75
|
+
}
|
76
|
+
Err(e) => {
|
77
|
+
error!("Failed to boot into worker mode: {}", e);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
exit(0)
|
81
|
+
}
|
82
|
+
}
|
83
|
+
Ok(())
|
84
|
+
}
|
85
|
+
|
86
|
+
pub(crate) fn memory_usage(&self) -> Option<u64> {
|
87
|
+
if let Some(pid) = *self.child_pid.lock() {
|
88
|
+
let s = System::new_all();
|
89
|
+
if let Some(process) = s.process(sysinfo::Pid::from(pid.as_raw() as usize)) {
|
90
|
+
return Some(process.memory());
|
91
|
+
}
|
92
|
+
}
|
93
|
+
None
|
94
|
+
}
|
95
|
+
|
96
|
+
pub(crate) async fn reboot(&self, cluster_template: Arc<ClusterMode>) -> Result<bool> {
|
97
|
+
self.graceful_shutdown(cluster_template.clone()).await;
|
98
|
+
let self_clone = self.clone();
|
99
|
+
let (booted_sender, mut booted_receiver) = watch::channel(false);
|
100
|
+
create_ruby_thread(move || {
|
101
|
+
call_without_gvl(move || {
|
102
|
+
if self_clone.boot(cluster_template).is_ok() {
|
103
|
+
booted_sender.send(true).ok()
|
104
|
+
} else {
|
105
|
+
booted_sender.send(false).ok()
|
106
|
+
};
|
107
|
+
})
|
108
|
+
});
|
109
|
+
|
110
|
+
booted_receiver
|
111
|
+
.changed()
|
112
|
+
.await
|
113
|
+
.map_err(|_| ItsiError::InternalServerError("Failed to boot worker".to_owned()))?;
|
114
|
+
|
115
|
+
let guard = booted_receiver.borrow();
|
116
|
+
let result = guard.to_owned();
|
117
|
+
// Not very robust, we should check to see if the worker is actually listening before considering this successful.
|
118
|
+
sleep(Duration::from_secs(1)).await;
|
119
|
+
Ok(result)
|
120
|
+
}
|
121
|
+
|
122
|
+
pub(crate) async fn graceful_shutdown(&self, cluster_template: Arc<ClusterMode>) {
|
123
|
+
let self_clone = self.clone();
|
124
|
+
self_clone.request_shutdown();
|
125
|
+
let force_kill_time =
|
126
|
+
Instant::now() + Duration::from_secs_f64(cluster_template.server.shutdown_timeout);
|
127
|
+
while self_clone.is_alive() && force_kill_time > Instant::now() {
|
128
|
+
tokio::time::sleep(Duration::from_millis(100)).await;
|
129
|
+
}
|
130
|
+
if self_clone.is_alive() {
|
131
|
+
self_clone.force_kill();
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
pub(crate) fn boot_if_dead(&self, cluster_template: Arc<ClusterMode>) -> bool {
|
136
|
+
if !self.is_alive() {
|
137
|
+
if self.just_started() {
|
138
|
+
error!(
|
139
|
+
"Worker in crash loop {:?}. Refusing to restart",
|
140
|
+
self.child_pid.lock()
|
141
|
+
);
|
142
|
+
return false;
|
143
|
+
} else {
|
144
|
+
let self_clone = self.clone();
|
145
|
+
create_ruby_thread(move || {
|
146
|
+
call_without_gvl(move || {
|
147
|
+
self_clone.boot(cluster_template).ok();
|
148
|
+
})
|
149
|
+
});
|
150
|
+
}
|
151
|
+
}
|
152
|
+
true
|
153
|
+
}
|
154
|
+
|
155
|
+
pub(crate) fn request_shutdown(&self) {
|
156
|
+
let child_pid = *self.child_pid.lock();
|
157
|
+
if let Some(pid) = child_pid {
|
158
|
+
if let Err(e) = kill(pid, SIGTERM) {
|
159
|
+
error!("Failed to send SIGTERM to process {}: {}", pid, e);
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
pub(crate) fn force_kill(&self) {
|
165
|
+
let child_pid = *self.child_pid.lock();
|
166
|
+
if let Some(pid) = child_pid {
|
167
|
+
if let Err(e) = kill(pid, SIGKILL) {
|
168
|
+
error!("Failed to force kill process {}: {}", pid, e);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
pub(crate) fn just_started(&self) -> bool {
|
174
|
+
let now = Instant::now();
|
175
|
+
now.duration_since(self.started_at).as_millis() < 2000
|
176
|
+
}
|
177
|
+
|
178
|
+
pub(crate) fn is_alive(&self) -> bool {
|
179
|
+
let child_pid = *self.child_pid.lock();
|
180
|
+
if let Some(pid) = child_pid {
|
181
|
+
match waitpid(pid, Some(WaitPidFlag::WNOHANG)) {
|
182
|
+
Ok(WaitStatus::Exited(_, _)) | Ok(WaitStatus::Signaled(_, _, _)) => {
|
183
|
+
return false;
|
184
|
+
}
|
185
|
+
Ok(WaitStatus::StillAlive) | Ok(_) => {}
|
186
|
+
Err(_) => return false,
|
187
|
+
}
|
188
|
+
match kill(pid, None) {
|
189
|
+
Ok(_) => true,
|
190
|
+
Err(errno) => !matches!(errno, Errno::ESRCH),
|
191
|
+
}
|
192
|
+
} else {
|
193
|
+
false
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|