itsi 0.1.8 → 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 +6 -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 +74 -1
- data/crates/itsi_server/src/request/itsi_request.rs +32 -11
- data/crates/itsi_server/src/response/itsi_response.rs +14 -4
- data/crates/itsi_server/src/server/bind.rs +16 -12
- data/crates/itsi_server/src/server/itsi_server.rs +146 -95
- data/crates/itsi_server/src/server/listener.rs +10 -10
- 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 +134 -115
- data/crates/itsi_server/src/server/signal.rs +4 -0
- data/crates/itsi_server/src/server/thread_worker.rs +55 -24
- data/crates/itsi_server/src/server/tls.rs +11 -8
- data/crates/itsi_tracing/src/lib.rs +18 -1
- data/gems/scheduler/Cargo.lock +12 -12
- 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 +74 -1
- data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +32 -11
- data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +14 -4
- data/gems/scheduler/ext/itsi_server/src/server/bind.rs +16 -12
- data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +146 -95
- data/gems/scheduler/ext/itsi_server/src/server/listener.rs +10 -10
- 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 +134 -115
- data/gems/scheduler/ext/itsi_server/src/server/signal.rs +4 -0
- data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +55 -24
- data/gems/scheduler/ext/itsi_server/src/server/tls.rs +11 -8
- data/gems/scheduler/ext/itsi_tracing/src/lib.rs +18 -1
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/test/test_address_resolve.rb +0 -1
- data/gems/scheduler/test/test_file_io.rb +0 -1
- data/gems/scheduler/test/test_kernel_sleep.rb +3 -4
- data/gems/server/Cargo.lock +11 -2
- data/gems/server/Rakefile +8 -1
- 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 +74 -1
- data/gems/server/ext/itsi_server/src/request/itsi_request.rs +32 -11
- data/gems/server/ext/itsi_server/src/response/itsi_response.rs +14 -4
- data/gems/server/ext/itsi_server/src/server/bind.rs +16 -12
- data/gems/server/ext/itsi_server/src/server/itsi_server.rs +146 -95
- data/gems/server/ext/itsi_server/src/server/listener.rs +10 -10
- 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 +134 -115
- data/gems/server/ext/itsi_server/src/server/signal.rs +4 -0
- data/gems/server/ext/itsi_server/src/server/thread_worker.rs +55 -24
- data/gems/server/ext/itsi_server/src/server/tls.rs +11 -8
- data/gems/server/ext/itsi_tracing/src/lib.rs +18 -1
- data/gems/server/lib/itsi/request.rb +29 -21
- 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 -7
- data/gems/server/lib/itsi/server/rack_interface.rb +75 -0
- data/gems/server/lib/itsi/server/scheduler_interface.rb +21 -0
- data/gems/server/lib/itsi/server/signal_trap.rb +23 -0
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +71 -101
- data/gems/server/test/helpers/test_helper.rb +30 -0
- data/gems/server/test/test_itsi_server.rb +294 -3
- data/lib/itsi/version.rb +1 -1
- data/location_dsl.rb +381 -0
- data/sandbox/deploy/main.tf +1 -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 +25 -8
- metadata +21 -14
- data/gems/server/lib/itsi/signals.rb +0 -23
- data/gems/server/test/test_helper.rb +0 -7
- /data/gems/server/lib/itsi/{index.html.erb → index.html} +0 -0
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
@@ -58,6 +58,7 @@ task :sync_crates do
|
|
58
58
|
Dir.chdir('crates') do
|
59
59
|
to_sync = Dir['*'].each do |to_sync|
|
60
60
|
system("rsync -q -av #{to_sync}/ ../#{gem_info[:dir]}/ext/#{to_sync} --delete")
|
61
|
+
system("cp ../Cargo.lock ../#{gem_info[:dir]}/Cargo.lock")
|
61
62
|
end
|
62
63
|
end
|
63
64
|
end
|
@@ -82,13 +83,16 @@ end
|
|
82
83
|
task :test_env_down do
|
83
84
|
system("terraform -chdir=sandbox/deploy destroy")
|
84
85
|
end
|
85
|
-
|
86
86
|
%i[itsi puma iodine falcon unicorn].each do |server|
|
87
87
|
namespace server do
|
88
88
|
%i[hanami roda async rack rack_lint rails sinatra].each do |sandbox|
|
89
89
|
namespace sandbox do
|
90
90
|
task :serve do |args|
|
91
|
-
|
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
|
92
96
|
end
|
93
97
|
end
|
94
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))?;
|
@@ -71,6 +139,7 @@ fn init(ruby: &Ruby) -> Result<()> {
|
|
71
139
|
server.define_singleton_method("new", function!(Server::new, -1))?;
|
72
140
|
server.define_singleton_method("reset_signal_handlers", function!(reset_signal_handlers, 0))?;
|
73
141
|
server.define_method("start", method!(Server::start, 0))?;
|
142
|
+
server.define_method("stop", method!(Server::stop, 0))?;
|
74
143
|
|
75
144
|
let request = ruby.get_inner(&ITSI_REQUEST);
|
76
145
|
request.define_method("path", method!(ItsiRequest::path, 0))?;
|
@@ -86,6 +155,8 @@ fn init(ruby: &Ruby) -> Result<()> {
|
|
86
155
|
request.define_method("port", method!(ItsiRequest::port, 0))?;
|
87
156
|
request.define_method("body", method!(ItsiRequest::body, 0))?;
|
88
157
|
request.define_method("response", method!(ItsiRequest::response, 0))?;
|
158
|
+
request.define_method("json?", method!(ItsiRequest::is_json, 0))?;
|
159
|
+
request.define_method("html?", method!(ItsiRequest::is_html, 0))?;
|
89
160
|
|
90
161
|
let body_proxy = ruby.get_inner(&ITSI_BODY_PROXY);
|
91
162
|
body_proxy.define_method("gets", method!(ItsiBodyProxy::gets, 0))?;
|
@@ -102,6 +173,8 @@ fn init(ruby: &Ruby) -> Result<()> {
|
|
102
173
|
response.define_method("close_read", method!(ItsiResponse::close_read, 0))?;
|
103
174
|
response.define_method("close", method!(ItsiResponse::close, 0))?;
|
104
175
|
response.define_method("hijack", method!(ItsiResponse::hijack, 1))?;
|
176
|
+
response.define_method("json?", method!(ItsiResponse::is_json, 0))?;
|
177
|
+
response.define_method("html?", method!(ItsiResponse::is_html, 0))?;
|
105
178
|
|
106
179
|
Ok(())
|
107
180
|
}
|
@@ -13,10 +13,11 @@ use crate::{
|
|
13
13
|
use bytes::Bytes;
|
14
14
|
use derive_more::Debug;
|
15
15
|
use futures::StreamExt;
|
16
|
-
use http::{request::Parts, Response, StatusCode};
|
16
|
+
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)]
|
@@ -49,6 +49,7 @@ pub struct ItsiRequest {
|
|
49
49
|
pub server: Arc<Server>,
|
50
50
|
pub response: ItsiResponse,
|
51
51
|
pub start: Instant,
|
52
|
+
pub content_type: String,
|
52
53
|
}
|
53
54
|
|
54
55
|
impl fmt::Display for ItsiRequest {
|
@@ -82,6 +83,14 @@ impl ItsiRequest {
|
|
82
83
|
}
|
83
84
|
}
|
84
85
|
|
86
|
+
pub fn is_json(&self) -> bool {
|
87
|
+
self.content_type.eq("application/json")
|
88
|
+
}
|
89
|
+
|
90
|
+
pub fn is_html(&self) -> bool {
|
91
|
+
self.content_type.eq("text/html")
|
92
|
+
}
|
93
|
+
|
85
94
|
pub fn process(
|
86
95
|
self,
|
87
96
|
ruby: &Ruby,
|
@@ -106,14 +115,7 @@ impl ItsiRequest {
|
|
106
115
|
debug!("Connection closed by client");
|
107
116
|
response.close();
|
108
117
|
} else if let Some(rb_err) = err.value() {
|
109
|
-
|
110
|
-
.funcall::<_, _, Vec<String>>(*ID_BACKTRACE, ())
|
111
|
-
.unwrap_or_default();
|
112
|
-
|
113
|
-
error!("Error occurred in Handler: {:?}", rb_err);
|
114
|
-
for line in backtrace {
|
115
|
-
error!("{}", line);
|
116
|
-
}
|
118
|
+
print_rb_backtrace(rb_err);
|
117
119
|
response.internal_server_error(err.to_string());
|
118
120
|
} else {
|
119
121
|
response.internal_server_error(err.to_string());
|
@@ -175,8 +177,27 @@ impl ItsiRequest {
|
|
175
177
|
server,
|
176
178
|
listener,
|
177
179
|
version: format!("{:?}", &parts.version),
|
178
|
-
response: ItsiResponse::new(
|
180
|
+
response: ItsiResponse::new(
|
181
|
+
parts.clone(),
|
182
|
+
response_channel.0,
|
183
|
+
parts
|
184
|
+
.headers
|
185
|
+
.get("Accept")
|
186
|
+
.unwrap_or(&HeaderValue::from_static("text/html"))
|
187
|
+
.to_str()
|
188
|
+
.unwrap()
|
189
|
+
.to_string(),
|
190
|
+
),
|
179
191
|
start: Instant::now(),
|
192
|
+
content_type: parts
|
193
|
+
.headers
|
194
|
+
.get("Content-Type")
|
195
|
+
.unwrap_or(&HeaderValue::from_static(
|
196
|
+
"application/x-www-form-urlencoded",
|
197
|
+
))
|
198
|
+
.to_str()
|
199
|
+
.unwrap()
|
200
|
+
.to_string(),
|
180
201
|
parts,
|
181
202
|
},
|
182
203
|
response_channel.1,
|
@@ -37,6 +37,7 @@ use crate::server::serve_strategy::single_mode::RunningPhase;
|
|
37
37
|
#[derive(Debug, Clone)]
|
38
38
|
pub struct ItsiResponse {
|
39
39
|
pub data: Arc<ResponseData>,
|
40
|
+
pub accept: String,
|
40
41
|
}
|
41
42
|
|
42
43
|
#[derive(Debug)]
|
@@ -76,7 +77,6 @@ impl ItsiResponse {
|
|
76
77
|
(ReceiverStream::new(receiver), shutdown_rx),
|
77
78
|
|(mut receiver, mut shutdown_rx)| async move {
|
78
79
|
if let RunningPhase::ShutdownPending = *shutdown_rx.borrow() {
|
79
|
-
warn!("Disconnecting streaming client.");
|
80
80
|
return None;
|
81
81
|
}
|
82
82
|
loop {
|
@@ -279,7 +279,8 @@ impl ItsiResponse {
|
|
279
279
|
if let Some(writer) = writer.write().as_ref() {
|
280
280
|
writer
|
281
281
|
.blocking_send(Some(frame))
|
282
|
-
.map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)
|
282
|
+
.map_err(|_| itsi_error::ItsiError::ClientConnectionClosed)
|
283
|
+
.ok();
|
283
284
|
}
|
284
285
|
Ok(0)
|
285
286
|
}
|
@@ -293,11 +294,19 @@ impl ItsiResponse {
|
|
293
294
|
Ok(true)
|
294
295
|
}
|
295
296
|
|
297
|
+
pub fn is_html(&self) -> bool {
|
298
|
+
self.accept.starts_with("text/html")
|
299
|
+
}
|
300
|
+
|
301
|
+
pub fn is_json(&self) -> bool {
|
302
|
+
self.accept.starts_with("application/json")
|
303
|
+
}
|
304
|
+
|
296
305
|
pub fn close_read(&self) -> MagnusResult<bool> {
|
297
|
-
|
306
|
+
Ok(true)
|
298
307
|
}
|
299
308
|
|
300
|
-
pub fn new(parts: Parts, response_writer: mpsc::Sender<Option<Bytes
|
309
|
+
pub fn new(parts: Parts, response_writer: mpsc::Sender<Option<Bytes>>, accept: String) -> Self {
|
301
310
|
Self {
|
302
311
|
data: Arc::new(ResponseData {
|
303
312
|
response: RwLock::new(Some(Response::new(BoxBody::new(Empty::new())))),
|
@@ -306,6 +315,7 @@ impl ItsiResponse {
|
|
306
315
|
hijacked_socket: RwLock::new(None),
|
307
316
|
parts,
|
308
317
|
}),
|
318
|
+
accept,
|
309
319
|
}
|
310
320
|
}
|
311
321
|
|
@@ -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 {
|