itsi 0.1.9 → 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.
- checksums.yaml +4 -4
- data/Cargo.lock +11 -2
- data/Rakefile +5 -2
- data/crates/itsi_rb_helpers/src/lib.rs +27 -4
- data/crates/itsi_server/Cargo.toml +4 -1
- data/crates/itsi_server/src/lib.rs +69 -1
- data/crates/itsi_server/src/request/itsi_request.rs +2 -9
- data/crates/itsi_server/src/response/itsi_response.rs +2 -2
- data/crates/itsi_server/src/server/bind.rs +16 -12
- data/crates/itsi_server/src/server/itsi_server.rs +43 -49
- data/crates/itsi_server/src/server/listener.rs +9 -9
- data/crates/itsi_server/src/server/process_worker.rs +10 -3
- data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +15 -9
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +124 -111
- data/crates/itsi_server/src/server/signal.rs +1 -4
- data/crates/itsi_server/src/server/thread_worker.rs +52 -20
- data/crates/itsi_server/src/server/tls.rs +1 -1
- data/gems/scheduler/ext/itsi_rb_helpers/src/lib.rs +27 -4
- data/gems/scheduler/ext/itsi_server/Cargo.toml +4 -1
- data/gems/scheduler/ext/itsi_server/src/lib.rs +69 -1
- data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +2 -9
- data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +2 -2
- data/gems/scheduler/ext/itsi_server/src/server/bind.rs +16 -12
- data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +43 -49
- data/gems/scheduler/ext/itsi_server/src/server/listener.rs +9 -9
- data/gems/scheduler/ext/itsi_server/src/server/process_worker.rs +10 -3
- data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +15 -9
- data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +124 -111
- data/gems/scheduler/ext/itsi_server/src/server/signal.rs +1 -4
- data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +52 -20
- data/gems/scheduler/ext/itsi_server/src/server/tls.rs +1 -1
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/server/Cargo.lock +11 -2
- data/gems/server/exe/itsi +53 -23
- data/gems/server/ext/itsi_rb_helpers/src/lib.rs +27 -4
- data/gems/server/ext/itsi_server/Cargo.toml +4 -1
- data/gems/server/ext/itsi_server/src/lib.rs +69 -1
- data/gems/server/ext/itsi_server/src/request/itsi_request.rs +2 -9
- data/gems/server/ext/itsi_server/src/response/itsi_response.rs +2 -2
- data/gems/server/ext/itsi_server/src/server/bind.rs +16 -12
- data/gems/server/ext/itsi_server/src/server/itsi_server.rs +43 -49
- data/gems/server/ext/itsi_server/src/server/listener.rs +9 -9
- data/gems/server/ext/itsi_server/src/server/process_worker.rs +10 -3
- data/gems/server/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +15 -9
- data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +124 -111
- data/gems/server/ext/itsi_server/src/server/signal.rs +1 -4
- data/gems/server/ext/itsi_server/src/server/thread_worker.rs +52 -20
- data/gems/server/ext/itsi_server/src/server/tls.rs +1 -1
- data/gems/server/lib/itsi/server/Itsi.rb +127 -0
- data/gems/server/lib/itsi/server/config.rb +36 -0
- data/gems/server/lib/itsi/server/options_dsl.rb +401 -0
- data/gems/server/lib/itsi/server/rack/handler/itsi.rb +18 -6
- data/gems/server/lib/itsi/server/rack_interface.rb +1 -5
- data/gems/server/lib/itsi/server/signal_trap.rb +0 -1
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +7 -3
- data/gems/server/test/helpers/test_helper.rb +7 -5
- data/gems/server/test/test_itsi_server.rb +21 -2
- data/lib/itsi/version.rb +1 -1
- data/location_dsl.rb +381 -0
- data/sandbox/itsi_itsi_file/Itsi.rb +119 -0
- data/sandbox/itsi_sandbox_async/Gemfile +1 -1
- data/sandbox/itsi_sandbox_rack/Gemfile.lock +2 -2
- data/sandbox/itsi_sandbox_rails/Gemfile.lock +2 -2
- data/tasks.txt +27 -4
- metadata +14 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59369e9fbde937f7f81412211e2003f3537bb0b3adcfe5ac65f141cdb74bd150
|
4
|
+
data.tar.gz: a0bc192989bcfd5da5223ee5d1602da8e70775383cb0e04db8a7ab5f759ba602
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f0a6aa10401ce95b78701ee49761449b10091cbe7262337fb2ff07812866852bca54c43bb17c530c5f6a403058b4bca930cdf756713efdf2a5652636906b737
|
7
|
+
data.tar.gz: 23547b8969bb3b2086f59ea1d3b25c9414bea53410ae50f21d928f0d95edda7a25e979ca5b2860f3e71453632d9c32d1c74c548d0c834970177b8f7a17eaaf3b
|
data/Cargo.lock
CHANGED
@@ -1011,6 +1011,7 @@ dependencies = [
|
|
1011
1011
|
"crossbeam",
|
1012
1012
|
"derive_more",
|
1013
1013
|
"dirs",
|
1014
|
+
"fnv",
|
1014
1015
|
"fs2",
|
1015
1016
|
"futures",
|
1016
1017
|
"http",
|
@@ -1027,7 +1028,9 @@ dependencies = [
|
|
1027
1028
|
"pin-project",
|
1028
1029
|
"rb-sys",
|
1029
1030
|
"rcgen",
|
1031
|
+
"regex",
|
1030
1032
|
"ring",
|
1033
|
+
"route-recognizer",
|
1031
1034
|
"rustls",
|
1032
1035
|
"rustls-pemfile",
|
1033
1036
|
"socket2",
|
@@ -1758,6 +1761,12 @@ dependencies = [
|
|
1758
1761
|
"windows-sys 0.52.0",
|
1759
1762
|
]
|
1760
1763
|
|
1764
|
+
[[package]]
|
1765
|
+
name = "route-recognizer"
|
1766
|
+
version = "0.3.1"
|
1767
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1768
|
+
checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
|
1769
|
+
|
1761
1770
|
[[package]]
|
1762
1771
|
name = "rustc-demangle"
|
1763
1772
|
version = "0.1.24"
|
@@ -2170,9 +2179,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|
2170
2179
|
|
2171
2180
|
[[package]]
|
2172
2181
|
name = "tokio"
|
2173
|
-
version = "1.
|
2182
|
+
version = "1.44.1"
|
2174
2183
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
2175
|
-
checksum = "
|
2184
|
+
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
|
2176
2185
|
dependencies = [
|
2177
2186
|
"backtrace",
|
2178
2187
|
"bytes",
|
data/Rakefile
CHANGED
@@ -83,13 +83,16 @@ end
|
|
83
83
|
task :test_env_down do
|
84
84
|
system("terraform -chdir=sandbox/deploy destroy")
|
85
85
|
end
|
86
|
-
|
87
86
|
%i[itsi puma iodine falcon unicorn].each do |server|
|
88
87
|
namespace server do
|
89
88
|
%i[hanami roda async rack rack_lint rails sinatra].each do |sandbox|
|
90
89
|
namespace sandbox do
|
91
90
|
task :serve do |args|
|
92
|
-
|
91
|
+
begin
|
92
|
+
system("(cd sandbox/itsi_sandbox_#{sandbox} && bundle exec #{server} #{ARGV[2..]&.join(" ")} )")
|
93
|
+
rescue Interrupt
|
94
|
+
# Suppress the stacktrace and message for Interrupt
|
95
|
+
end
|
93
96
|
end
|
94
97
|
end
|
95
98
|
end
|
@@ -1,7 +1,8 @@
|
|
1
|
-
use std::{os::raw::c_void, ptr::null_mut
|
1
|
+
use std::{os::raw::c_void, ptr::null_mut};
|
2
2
|
|
3
3
|
use magnus::{
|
4
4
|
RArray, Ruby, Thread, Value,
|
5
|
+
block::Proc,
|
5
6
|
rb_sys::FromRawValue,
|
6
7
|
value::{LazyId, ReprValue},
|
7
8
|
};
|
@@ -17,6 +18,7 @@ static ID_LIST: LazyId = LazyId::new("list");
|
|
17
18
|
static ID_EQ: LazyId = LazyId::new("==");
|
18
19
|
static ID_ALIVE: LazyId = LazyId::new("alive?");
|
19
20
|
static ID_THREAD_VARIABLE_GET: LazyId = LazyId::new("thread_variable_get");
|
21
|
+
static ID_BACKTRACE: LazyId = LazyId::new("backtrace");
|
20
22
|
|
21
23
|
pub fn schedule_thread() {
|
22
24
|
unsafe {
|
@@ -120,20 +122,30 @@ where
|
|
120
122
|
*result_box
|
121
123
|
}
|
122
124
|
|
123
|
-
pub fn fork(after_fork:
|
125
|
+
pub fn fork(after_fork: Option<HeapValue<Proc>>) -> Option<i32> {
|
124
126
|
let ruby = Ruby::get().unwrap();
|
125
127
|
let fork_result = ruby
|
126
128
|
.module_kernel()
|
127
129
|
.funcall::<_, _, Option<i32>>(*ID_FORK, ())
|
128
130
|
.unwrap();
|
129
131
|
if fork_result.is_none() {
|
130
|
-
if let Some(
|
131
|
-
|
132
|
+
if let Some(proc) = after_fork {
|
133
|
+
call_proc_and_log_errors(proc)
|
132
134
|
}
|
133
135
|
}
|
134
136
|
fork_result
|
135
137
|
}
|
136
138
|
|
139
|
+
pub fn call_proc_and_log_errors(proc: HeapValue<Proc>) {
|
140
|
+
if let Err(e) = proc.call::<_, Value>(()) {
|
141
|
+
if let Some(value) = e.value() {
|
142
|
+
print_rb_backtrace(value);
|
143
|
+
} else {
|
144
|
+
eprintln!("Error occurred {:?}", e);
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
137
149
|
pub fn kill_threads<T>(threads: Vec<T>)
|
138
150
|
where
|
139
151
|
T: ReprValue,
|
@@ -176,3 +188,14 @@ pub fn terminate_non_fork_safe_threads() {
|
|
176
188
|
|
177
189
|
kill_threads(non_fork_safe_threads);
|
178
190
|
}
|
191
|
+
|
192
|
+
pub fn print_rb_backtrace(rb_err: Value) {
|
193
|
+
let backtrace = rb_err
|
194
|
+
.funcall::<_, _, Vec<String>>(*ID_BACKTRACE, ())
|
195
|
+
.unwrap_or_default();
|
196
|
+
|
197
|
+
eprintln!("Ruby exception {:?}", rb_err);
|
198
|
+
for line in backtrace {
|
199
|
+
eprintln!("{}", line);
|
200
|
+
}
|
201
|
+
}
|
@@ -24,7 +24,7 @@ rcgen = { version = "0.13.2", features = ["x509-parser", "pem"] }
|
|
24
24
|
base64 = "0.22.1"
|
25
25
|
http-body-util = "0.1.2"
|
26
26
|
hyper = { version = "1.5.0", features = ["full", "server", "http1", "http2"] }
|
27
|
-
tokio = { version = "1", features = ["full"] }
|
27
|
+
tokio = { version = "1.44.1", features = ["full"] }
|
28
28
|
hyper-util = { version = "0.1.10", features = ["full"] }
|
29
29
|
derive_more = { version = "2.0.1", features = ["debug"] }
|
30
30
|
http = "1.3.1"
|
@@ -45,3 +45,6 @@ fs2 = "0.4.3"
|
|
45
45
|
ring = "0.17.14"
|
46
46
|
async-trait = "0.1.87"
|
47
47
|
dirs = "6.0.0"
|
48
|
+
regex = "1.11.1"
|
49
|
+
route-recognizer = "0.3.1"
|
50
|
+
fnv = "1.0.7"
|
@@ -1,5 +1,8 @@
|
|
1
1
|
use body_proxy::itsi_body_proxy::ItsiBodyProxy;
|
2
|
-
use magnus::{
|
2
|
+
use magnus::{
|
3
|
+
error::Result, function, method, value::Lazy, Module, Object, RClass, RHash, RModule, Ruby,
|
4
|
+
};
|
5
|
+
use regex::{Regex, RegexSet};
|
3
6
|
use request::itsi_request::ItsiRequest;
|
4
7
|
use response::itsi_response::ItsiResponse;
|
5
8
|
use server::{itsi_server::Server, signal::reset_signal_handlers};
|
@@ -54,6 +57,70 @@ pub fn log_error(msg: String) {
|
|
54
57
|
error!(msg);
|
55
58
|
}
|
56
59
|
|
60
|
+
const ROUTES: [&str; 39] = [
|
61
|
+
r"(?-u)^/organisations/(?<organisation_id>\d+)/users/(?<user_id>\d+)$",
|
62
|
+
r"(?-u)^/projects/(?<project_id>\d+)/tasks/(?<task_id>\d+)$",
|
63
|
+
r"(?-u)^/products/(?<product_id>\d+)(?:/reviews/(?<review_id>\d+))?$",
|
64
|
+
r"(?-u)^/orders/(?<order_id>\d+)/items(?:/(?<item_id>\d+))?$",
|
65
|
+
r"(?-u)^/posts/(?<post_id>\d+)/comments(?:/(?<comment_id>\d+))?$",
|
66
|
+
r"(?-u)^/teams/(?<team_id>\d+)(?:/members/(?<member_id>\d+))?$",
|
67
|
+
r"(?-u)^/categories/(?<category_id>\d+)/subcategories(?:/(?<subcategory_id>\d+))?$",
|
68
|
+
r"(?-u)^/departments/(?<department_id>\d+)/employees/(?<employee_id>\d+)$",
|
69
|
+
r"(?-u)^/events/(?<event_id>\d+)(?:/sessions/(?<session_id>\d+))?$",
|
70
|
+
r"(?-u)^/invoices/(?<invoice_id>\d+)/payments(?:/(?<payment_id>\d+))?$",
|
71
|
+
r"(?-u)^/tickets/(?<ticket_id>\d+)(?:/responses/(?<response_id>\d+))?$",
|
72
|
+
r"(?-u)^/forums/(?<forum_id>\d+)(?:/threads/(?<thread_id>\d+))?$",
|
73
|
+
r"(?-u)^/subscriptions/(?<subscription_id>\d+)/plans(?:/(?<plan_id>\d+))?$",
|
74
|
+
r"(?-u)^/profiles/(?<profile_id>\d+)/settings$",
|
75
|
+
r"(?-u)^/organizations/(?<organization_id>\d+)/billing(?:/(?<billing_id>\d+))?$",
|
76
|
+
r"(?-u)^/vendors/(?<vendor_id>\d+)/products(?:/(?<product_id>\d+))?$",
|
77
|
+
r"(?-u)^/courses/(?<course_id>\d+)/modules(?:/(?<module_id>\d+))?$",
|
78
|
+
r"(?-u)^/accounts/(?<account_id>\d+)(?:/transactions/(?<transaction_id>\d+))?$",
|
79
|
+
r"(?-u)^/warehouses/(?<warehouse_id>\d+)/inventory(?:/(?<inventory_id>\d+))?$",
|
80
|
+
r"(?-u)^/campaigns/(?<campaign_id>\d+)/ads(?:/(?<ad_id>\d+))?$",
|
81
|
+
r"(?-u)^/applications/(?<application_id>\d+)/stages(?:/(?<stage_id>\d+))?$",
|
82
|
+
r"(?-u)^/notifications/(?<notification_id>\d+)$",
|
83
|
+
r"(?-u)^/albums/(?<album_id>\d+)/photos(?:/(?<photo_id>\d+))?$",
|
84
|
+
r"(?-u)^/news/(?<news_id>\d+)/articles(?:/(?<article_id>\d+))?$",
|
85
|
+
r"(?-u)^/libraries/(?<library_id>\d+)/books(?:/(?<book_id>\d+))?$",
|
86
|
+
r"(?-u)^/universities/(?<university_id>\d+)/students(?:/(?<student_id>\d+))?$",
|
87
|
+
r"(?-u)^/banks/(?<bank_id>\d+)/branches(?:/(?<branch_id>\d+))?$",
|
88
|
+
r"(?-u)^/vehicles/(?<vehicle_id>\d+)/services(?:/(?<service_id>\d+))?$",
|
89
|
+
r"(?-u)^/hotels/(?<hotel_id>\d+)/rooms(?:/(?<room_id>\d+))?$",
|
90
|
+
r"(?-u)^/doctors/(?<doctor_id>\d+)/appointments(?:/(?<appointment_id>\d+))?$",
|
91
|
+
r"(?-u)^/gyms/(?<gym_id>\d+)/memberships(?:/(?<membership_id>\d+))?$",
|
92
|
+
r"(?-u)^/restaurants/(?<restaurant_id>\d+)/menus(?:/(?<menu_id>\d+))?$",
|
93
|
+
r"(?-u)^/parks/(?<park_id>\d+)/events(?:/(?<event_id>\d+))?$",
|
94
|
+
r"(?-u)^/theaters/(?<theater_id>\d+)/shows(?:/(?<show_id>\d+))?$",
|
95
|
+
r"(?-u)^/museums/(?<museum_id>\d+)/exhibits(?:/(?<exhibit_id>\d+))?$",
|
96
|
+
r"(?-u)^/stadiums/(?<stadium_id>\d+)/games(?:/(?<game_id>\d+))?$",
|
97
|
+
r"(?-u)^/schools/(?<school_id>\d+)/classes(?:/(?<class_id>\d+))?$",
|
98
|
+
r"(?-u)^/clubs/(?<club_id>\d+)/events(?:/(?<event_id>\d+))?$",
|
99
|
+
r"(?-u)^/festivals/(?<festival_id>\d+)/tickets(?:/(?<ticket_id>\d+))?$",
|
100
|
+
];
|
101
|
+
use std::sync::LazyLock;
|
102
|
+
|
103
|
+
static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| RegexSet::new(ROUTES).unwrap());
|
104
|
+
static REGEXES: LazyLock<Vec<Regex>> =
|
105
|
+
LazyLock::new(|| ROUTES.iter().map(|&r| Regex::new(r).unwrap()).collect());
|
106
|
+
|
107
|
+
fn match_route(input: String) -> Result<Option<(usize, Option<RHash>)>> {
|
108
|
+
if let Some(index) = REGEX_SET.matches(&input).iter().next() {
|
109
|
+
let regex = ®EXES[index];
|
110
|
+
if let Some(captures) = regex.captures(&input) {
|
111
|
+
let params = RHash::with_capacity(captures.len());
|
112
|
+
for name in regex.capture_names().flatten() {
|
113
|
+
if let Some(value) = captures.name(name) {
|
114
|
+
params.aset(name, value.as_str()).ok();
|
115
|
+
}
|
116
|
+
}
|
117
|
+
return Ok(Some((index, Some(params))));
|
118
|
+
}
|
119
|
+
return Ok(Some((index, None)));
|
120
|
+
}
|
121
|
+
Ok(None)
|
122
|
+
}
|
123
|
+
|
57
124
|
#[magnus::init]
|
58
125
|
fn init(ruby: &Ruby) -> Result<()> {
|
59
126
|
itsi_tracing::init();
|
@@ -62,6 +129,7 @@ fn init(ruby: &Ruby) -> Result<()> {
|
|
62
129
|
.ok();
|
63
130
|
|
64
131
|
let itsi = ruby.get_inner(&ITSI_MODULE);
|
132
|
+
itsi.define_singleton_method("match_route", function!(match_route, 1))?;
|
65
133
|
itsi.define_singleton_method("log_debug", function!(log_debug, 1))?;
|
66
134
|
itsi.define_singleton_method("log_info", function!(log_info, 1))?;
|
67
135
|
itsi.define_singleton_method("log_warn", function!(log_warn, 1))?;
|
@@ -17,6 +17,7 @@ use http::{request::Parts, HeaderValue, Response, StatusCode};
|
|
17
17
|
use http_body_util::{combinators::BoxBody, BodyExt, Empty};
|
18
18
|
use hyper::{body::Incoming, Request};
|
19
19
|
use itsi_error::from::CLIENT_CONNECTION_CLOSED;
|
20
|
+
use itsi_rb_helpers::print_rb_backtrace;
|
20
21
|
use itsi_tracing::{debug, error};
|
21
22
|
use magnus::{
|
22
23
|
error::{ErrorType, Result as MagnusResult},
|
@@ -33,7 +34,6 @@ use tokio::sync::{
|
|
33
34
|
};
|
34
35
|
static ID_CALL: LazyId = LazyId::new("call");
|
35
36
|
static ID_MESSAGE: LazyId = LazyId::new("message");
|
36
|
-
static ID_BACKTRACE: LazyId = LazyId::new("backtrace");
|
37
37
|
|
38
38
|
#[derive(Debug)]
|
39
39
|
#[magnus::wrap(class = "Itsi::Request", free_immediately, size)]
|
@@ -115,14 +115,7 @@ impl ItsiRequest {
|
|
115
115
|
debug!("Connection closed by client");
|
116
116
|
response.close();
|
117
117
|
} else if let Some(rb_err) = err.value() {
|
118
|
-
|
119
|
-
.funcall::<_, _, Vec<String>>(*ID_BACKTRACE, ())
|
120
|
-
.unwrap_or_default();
|
121
|
-
|
122
|
-
error!("Error occurred in Handler: {:?}", rb_err);
|
123
|
-
for line in backtrace {
|
124
|
-
error!("{}", line);
|
125
|
-
}
|
118
|
+
print_rb_backtrace(rb_err);
|
126
119
|
response.internal_server_error(err.to_string());
|
127
120
|
} else {
|
128
121
|
response.internal_server_error(err.to_string());
|
@@ -77,7 +77,6 @@ impl ItsiResponse {
|
|
77
77
|
(ReceiverStream::new(receiver), shutdown_rx),
|
78
78
|
|(mut receiver, mut shutdown_rx)| async move {
|
79
79
|
if let RunningPhase::ShutdownPending = *shutdown_rx.borrow() {
|
80
|
-
warn!("Disconnecting streaming client.");
|
81
80
|
return None;
|
82
81
|
}
|
83
82
|
loop {
|
@@ -280,7 +279,8 @@ impl ItsiResponse {
|
|
280
279
|
if let Some(writer) = writer.write().as_ref() {
|
281
280
|
writer
|
282
281
|
.blocking_send(Some(frame))
|
283
|
-
.map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)
|
282
|
+
.map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)
|
283
|
+
.ok();
|
284
284
|
}
|
285
285
|
Ok(0)
|
286
286
|
}
|
@@ -101,7 +101,7 @@ impl FromStr for Bind {
|
|
101
101
|
"IPv6 addresses must use [ ] when specifying a port".to_owned(),
|
102
102
|
));
|
103
103
|
} else {
|
104
|
-
(h,
|
104
|
+
(h, p.parse::<u16>().ok()) // Treat as a hostname
|
105
105
|
}
|
106
106
|
} else {
|
107
107
|
(url, None)
|
@@ -110,18 +110,22 @@ impl FromStr for Bind {
|
|
110
110
|
let address = if let Ok(ip) = host.parse::<IpAddr>() {
|
111
111
|
BindAddress::Ip(ip)
|
112
112
|
} else {
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
113
|
+
match protocol {
|
114
|
+
BindProtocol::Https | BindProtocol::Http => resolve_hostname(host)
|
115
|
+
.map(BindAddress::Ip)
|
116
|
+
.ok_or(ItsiError::ArgumentError(format!(
|
117
|
+
"Failed to resolve hostname {}",
|
118
|
+
host
|
119
|
+
)))?,
|
120
|
+
BindProtocol::Unix | BindProtocol::Unixs => BindAddress::UnixSocket(host.into()),
|
121
|
+
}
|
119
122
|
};
|
120
|
-
|
121
|
-
|
122
|
-
BindProtocol::
|
123
|
-
BindProtocol::
|
124
|
-
BindProtocol::
|
123
|
+
|
124
|
+
let port = match protocol {
|
125
|
+
BindProtocol::Http => port.or(Some(80)),
|
126
|
+
BindProtocol::Https => port.or(Some(443)),
|
127
|
+
BindProtocol::Unix => None,
|
128
|
+
BindProtocol::Unixs => None,
|
125
129
|
};
|
126
130
|
|
127
131
|
let tls_config = match protocol {
|
@@ -8,17 +8,17 @@ use super::{
|
|
8
8
|
};
|
9
9
|
use crate::{request::itsi_request::ItsiRequest, server::serve_strategy::ServeStrategy};
|
10
10
|
use derive_more::Debug;
|
11
|
-
use itsi_rb_helpers::{call_without_gvl, HeapVal};
|
11
|
+
use itsi_rb_helpers::{call_without_gvl, HeapVal, HeapValue};
|
12
12
|
use itsi_tracing::{error, run_silently};
|
13
13
|
use magnus::{
|
14
14
|
block::Proc,
|
15
15
|
error::Result,
|
16
16
|
scan_args::{get_kwargs, scan_args, Args, KwArgs, ScanArgsKw, ScanArgsOpt, ScanArgsRequired},
|
17
|
-
value::
|
18
|
-
ArgList, RHash, Ruby, Symbol, Value,
|
17
|
+
value::ReprValue,
|
18
|
+
ArgList, RArray, RHash, Ruby, Symbol, Value,
|
19
19
|
};
|
20
20
|
use parking_lot::{Mutex, RwLock};
|
21
|
-
use std::{cmp::max, ops::Deref, sync::Arc};
|
21
|
+
use std::{cmp::max, collections::HashMap, ops::Deref, sync::Arc};
|
22
22
|
use tracing::{info, instrument};
|
23
23
|
|
24
24
|
static DEFAULT_BIND: &str = "http://localhost:3000";
|
@@ -36,7 +36,6 @@ impl Deref for Server {
|
|
36
36
|
&self.config
|
37
37
|
}
|
38
38
|
}
|
39
|
-
type AfterFork = Mutex<Arc<Option<Box<dyn Fn() + Send + Sync>>>>;
|
40
39
|
|
41
40
|
#[derive(Debug)]
|
42
41
|
pub struct ServerConfig {
|
@@ -51,15 +50,14 @@ pub struct ServerConfig {
|
|
51
50
|
pub script_name: String,
|
52
51
|
pub(crate) binds: Mutex<Vec<Bind>>,
|
53
52
|
#[debug(skip)]
|
54
|
-
pub
|
55
|
-
#[debug(skip)]
|
56
|
-
pub after_fork: AfterFork,
|
53
|
+
pub hooks: HashMap<String, HeapValue<Proc>>,
|
57
54
|
pub scheduler_class: Option<String>,
|
58
55
|
pub stream_body: Option<bool>,
|
59
56
|
pub worker_memory_limit: Option<u64>,
|
60
57
|
#[debug(skip)]
|
61
58
|
pub(crate) strategy: RwLock<Option<ServeStrategy>>,
|
62
59
|
pub silence: bool,
|
60
|
+
pub oob_gc_responses_threshold: Option<u64>,
|
63
61
|
}
|
64
62
|
|
65
63
|
#[derive(Debug)]
|
@@ -68,8 +66,6 @@ pub enum RequestJob {
|
|
68
66
|
Shutdown,
|
69
67
|
}
|
70
68
|
|
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
69
|
fn extract_args<Req, Opt, Splat>(
|
74
70
|
scan_args: &Args<(), (), (), (), RHash, ()>,
|
75
71
|
primaries: &[&str],
|
@@ -80,20 +76,17 @@ where
|
|
80
76
|
Opt: ScanArgsOpt,
|
81
77
|
Splat: ScanArgsKw,
|
82
78
|
{
|
83
|
-
// Combine the primary and rest names into one Vec of Symbols.
|
84
79
|
let symbols: Vec<Symbol> = primaries
|
85
80
|
.iter()
|
86
81
|
.chain(rest.iter())
|
87
82
|
.map(|&name| Symbol::new(name))
|
88
83
|
.collect();
|
89
84
|
|
90
|
-
// Call the "slice" function with the combined symbols.
|
91
85
|
let hash = scan_args
|
92
86
|
.keywords
|
93
87
|
.funcall::<_, _, RHash>("slice", symbols.into_arg_list_with(&Ruby::get().unwrap()))
|
94
88
|
.unwrap();
|
95
89
|
|
96
|
-
// Finally, call get_kwargs with the original name slices.
|
97
90
|
get_kwargs(hash, primaries, rest)
|
98
91
|
}
|
99
92
|
|
@@ -129,14 +122,14 @@ impl Server {
|
|
129
122
|
type Args2 = KwArgs<
|
130
123
|
(),
|
131
124
|
(
|
132
|
-
//
|
133
|
-
Option<
|
134
|
-
// After Fork
|
135
|
-
Option<Proc>,
|
125
|
+
// Hooks
|
126
|
+
Option<RHash>,
|
136
127
|
// Scheduler Class
|
137
128
|
Option<String>,
|
138
129
|
// Worker Memory Limit
|
139
130
|
Option<u64>,
|
131
|
+
// Out-of-band GC Responses Threshold
|
132
|
+
Option<u64>,
|
140
133
|
// Silence
|
141
134
|
Option<bool>,
|
142
135
|
),
|
@@ -160,14 +153,33 @@ impl Server {
|
|
160
153
|
&scan_args,
|
161
154
|
&[],
|
162
155
|
&[
|
163
|
-
"
|
164
|
-
"after_fork",
|
156
|
+
"hooks",
|
165
157
|
"scheduler_class",
|
166
158
|
"worker_memory_limit",
|
159
|
+
"oob_gc_responses_threshold",
|
167
160
|
"silence",
|
168
161
|
],
|
169
162
|
)?;
|
170
163
|
|
164
|
+
let hooks = args2
|
165
|
+
.optional
|
166
|
+
.0
|
167
|
+
.map(|rhash| -> Result<HashMap<String, HeapValue<Proc>>> {
|
168
|
+
let mut hook_map: HashMap<String, HeapValue<Proc>> = HashMap::new();
|
169
|
+
for pair in rhash.enumeratorize::<_, ()>("each", ()) {
|
170
|
+
if let Some(pair_value) = RArray::from_value(pair?) {
|
171
|
+
if let (Ok(key), Ok(value)) =
|
172
|
+
(pair_value.entry::<Value>(0), pair_value.entry::<Proc>(1))
|
173
|
+
{
|
174
|
+
hook_map.insert(key.to_string(), HeapValue::from(value));
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}
|
178
|
+
Ok(hook_map)
|
179
|
+
})
|
180
|
+
.transpose()?
|
181
|
+
.unwrap_or_default();
|
182
|
+
|
171
183
|
let config = ServerConfig {
|
172
184
|
app: HeapVal::from(args1.required.0),
|
173
185
|
workers: max(args1.optional.0.unwrap_or(1), 1),
|
@@ -184,32 +196,16 @@ impl Server {
|
|
184
196
|
.collect::<itsi_error::Result<Vec<Bind>>>()?,
|
185
197
|
),
|
186
198
|
stream_body: args1.optional.5,
|
187
|
-
|
188
|
-
|
189
|
-
|
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),
|
199
|
+
hooks,
|
200
|
+
scheduler_class: args2.optional.1.clone(),
|
201
|
+
worker_memory_limit: args2.optional.2,
|
208
202
|
strategy: RwLock::new(None),
|
203
|
+
oob_gc_responses_threshold: args2.optional.3,
|
204
|
+
silence: args2.optional.4.is_some_and(|s| s),
|
209
205
|
};
|
210
206
|
|
211
207
|
if !config.silence {
|
212
|
-
if let Some(scheduler_class) = args2.optional.
|
208
|
+
if let Some(scheduler_class) = args2.optional.1 {
|
213
209
|
info!(scheduler_class, fiber_scheduler = true);
|
214
210
|
} else {
|
215
211
|
info!(fiber_scheduler = false);
|
@@ -222,7 +218,7 @@ impl Server {
|
|
222
218
|
}
|
223
219
|
|
224
220
|
#[instrument(name = "Bind", skip_all, fields(binds=format!("{:?}", self.config.binds.lock())))]
|
225
|
-
pub(crate) fn build_listeners(&self) -> Result<
|
221
|
+
pub(crate) fn build_listeners(&self) -> Result<Vec<Listener>> {
|
226
222
|
let listeners = self
|
227
223
|
.config
|
228
224
|
.binds
|
@@ -232,13 +228,13 @@ impl Server {
|
|
232
228
|
.map(Listener::try_from)
|
233
229
|
.collect::<std::result::Result<Vec<Listener>, _>>()?
|
234
230
|
.into_iter()
|
235
|
-
.map(Arc::new)
|
236
231
|
.collect::<Vec<_>>();
|
237
232
|
info!("Bound {:?} listeners", listeners.len());
|
238
|
-
Ok(
|
233
|
+
Ok(listeners)
|
239
234
|
}
|
240
235
|
|
241
|
-
pub(crate) fn build_strategy(self
|
236
|
+
pub(crate) fn build_strategy(self) -> Result<()> {
|
237
|
+
let listeners = self.build_listeners()?;
|
242
238
|
let server = Arc::new(self);
|
243
239
|
let server_clone = server.clone();
|
244
240
|
|
@@ -276,11 +272,9 @@ impl Server {
|
|
276
272
|
fn build_and_run_strategy(&self) -> Result<()> {
|
277
273
|
reset_signal_handlers();
|
278
274
|
let rself = self.clone();
|
279
|
-
let listeners = self.build_listeners()?;
|
280
|
-
let listeners_clone = listeners.clone();
|
281
275
|
call_without_gvl(move || -> Result<()> {
|
282
|
-
rself.clone().build_strategy(
|
283
|
-
if let Err(e) = rself.
|
276
|
+
rself.clone().build_strategy()?;
|
277
|
+
if let Err(e) = rself.strategy.read().as_ref().unwrap().run() {
|
284
278
|
error!("Error running server: {}", e);
|
285
279
|
rself.strategy.read().as_ref().unwrap().stop()?;
|
286
280
|
}
|
@@ -243,20 +243,20 @@ impl std::fmt::Display for SockAddr {
|
|
243
243
|
}
|
244
244
|
|
245
245
|
impl Listener {
|
246
|
-
pub fn
|
246
|
+
pub fn into_tokio_listener(self) -> TokioListener {
|
247
247
|
match self {
|
248
|
-
Listener::Tcp(listener) =>
|
249
|
-
TokioTcpListener::from_std(
|
250
|
-
|
248
|
+
Listener::Tcp(listener) => {
|
249
|
+
TokioListener::Tcp(TokioTcpListener::from_std(listener).unwrap())
|
250
|
+
}
|
251
251
|
Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls(
|
252
|
-
TokioTcpListener::from_std(
|
252
|
+
TokioTcpListener::from_std(listener).unwrap(),
|
253
253
|
acceptor.clone(),
|
254
254
|
),
|
255
|
-
Listener::Unix(listener) =>
|
256
|
-
TokioUnixListener::from_std(
|
257
|
-
|
255
|
+
Listener::Unix(listener) => {
|
256
|
+
TokioListener::Unix(TokioUnixListener::from_std(listener).unwrap())
|
257
|
+
}
|
258
258
|
Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls(
|
259
|
-
TokioUnixListener::from_std(
|
259
|
+
TokioUnixListener::from_std(listener).unwrap(),
|
260
260
|
acceptor.clone(),
|
261
261
|
),
|
262
262
|
}
|
@@ -53,8 +53,8 @@ impl ProcessWorker {
|
|
53
53
|
}
|
54
54
|
*self.child_pid.lock() = None;
|
55
55
|
}
|
56
|
-
|
57
|
-
|
56
|
+
match call_with_gvl(|_ruby| fork(cluster_template.server.hooks.get("after_fork").cloned()))
|
57
|
+
{
|
58
58
|
Some(pid) => {
|
59
59
|
*self.child_pid.lock() = Some(Pid::from_raw(pid));
|
60
60
|
}
|
@@ -67,7 +67,7 @@ impl ProcessWorker {
|
|
67
67
|
}
|
68
68
|
match SingleMode::new(
|
69
69
|
cluster_template.server.clone(),
|
70
|
-
cluster_template.listeners.
|
70
|
+
cluster_template.listeners.lock().drain(..).collect(),
|
71
71
|
cluster_template.lifecycle_channel.clone(),
|
72
72
|
) {
|
73
73
|
Ok(single_mode) => {
|
@@ -83,6 +83,13 @@ impl ProcessWorker {
|
|
83
83
|
Ok(())
|
84
84
|
}
|
85
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
|
+
|
86
93
|
pub(crate) fn memory_usage(&self) -> Option<u64> {
|
87
94
|
if let Some(pid) = *self.child_pid.lock() {
|
88
95
|
let s = System::new_all();
|