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.

Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +2926 -0
  3. data/Cargo.toml +7 -0
  4. data/Rakefile +8 -1
  5. data/exe/itsi +119 -29
  6. data/ext/itsi_error/Cargo.toml +2 -0
  7. data/ext/itsi_error/src/from.rs +68 -0
  8. data/ext/itsi_error/src/lib.rs +13 -38
  9. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  10. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  11. data/ext/itsi_rb_helpers/Cargo.toml +2 -0
  12. data/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
  13. data/ext/itsi_rb_helpers/src/lib.rs +112 -9
  14. data/ext/itsi_scheduler/Cargo.toml +24 -0
  15. data/ext/itsi_scheduler/extconf.rb +6 -0
  16. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  17. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  18. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  19. data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
  20. data/ext/itsi_scheduler/src/lib.rs +38 -0
  21. data/ext/itsi_server/Cargo.lock +2956 -0
  22. data/ext/itsi_server/Cargo.toml +25 -4
  23. data/ext/itsi_server/extconf.rb +1 -1
  24. data/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
  25. data/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
  26. data/ext/itsi_server/src/body_proxy/mod.rs +2 -0
  27. data/ext/itsi_server/src/env.rs +43 -0
  28. data/ext/itsi_server/src/lib.rs +136 -8
  29. data/ext/itsi_server/src/request/itsi_request.rs +258 -103
  30. data/ext/itsi_server/src/response/itsi_response.rs +357 -0
  31. data/ext/itsi_server/src/response/mod.rs +1 -0
  32. data/ext/itsi_server/src/server/bind.rs +65 -29
  33. data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
  34. data/ext/itsi_server/src/server/io_stream.rs +104 -0
  35. data/ext/itsi_server/src/server/itsi_server.rs +245 -139
  36. data/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
  37. data/ext/itsi_server/src/server/listener.rs +237 -137
  38. data/ext/itsi_server/src/server/mod.rs +7 -1
  39. data/ext/itsi_server/src/server/process_worker.rs +203 -0
  40. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +260 -0
  41. data/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
  42. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +276 -0
  43. data/ext/itsi_server/src/server/signal.rs +74 -0
  44. data/ext/itsi_server/src/server/thread_worker.rs +399 -0
  45. data/ext/itsi_server/src/server/tls/locked_dir_cache.rs +132 -0
  46. data/ext/itsi_server/src/server/tls.rs +187 -60
  47. data/ext/itsi_tracing/Cargo.toml +4 -0
  48. data/ext/itsi_tracing/src/lib.rs +53 -6
  49. data/lib/itsi/index.html +91 -0
  50. data/lib/itsi/request.rb +38 -14
  51. data/lib/itsi/server/Itsi.rb +127 -0
  52. data/lib/itsi/server/config.rb +36 -0
  53. data/lib/itsi/server/options_dsl.rb +401 -0
  54. data/lib/itsi/server/rack/handler/itsi.rb +36 -0
  55. data/lib/itsi/server/rack_interface.rb +75 -0
  56. data/lib/itsi/server/scheduler_interface.rb +21 -0
  57. data/lib/itsi/server/scheduler_mode.rb +6 -0
  58. data/lib/itsi/server/signal_trap.rb +23 -0
  59. data/lib/itsi/server/version.rb +1 -1
  60. data/lib/itsi/server.rb +79 -9
  61. data/lib/itsi/stream_io.rb +38 -0
  62. metadata +49 -27
  63. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -32
  64. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -52
  65. data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
  66. data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
@@ -1,61 +1,182 @@
1
1
  use super::bind::{Bind, BindAddress};
2
- use super::transfer_protocol::TransferProtocol;
3
- use hyper_util::rt::TokioIo;
4
- use itsi_error::Result;
2
+ use super::bind_protocol::BindProtocol;
3
+ use super::io_stream::IoStream;
4
+ use super::serve_strategy::single_mode::RunningPhase;
5
+ use super::tls::ItsiTlsAcceptor;
6
+ use itsi_error::{ItsiError, Result};
5
7
  use itsi_tracing::info;
