itsi-scheduler 0.1.5 → 0.2.2

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.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +120 -52
  3. data/README.md +57 -24
  4. data/Rakefile +0 -4
  5. data/ext/itsi_acme/Cargo.toml +86 -0
  6. data/ext/itsi_acme/examples/high_level.rs +63 -0
  7. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  8. data/ext/itsi_acme/examples/low_level.rs +87 -0
  9. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  10. data/ext/itsi_acme/src/acceptor.rs +81 -0
  11. data/ext/itsi_acme/src/acme.rs +354 -0
  12. data/ext/itsi_acme/src/axum.rs +86 -0
  13. data/ext/itsi_acme/src/cache.rs +39 -0
  14. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  15. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  16. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  17. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  18. data/ext/itsi_acme/src/caches/no.rs +78 -0
  19. data/ext/itsi_acme/src/caches/test.rs +136 -0
  20. data/ext/itsi_acme/src/config.rs +172 -0
  21. data/ext/itsi_acme/src/https_helper.rs +69 -0
  22. data/ext/itsi_acme/src/incoming.rs +142 -0
  23. data/ext/itsi_acme/src/jose.rs +161 -0
  24. data/ext/itsi_acme/src/lib.rs +142 -0
  25. data/ext/itsi_acme/src/resolver.rs +59 -0
  26. data/ext/itsi_acme/src/state.rs +424 -0
  27. data/ext/itsi_error/Cargo.toml +1 -0
  28. data/ext/itsi_error/src/lib.rs +106 -7
  29. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  30. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  31. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  32. data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  33. data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
  34. data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
  35. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
  36. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
  37. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
  38. data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
  39. data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
  40. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
  41. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
  42. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
  43. data/ext/itsi_rb_helpers/Cargo.toml +1 -0
  44. data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
  45. data/ext/itsi_rb_helpers/src/lib.rs +63 -12
  46. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  47. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  48. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  49. 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
  50. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
  51. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
  52. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
  53. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
  54. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
  55. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
  56. data/ext/itsi_scheduler/Cargo.toml +1 -1
  57. data/ext/itsi_scheduler/src/itsi_scheduler.rs +9 -3
  58. data/ext/itsi_scheduler/src/lib.rs +1 -0
  59. data/ext/itsi_server/Cargo.lock +2956 -0
  60. data/ext/itsi_server/Cargo.toml +73 -29
  61. data/ext/itsi_server/src/default_responses/mod.rs +11 -0
  62. data/ext/itsi_server/src/env.rs +43 -0
  63. data/ext/itsi_server/src/lib.rs +114 -75
  64. data/ext/itsi_server/src/prelude.rs +2 -0
  65. data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  66. data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +29 -8
  67. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +344 -0
  68. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +264 -0
  69. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +362 -0
  70. data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +84 -40
  71. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +233 -0
  72. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +565 -0
  73. data/ext/itsi_server/src/ruby_types/itsi_server.rs +86 -0
  74. data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
  75. data/ext/itsi_server/src/server/{bind.rs → binds/bind.rs} +59 -24
  76. data/ext/itsi_server/src/server/binds/listener.rs +444 -0
  77. data/ext/itsi_server/src/server/binds/mod.rs +4 -0
  78. data/ext/itsi_server/src/server/{tls → binds/tls}/locked_dir_cache.rs +57 -19
  79. data/ext/itsi_server/src/server/{tls.rs → binds/tls.rs} +120 -31
  80. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  81. data/ext/itsi_server/src/server/http_message_types.rs +97 -0
  82. data/ext/itsi_server/src/server/io_stream.rs +2 -1
  83. data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
  84. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
  85. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
  86. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
  87. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +94 -0
  88. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
  89. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
  90. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +316 -0
  91. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +301 -0
  92. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
  93. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
  94. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +192 -0
  95. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +171 -0
  96. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +198 -0
  97. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
  98. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +209 -0
  99. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  100. data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  101. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +116 -0
  102. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +411 -0
  103. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +142 -0
  104. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +55 -0
  105. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
  106. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -0
  107. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +126 -0
  108. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +187 -0
  109. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +55 -0
  110. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +173 -0
  111. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
  112. data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
  113. data/ext/itsi_server/src/server/mod.rs +7 -5
  114. data/ext/itsi_server/src/server/process_worker.rs +65 -14
  115. data/ext/itsi_server/src/server/redirect_type.rs +26 -0
  116. data/ext/itsi_server/src/server/request_job.rs +11 -0
  117. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +150 -50
  118. data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
  119. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +399 -165
  120. data/ext/itsi_server/src/server/signal.rs +33 -26
  121. data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
  122. data/ext/itsi_server/src/server/thread_worker.rs +218 -107
  123. data/ext/itsi_server/src/services/cache_store.rs +74 -0
  124. data/ext/itsi_server/src/services/itsi_http_service.rs +257 -0
  125. data/ext/itsi_server/src/services/mime_types.rs +1416 -0
  126. data/ext/itsi_server/src/services/mod.rs +6 -0
  127. data/ext/itsi_server/src/services/password_hasher.rs +83 -0
  128. data/ext/itsi_server/src/services/rate_limiter.rs +580 -0
  129. data/ext/itsi_server/src/services/static_file_server.rs +1340 -0
  130. data/ext/itsi_tracing/Cargo.toml +1 -0
  131. data/ext/itsi_tracing/src/lib.rs +362 -33
  132. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
  133. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
  134. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
  135. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
  136. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
  137. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
  138. data/itsi-scheduler-100.png +0 -0
  139. data/lib/itsi/scheduler/version.rb +1 -1
  140. data/lib/itsi/scheduler.rb +11 -6
  141. metadata +117 -24
  142. data/CHANGELOG.md +0 -5
  143. data/CODE_OF_CONDUCT.md +0 -132
  144. data/LICENSE.txt +0 -21
  145. data/ext/itsi_error/src/from.rs +0 -71
  146. data/ext/itsi_server/extconf.rb +0 -6
  147. data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  148. data/ext/itsi_server/src/request/itsi_request.rs +0 -277
  149. data/ext/itsi_server/src/request/mod.rs +0 -1
  150. data/ext/itsi_server/src/response/mod.rs +0 -1
  151. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -13
  152. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -5
  153. data/ext/itsi_server/src/server/itsi_server.rs +0 -244
  154. data/ext/itsi_server/src/server/listener.rs +0 -327
  155. /data/ext/itsi_server/src/server/{bind_protocol.rs → binds/bind_protocol.rs} +0 -0
@@ -0,0 +1,257 @@
1
+ use crate::default_responses::{NOT_FOUND_RESPONSE, TIMEOUT_RESPONSE};
2
+ use crate::ruby_types::itsi_server::itsi_server_config::{ItsiServerTokenPreference, ServerParams};
3
+ use crate::server::binds::listener::ListenerInfo;
4
+ use crate::server::http_message_types::{ConversionExt, HttpResponse, RequestExt, ResponseFormat};
5
+ use crate::server::lifecycle_event::LifecycleEvent;
6
+ use crate::server::middleware_stack::MiddlewareLayer;
7
+ use crate::server::request_job::RequestJob;
8
+ use crate::server::serve_strategy::single_mode::RunningPhase;
9
+ use crate::server::signal::send_lifecycle_event;
10
+ use chrono;
11
+ use chrono::Local;
12
+ use either::Either;
13
+ use http::header::ACCEPT_ENCODING;
14
+ use http::{HeaderValue, Request};
15
+ use hyper::body::Incoming;
16
+ use hyper::service::Service;
17
+ use itsi_error::ItsiError;
18
+ use regex::Regex;
19
+ use std::sync::atomic::{AtomicBool, Ordering};
20
+ use std::sync::OnceLock;
21
+ use tracing::error;
22
+
23
+ use std::{future::Future, ops::Deref, pin::Pin, sync::Arc};
24
+ use tokio::sync::watch::{self};
25
+ use tokio::time::timeout;
26
+
27
+ #[derive(Clone)]
28
+ pub struct ItsiHttpService {
29
+ pub inner: Arc<ItsiHttpServiceInner>,
30
+ }
31
+
32
+ impl Deref for ItsiHttpService {
33
+ type Target = Arc<ItsiHttpServiceInner>;
34
+
35
+ fn deref(&self) -> &Self::Target {
36
+ &self.inner
37
+ }
38
+ }
39
+
40
+ pub struct ItsiHttpServiceInner {
41
+ pub sender: async_channel::Sender<RequestJob>,
42
+ pub nonblocking_sender: async_channel::Sender<RequestJob>,
43
+ pub server_params: Arc<ServerParams>,
44
+ pub listener: Arc<ListenerInfo>,
45
+ pub addr: String,
46
+ pub shutdown_channel: watch::Receiver<RunningPhase>,
47
+ }
48
+
49
+ #[derive(Clone)]
50
+ pub struct HttpRequestContext {
51
+ inner: Arc<RequestContextInner>,
52
+ }
53
+
54
+ impl Deref for HttpRequestContext {
55
+ type Target = Arc<RequestContextInner>;
56
+
57
+ fn deref(&self) -> &Self::Target {
58
+ &self.inner
59
+ }
60
+ }
61
+
62
+ impl Deref for RequestContextInner {
63
+ type Target = ItsiHttpService;
64
+
65
+ fn deref(&self) -> &Self::Target {
66
+ &self.service
67
+ }
68
+ }
69
+
70
+ pub struct RequestContextInner {
71
+ pub request_id: u128,
72
+ pub service: ItsiHttpService,
73
+ pub accept: ResponseFormat,
74
+ pub matching_pattern: Option<Arc<Regex>>,
75
+ pub origin: OnceLock<Option<String>>,
76
+ pub response_format: OnceLock<ResponseFormat>,
77
+ pub start_time: chrono::DateTime<chrono::Utc>,
78
+ pub request: Option<Arc<Request<Incoming>>>,
79
+ pub request_start_time: OnceLock<chrono::DateTime<Local>>,
80
+ pub if_none_match: OnceLock<Option<String>>,
81
+ pub supported_encoding_set: Vec<HeaderValue>,
82
+ pub is_ruby_request: Arc<AtomicBool>,
83
+ }
84
+
85
+ impl HttpRequestContext {
86
+ fn new(
87
+ service: ItsiHttpService,
88
+ matching_pattern: Option<Arc<Regex>>,
89
+ accept: ResponseFormat,
90
+ supported_encoding_set: Vec<HeaderValue>,
91
+ is_ruby_request: Arc<AtomicBool>,
92
+ ) -> Self {
93
+ HttpRequestContext {
94
+ inner: Arc::new(RequestContextInner {
95
+ request_id: rand::random::<u128>(),
96
+ service,
97
+ matching_pattern,
98
+ accept,
99
+ origin: OnceLock::new(),
100
+ response_format: OnceLock::new(),
101
+ start_time: chrono::Utc::now(),
102
+ request: None,
103
+ request_start_time: OnceLock::new(),
104
+ if_none_match: OnceLock::new(),
105
+ supported_encoding_set,
106
+ is_ruby_request,
107
+ }),
108
+ }
109
+ }
110
+
111
+ pub fn set_origin(&self, origin: Option<String>) {
112
+ self.inner.origin.set(origin).unwrap();
113
+ }
114
+
115
+ pub fn set_if_none_match(&self, value: Option<String>) {
116
+ self.inner.if_none_match.set(value).unwrap();
117
+ }
118
+
119
+ pub fn get_if_none_match(&self) -> Option<String> {
120
+ self.inner.if_none_match.get().cloned().flatten()
121
+ }
122
+
123
+ pub fn short_request_id(&self) -> String {
124
+ format!("{:016x}", self.inner.request_id & 0xffff_ffff_ffff_ffff)
125
+ }
126
+
127
+ pub fn request_id(&self) -> String {
128
+ format!("{:016x}", self.inner.request_id)
129
+ }
130
+
131
+ pub fn track_start_time(&self) {
132
+ self.inner
133
+ .request_start_time
134
+ .get_or_init(chrono::Local::now);
135
+ }
136
+
137
+ pub fn start_time(&self) -> Option<chrono::DateTime<Local>> {
138
+ self.inner.request_start_time.get().cloned()
139
+ }
140
+
141
+ pub fn get_response_time(&self) -> Option<chrono::TimeDelta> {
142
+ self.inner
143
+ .request_start_time
144
+ .get()
145
+ .map(|instant| Local::now() - instant)
146
+ }
147
+
148
+ pub fn set_response_format(&self, format: ResponseFormat) {
149
+ self.inner.response_format.set(format).unwrap()
150
+ }
151
+
152
+ pub fn response_format(&self) -> &ResponseFormat {
153
+ self.inner.response_format.get().unwrap()
154
+ }
155
+ }
156
+
157
+ const SERVER_TOKEN_VERSION: HeaderValue =
158
+ HeaderValue::from_static(concat!("Itsi/", env!("CARGO_PKG_VERSION")));
159
+ const SERVER_TOKEN_NAME: HeaderValue = HeaderValue::from_static("Itsi");
160
+
161
+ impl Service<Request<Incoming>> for ItsiHttpService {
162
+ type Response = HttpResponse;
163
+ type Error = ItsiError;
164
+ type Future = Pin<Box<dyn Future<Output = itsi_error::Result<HttpResponse>> + Send>>;
165
+
166
+ fn call(&self, req: Request<Incoming>) -> Self::Future {
167
+ let params = self.server_params.clone();
168
+ let self_clone = self.clone();
169
+ let mut req = req.limit();
170
+ let accept: ResponseFormat = req.accept().into();
171
+ let accept_clone = accept.clone();
172
+ let is_single_mode = self.server_params.workers == 1;
173
+ let supported_encoding_set = req
174
+ .headers()
175
+ .get_all(ACCEPT_ENCODING)
176
+ .into_iter()
177
+ .cloned()
178
+ .collect::<Vec<_>>();
179
+ let request_timeout = self.server_params.request_timeout;
180
+ let is_ruby_request = Arc::new(AtomicBool::new(false));
181
+ let irr_clone = is_ruby_request.clone();
182
+ let service_future = async move {
183
+ let mut resp: Option<HttpResponse> = None;
184
+ let (stack, matching_pattern) = params.middleware.get().unwrap().stack_for(&req)?;
185
+
186
+ let mut context = HttpRequestContext::new(
187
+ self_clone,
188
+ matching_pattern,
189
+ accept_clone.clone(),
190
+ supported_encoding_set,
191
+ irr_clone,
192
+ );
193
+ let mut depth = 0;
194
+
195
+ for (index, elm) in stack.iter().enumerate() {
196
+ match elm.before(req, &mut context).await {
197
+ Ok(Either::Left(r)) => req = r,
198
+ Ok(Either::Right(r)) => {
199
+ resp = Some(r);
200
+ depth = index;
201
+ break;
202
+ }
203
+ Err(e) => {
204
+ error!("Middleware error: {}", e);
205
+ break;
206
+ }
207
+ }
208
+ }
209
+
210
+ let mut resp = match resp {
211
+ Some(r) => r,
212
+ None => return Ok(NOT_FOUND_RESPONSE.to_http_response(accept_clone).await),
213
+ };
214
+
215
+ for elm in stack.iter().rev().skip(stack.len() - depth - 1) {
216
+ resp = elm.after(resp, &mut context).await;
217
+ }
218
+
219
+ match params.itsi_server_token_preference {
220
+ ItsiServerTokenPreference::Version => {
221
+ resp.headers_mut().insert("Server", SERVER_TOKEN_VERSION);
222
+ }
223
+ ItsiServerTokenPreference::Name => {
224
+ resp.headers_mut().insert("Server", SERVER_TOKEN_NAME);
225
+ }
226
+ ItsiServerTokenPreference::None => {}
227
+ }
228
+
229
+ Ok(resp)
230
+ };
231
+
232
+ Box::pin(async move {
233
+ if let Some(timeout_duration) = request_timeout {
234
+ match timeout(timeout_duration, service_future).await {
235
+ Ok(result) => result,
236
+ Err(_) => {
237
+ // If we're still running Ruby at this point, we can't just kill the
238
+ // thread as it might be in a critical section.
239
+ // Instead we must ask the worker to hot restart.
240
+ if is_ruby_request.load(Ordering::Relaxed) {
241
+ if is_single_mode {
242
+ // If we're in single mode, re-exec the whole process
243
+ send_lifecycle_event(LifecycleEvent::Restart);
244
+ } else {
245
+ // Otherwise we can shutdown the worker and rely on the master to restart it
246
+ send_lifecycle_event(LifecycleEvent::Shutdown);
247
+ }
248
+ }
249
+ Ok(TIMEOUT_RESPONSE.to_http_response(accept).await)
250
+ }
251
+ }
252
+ } else {
253
+ service_future.await
254
+ }
255
+ })
256
+ }
257
+ }