6
8
  use socket2::{Domain, Protocol, Socket, Type};
7
- use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
8
- use std::pin::Pin;
9
+ use std::net::{IpAddr, SocketAddr, TcpListener};
9
10
  use std::sync::Arc;
10
- use std::{os::unix::net::UnixListener as StdUnixListener, path::PathBuf};
11
- use tokio::net::{unix, TcpListener, TcpStream, UnixListener, UnixStream};
11
+ use std::{os::unix::net::UnixListener, path::PathBuf};
12
+ use tokio::net::TcpListener as TokioTcpListener;
13
+ use tokio::net::UnixListener as TokioUnixListener;
14
+ use tokio::net::{unix, TcpStream, UnixStream};
15
+ use tokio::sync::watch::Receiver;
12
16
  use tokio_rustls::TlsAcceptor;
13
-
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 {}
17
+ use tokio_stream::StreamExt;
18
+ use tracing::error;
19
19
 
20
20
  pub(crate) enum Listener {
21
21
  Tcp(TcpListener),
22
- TcpTls((TcpListener, TlsAcceptor)),
22
+ TcpTls((TcpListener, ItsiTlsAcceptor)),
23
23
  Unix(UnixListener),
24
- UnixTls((UnixListener, TlsAcceptor)),
24
+ UnixTls((UnixListener, ItsiTlsAcceptor)),
25
25
  }
26
26
 
27
- enum Stream {
28
- TcpStream((TcpStream, SocketAddr)),
29
- UnixStream((UnixStream, unix::SocketAddr)),
27
+ pub(crate) enum TokioListener {
28
+ Tcp(TokioTcpListener),
29
+ TcpTls(TokioTcpListener, ItsiTlsAcceptor),
30
+ Unix(TokioUnixListener),
31
+ UnixTls(TokioUnixListener, ItsiTlsAcceptor),
30
32
  }
31
33
 
32
- #[derive(Clone, Debug)]
33
- pub enum SockAddr {
34
- Tcp(Arc<SocketAddr>),
35
- Unix(Arc<unix::SocketAddr>),
34
+ #[derive(Debug, Clone)]
35
+ pub struct ListenerInfo {
36
+ pub host: String,
37
+ pub port: u16,
38
+ pub scheme: String,
36
39
  }
37
- impl std::fmt::Display for SockAddr {
38
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40
+
41
+ impl TokioListener {
42
+ pub fn listener_info(&self) -> ListenerInfo {
39
43
  match self {
40
- SockAddr::Tcp(socket_addr) => write!(f, "{}", socket_addr.ip().to_canonical()),
41
- SockAddr::Unix(socket_addr) => match socket_addr.as_pathname() {
42
- Some(path) => write!(f, "{:?}", path),
43
- None => write!(f, ""),
44
+ TokioListener::Tcp(listener) => ListenerInfo {
45
+ host: listener
46
+ .local_addr()
47
+ .unwrap()
48
+ .ip()
49
+ .to_canonical()
50
+ .to_string(),
51
+ port: listener.local_addr().unwrap().port(),
52
+ scheme: "http".to_string(),
53
+ },
54
+ TokioListener::TcpTls(listener, _) => ListenerInfo {
55
+ host: listener
56
+ .local_addr()
57
+ .unwrap()
58
+ .ip()
59
+ .to_canonical()
60
+ .to_string(),
61
+ port: listener.local_addr().unwrap().port(),
62
+ scheme: "https".to_string(),
63
+ },
64
+ TokioListener::Unix(listener) => ListenerInfo {
65
+ host: listener
66
+ .local_addr()
67
+ .unwrap()
68
+ .as_pathname()
69
+ .unwrap()
70
+ .to_str()
71
+ .unwrap()
72
+ .to_owned(),
73
+ port: 0,
74
+ scheme: "unix".to_string(),
75
+ },
76
+ TokioListener::UnixTls(listener, _) => ListenerInfo {
77
+ host: listener
78
+ .local_addr()
79
+ .unwrap()
80
+ .as_pathname()
81
+ .unwrap()
82
+ .to_str()
83
+ .unwrap()
84
+ .to_owned(),
85
+ port: 0,
86
+ scheme: "ssl".to_string(),
44
87
  },
45
88
  }
46
89
  }
47
- }
48
90
 
49
- impl Listener {
50
- pub(crate) async fn accept(&self) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
91
+ pub(crate) async fn accept(&self) -> Result<IoStream> {
51
92
  match self {
52
- Listener::Tcp(listener) => Listener::accept_tcp(listener).await,
53
- Listener::TcpTls((listener, acceptor)) => {
54
- Listener::accept_tls(listener, acceptor).await
93
+ TokioListener::Tcp(listener) => TokioListener::accept_tcp(listener).await,
94
+ TokioListener::TcpTls(listener, acceptor) => {
95
+ TokioListener::accept_tls(listener, acceptor).await
55
96
  }
56
- Listener::Unix(stream) => Listener::accept_unix(stream).await,
57
- Listener::UnixTls((listener, acceptor)) => {
58
- Listener::accept_unix_tls(listener, acceptor).await
97
+ TokioListener::Unix(listener) => TokioListener::accept_unix(listener).await,
98
+ TokioListener::UnixTls(listener, acceptor) => {
99
+ TokioListener::accept_unix_tls(listener, acceptor).await
100
+ }
101
+ }
102
+ }
103
+
104
+ async fn accept_tcp(listener: &TokioTcpListener) -> Result<IoStream> {
105
+ let tcp_stream = listener.accept().await?;
106
+ Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
107
+ }
108
+
109
+ pub async fn spawn_state_task(&self, mut shutdown_receiver: Receiver<RunningPhase>) {
110
+ if let TokioListener::TcpTls(
111
+ _,
112
+ ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
113
+ ) = self
114
+ {
115
+ let mut state = state.lock().await;
116
+ loop {
117
+ tokio::select! {
118
+ stream_event = StreamExt::next(&mut *state) => {
119
+ match stream_event {
120
+ Some(event) => info!("ACME Event: {:?}", event),
121
+ None => error!("Received no acme event"),
122
+ }
123
+ },
124
+ _ = shutdown_receiver.changed() => {
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ async fn accept_tls(
133
+ listener: &TokioTcpListener,
134
+ acceptor: &ItsiTlsAcceptor,
135
+ ) -> Result<IoStream> {
136
+ let tcp_stream = listener.accept().await?;
137
+ match acceptor {
138
+ ItsiTlsAcceptor::Manual(tls_acceptor) => {
139
+ Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(tls_acceptor)).await
140
+ }
141
+ ItsiTlsAcceptor::Automatic(acme_acceptor, _, rustls_config) => {
142
+ let accept_future = acme_acceptor.accept(tcp_stream.0);
143
+ match accept_future.await {
144
+ Ok(None) => Err(ItsiError::Pass()),
145
+ Ok(Some(start_handshake)) => {
146
+ let tls_stream = start_handshake.into_stream(rustls_config.clone()).await?;
147
+ Ok(IoStream::TcpTls {
148
+ stream: tls_stream,
149
+ addr: SockAddr::Tcp(Arc::new(tcp_stream.1)),
150
+ })
151
+ }
152
+ Err(error) => {
153
+ error!(error = format!("{:?}", error));
154
+ Err(ItsiError::Pass())
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ async fn accept_unix(listener: &TokioUnixListener) -> Result<IoStream> {
162
+ let unix_stream = listener.accept().await?;
163
+ Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
164
+ }
165
+
166
+ async fn accept_unix_tls(
167
+ listener: &TokioUnixListener,
168
+ acceptor: &ItsiTlsAcceptor,
169
+ ) -> Result<IoStream> {
170
+ let unix_stream = listener.accept().await?;
171
+ match acceptor {
172
+ ItsiTlsAcceptor::Manual(tls_acceptor) => {
173
+ Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(tls_acceptor)).await
174
+ }
175
+ ItsiTlsAcceptor::Automatic(_, _, _) => {
176
+ error!("Automatic TLS not supported on Unix sockets");
177
+ Err(ItsiError::UnsupportedProtocol(
178
+ "Automatic TLS on Unix Sockets".to_owned(),
179
+ ))
59
180
  }
60
181
  }
61
182
  }
@@ -63,156 +184,135 @@ impl Listener {
63
184
  async fn to_tokio_io(
64
185
  input_stream: Stream,
65
186
  tls_acceptor: Option<&TlsAcceptor>,
66
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
187
+ ) -> Result<IoStream> {
67
188
  match tls_acceptor {
68
189
  Some(acceptor) => match input_stream {
69
190
  Stream::TcpStream((tcp_stream, socket_address)) => {
70
191
  match acceptor.accept(tcp_stream).await {
71
- Ok(tls_stream) => Ok((
72
- TokioIo::new(Box::pin(tls_stream) as Pin<Box<dyn IoStream>>),
73
- SockAddr::Tcp(Arc::new(socket_address)),
74
- )),
192
+ Ok(tls_stream) => Ok(IoStream::TcpTls {
193
+ stream: tls_stream,
194
+ addr: SockAddr::Tcp(Arc::new(socket_address)),
195
+ }),
75
196
  Err(err) => Err(err.into()),
76
197
  }
77
198
  }
78
199
  Stream::UnixStream((unix_stream, socket_address)) => {
79
200
  match acceptor.accept(unix_stream).await {
80
- Ok(tls_stream) => Ok((
81
- TokioIo::new(Box::pin(tls_stream) as Pin<Box<dyn IoStream>>),
82
- SockAddr::Unix(Arc::new(socket_address)),
83
- )),
201
+ Ok(tls_stream) => Ok(IoStream::UnixTls {
202
+ stream: tls_stream,
203
+ addr: SockAddr::Unix(Arc::new(socket_address)),
204
+ }),
84
205
  Err(err) => Err(err.into()),
85
206
  }
86
207
  }
87
208
  },
88
209
  None => match input_stream {
89
- Stream::TcpStream((tcp_stream, socket_address)) => Ok((
90
- TokioIo::new(Box::pin(tcp_stream) as Pin<Box<dyn IoStream>>),
91
- SockAddr::Tcp(Arc::new(socket_address)),
92
- )),
93
- Stream::UnixStream((unix_stream, socket_address)) => Ok((
94
- TokioIo::new(Box::pin(unix_stream) as Pin<Box<dyn IoStream>>),
95
- SockAddr::Unix(Arc::new(socket_address)),
96
- )),
210
+ Stream::TcpStream((tcp_stream, socket_address)) => Ok(IoStream::Tcp {
211
+ stream: tcp_stream,
212
+ addr: SockAddr::Tcp(Arc::new(socket_address)),
213
+ }),
214
+ Stream::UnixStream((unix_stream, socket_address)) => Ok(IoStream::Unix {
215
+ stream: unix_stream,
216
+ addr: SockAddr::Unix(Arc::new(socket_address)),
217
+ }),
97
218
  },
98
219
  }
99
220
  }
221
+ }
100
222
 
101
- async fn accept_tcp(
102
- listener: &TcpListener,
103
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
104
- let tcp_stream = listener.accept().await?;
105
- Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
106
- }
107
-
108
- async fn accept_tls(
109
- listener: &TcpListener,
110
- acceptor: &TlsAcceptor,
111
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
112
- let tcp_stream = listener.accept().await?;
113
- Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(acceptor)).await
114
- }
115
-
116
- async fn accept_unix(
117
- listener: &UnixListener,
118
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
119
- let unix_stream = listener.accept().await?;
120
- Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
121
- }
122
-
123
- async fn accept_unix_tls(
124
- listener: &UnixListener,
125
- acceptor: &TlsAcceptor,
126
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
127
- let unix_stream = listener.accept().await?;
128
- Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(acceptor)).await
129
- }
130
-
131
- pub(crate) fn scheme(&self) -> String {
132
- match self {
133
- Listener::Tcp(_) => "http".to_string(),
134
- Listener::TcpTls(_) => "https".to_string(),
135
- Listener::Unix(_) => "http".to_string(),
136
- Listener::UnixTls(_) => "https".to_string(),
137
- }
138
- }
223
+ enum Stream {
224
+ TcpStream((TcpStream, SocketAddr)),
225
+ UnixStream((UnixStream, unix::SocketAddr)),
226
+ }
139
227
 
140
- pub(crate) fn port(&self) -> u16 {
228
+ #[derive(Clone, Debug)]
229
+ pub enum SockAddr {
230
+ Tcp(Arc<SocketAddr>),
231
+ Unix(Arc<unix::SocketAddr>),
232
+ }
233
+ impl std::fmt::Display for SockAddr {
234
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141
235
  match self {
142
- Listener::Tcp(listener) => listener.local_addr().unwrap().port(),
143
- Listener::TcpTls((listener, _)) => listener.local_addr().unwrap().port(),
144
- Listener::Unix(_) => 0,
145
- Listener::UnixTls(_) => 0,
236
+ SockAddr::Tcp(socket_addr) => write!(f, "{}", socket_addr.ip().to_canonical()),
237
+ SockAddr::Unix(socket_addr) => match socket_addr.as_pathname() {
238
+ Some(path) => write!(f, "{:?}", path),
239
+ None => write!(f, ""),
240
+ },
146
241
  }
147
242
  }
243
+ }
148
244
 
149
- pub(crate) fn host(&self) -> String {
245
+ impl Listener {
246
+ pub fn into_tokio_listener(self) -> TokioListener {
150
247
  match self {
151
- Listener::Tcp(listener) => listener.local_addr().unwrap().ip().to_string(),
152
- Listener::TcpTls((listener, _)) => listener.local_addr().unwrap().ip().to_string(),
153
- Listener::Unix(_) => "unix".to_string(),
154
- Listener::UnixTls(_) => "unix".to_string(),
248
+ Listener::Tcp(listener) => {
249
+ TokioListener::Tcp(TokioTcpListener::from_std(listener).unwrap())
250
+ }
251
+ Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls(
252
+ TokioTcpListener::from_std(listener).unwrap(),
253
+ acceptor.clone(),
254
+ ),
255
+ Listener::Unix(listener) => {
256
+ TokioListener::Unix(TokioUnixListener::from_std(listener).unwrap())
257
+ }
258
+ Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls(
259
+ TokioUnixListener::from_std(listener).unwrap(),
260
+ acceptor.clone(),
261
+ ),
155
262
  }
156
263
  }
157
264
  }
158
265
 
159
- impl From<Bind> for Listener {
160
- fn from(bind: Bind) -> Self {
161
- match bind.address {
266
+ impl TryFrom<Bind> for Listener {
267
+ type Error = itsi_error::ItsiError;
268
+
269
+ fn try_from(bind: Bind) -> std::result::Result<Self, Self::Error> {
270
+ let bound = match bind.address {
162
271
  BindAddress::Ip(addr) => match bind.protocol {
163
- TransferProtocol::Http => Listener::Tcp(
164
- TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap())).unwrap(),
165
- ),
166
- TransferProtocol::Https => {
167
- let tcp_listener =
168
- TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap()))
169
- .unwrap();
170
- let tls_acceptor = TlsAcceptor::from(Arc::new(bind.tls_config.unwrap()));
171
- Listener::TcpTls((tcp_listener, tls_acceptor))
272
+ BindProtocol::Http => Listener::Tcp(connect_tcp_socket(addr, bind.port.unwrap())?),
273
+ BindProtocol::Https => {
274
+ let tcp_listener = connect_tcp_socket(addr, bind.port.unwrap())?;
275
+ Listener::TcpTls((tcp_listener, bind.tls_config.unwrap()))
172
276
  }
173
277
  _ => unreachable!(),
174
278
  },
175
279
  BindAddress::UnixSocket(path) => match bind.tls_config {
176
- Some(tls_config) => {
177
- 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
- ))
182
- }
183
- None => Listener::Unix(UnixListener::from_std(connect_unix_socket(&path)).unwrap()),
280
+ Some(tls_config) => Listener::UnixTls((connect_unix_socket(&path)?, tls_config)),
281
+ None => Listener::Unix(connect_unix_socket(&path)?),
184
282
  },
185
- }
283
+ };
284
+ Ok(bound)
186
285
  }
187
286
  }
188
287
 
189
- fn connect_tcp_socket(addr: IpAddr, port: u16) -> StdTcpListener {
288
+ fn connect_tcp_socket(addr: IpAddr, port: u16) -> Result<TcpListener> {
190
289
  let domain = match addr {
191
290
  IpAddr::V4(_) => Domain::IPV4,
192
291
  IpAddr::V6(_) => Domain::IPV6,
193
292
  };
194
- let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).unwrap();
293
+ let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
195
294
  let socket_address: SocketAddr = SocketAddr::new(addr, port);
196
- socket.set_reuse_address(true).ok();
197
295
  socket.set_reuse_port(true).ok();
296
+ socket.set_reuse_address(true).ok();
198
297
  socket.set_nonblocking(true).ok();
199
298
  socket.set_nodelay(true).ok();
200
- socket.set_recv_buffer_size(1_048_576).ok();
201
- info!("Binding to {}", socket_address);
202
- socket.bind(&socket_address.into()).unwrap();
203
- socket.listen(1024).unwrap();
204
- socket.into()
299
+ socket.set_recv_buffer_size(262_144).ok();
300
+ info!("Binding to {:?}", socket_address);
301
+ socket.bind(&socket_address.into())?;
302
+ socket.listen(1024)?;
303
+ Ok(socket.into())
205
304
  }
206
305
 
207
- fn connect_unix_socket(path: &PathBuf) -> StdUnixListener {
306
+ fn connect_unix_socket(path: &PathBuf) -> Result<UnixListener> {
208
307
  let _ = std::fs::remove_file(path);
209
- let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap();
308
+ let socket = Socket::new(Domain::UNIX, Type::STREAM, None)?;
210
309
  socket.set_nonblocking(true).ok();
211
- let socket_address = socket2::SockAddr::unix(path).unwrap();
310
+
311
+ let socket_address = socket2::SockAddr::unix(path)?;
212
312
 
213
313
  info!("Binding to {:?}", path);
214
- socket.bind(&socket_address).unwrap();
215
- socket.listen(1024).unwrap();
314
+ socket.bind(&socket_address)?;
315
+ socket.listen(1024)?;
216
316
 
217
- socket.into()
317
+ Ok(socket.into())
218
318
  }
@@ -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,203 @@
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
+ match call_with_gvl(|_ruby| fork(cluster_template.server.hooks.get("after_fork").cloned()))
57
+ {
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.lock().drain(..).collect(),
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 fn pid(&self) -> i32 {
87
+ if let Some(pid) = *self.child_pid.lock() {
88
+ return pid.as_raw();
89
+ }
90
+ 0
91
+ }
92
+
93
+ pub(crate) fn memory_usage(&self) -> Option<u64> {
94
+ if let Some(pid) = *self.child_pid.lock() {
95
+ let s = System::new_all();
96
+ if let Some(process) = s.process(sysinfo::Pid::from(pid.as_raw() as usize)) {
97
+ return Some(process.memory());
98
+ }
99
+ }
100
+ None
101
+ }
102
+
103
+ pub(crate) async fn reboot(&self, cluster_template: Arc<ClusterMode>) -> Result<bool> {
104
+ self.graceful_shutdown(cluster_template.clone()).await;
105
+ let self_clone = self.clone();
106
+ let (booted_sender, mut booted_receiver) = watch::channel(false);
107
+ create_ruby_thread(move || {
108
+ call_without_gvl(move || {
109
+ if self_clone.boot(cluster_template).is_ok() {
110
+ booted_sender.send(true).ok()
111
+ } else {
112
+ booted_sender.send(false).ok()
113
+ };
114
+ })
115
+ });
116
+
117
+ booted_receiver
118
+ .changed()
119
+ .await
120
+ .map_err(|_| ItsiError::InternalServerError("Failed to boot worker".to_owned()))?;
121
+
122
+ let guard = booted_receiver.borrow();
123
+ let result = guard.to_owned();
124
+ // Not very robust, we should check to see if the worker is actually listening before considering this successful.
125
+ sleep(Duration::from_secs(1)).await;
126
+ Ok(result)
127
+ }
128
+
129
+ pub(crate) async fn graceful_shutdown(&self, cluster_template: Arc<ClusterMode>) {
130
+ let self_clone = self.clone();
131
+ self_clone.request_shutdown();
132
+ let force_kill_time =
133
+ Instant::now() + Duration::from_secs_f64(cluster_template.server.shutdown_timeout);
134
+ while self_clone.is_alive() && force_kill_time > Instant::now() {
135
+ tokio::time::sleep(Duration::from_millis(100)).await;
136
+ }
137
+ if self_clone.is_alive() {
138
+ self_clone.force_kill();
139
+ }
140
+ }
141
+
142
+ pub(crate) fn boot_if_dead(&self, cluster_template: Arc<ClusterMode>) -> bool {
143
+ if !self.is_alive() {
144
+ if self.just_started() {
145
+ error!(
146
+ "Worker in crash loop {:?}. Refusing to restart",
147
+ self.child_pid.lock()
148
+ );
149
+ return false;
150
+ } else {
151
+ let self_clone = self.clone();
152
+ create_ruby_thread(move || {
153
+ call_without_gvl(move || {
154
+ self_clone.boot(cluster_template).ok();
155
+ })
156
+ });
157
+ }
158
+ }
159
+ true
160
+ }
161
+
162
+ pub(crate) fn request_shutdown(&self) {
163
+ let child_pid = *self.child_pid.lock();
164
+ if let Some(pid) = child_pid {
165
+ if let Err(e) = kill(pid, SIGTERM) {
166
+ error!("Failed to send SIGTERM to process {}: {}", pid, e);
167
+ }
168
+ }
169
+ }
170
+
171
+ pub(crate) fn force_kill(&self) {
172
+ let child_pid = *self.child_pid.lock();
173
+ if let Some(pid) = child_pid {
174
+ if let Err(e) = kill(pid, SIGKILL) {
175
+ error!("Failed to force kill process {}: {}", pid, e);
176
+ }
177
+ }
178
+ }
179
+
180
+ pub(crate) fn just_started(&self) -> bool {
181
+ let now = Instant::now();
182
+ now.duration_since(self.started_at).as_millis() < 2000
183
+ }
184
+
185
+ pub(crate) fn is_alive(&self) -> bool {
186
+ let child_pid = *self.child_pid.lock();
187
+ if let Some(pid) = child_pid {
188
+ match waitpid(pid, Some(WaitPidFlag::WNOHANG)) {
189
+ Ok(WaitStatus::Exited(_, _)) | Ok(WaitStatus::Signaled(_, _, _)) => {
190
+ return false;
191
+ }
192
+ Ok(WaitStatus::StillAlive) | Ok(_) => {}
193
+ Err(_) => return false,
194
+ }
195
+ match kill(pid, None) {
196
+ Ok(_) => true,
197
+ Err(errno) => !matches!(errno, Errno::ESRCH),
198
+ }
199
+ } else {
200
+ false
201
+ }
202
+ }
203
+ }