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
data/gems/scheduler/Cargo.lock
CHANGED
@@ -68,9 +68,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
|
68
68
|
|
69
69
|
[[package]]
|
70
70
|
name = "cc"
|
71
|
-
version = "1.2.
|
71
|
+
version = "1.2.15"
|
72
72
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
73
|
-
checksum = "
|
73
|
+
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
74
74
|
dependencies = [
|
75
75
|
"shlex",
|
76
76
|
]
|
@@ -186,9 +186,9 @@ dependencies = [
|
|
186
186
|
|
187
187
|
[[package]]
|
188
188
|
name = "itoa"
|
189
|
-
version = "1.0.
|
189
|
+
version = "1.0.14"
|
190
190
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
191
|
-
checksum = "
|
191
|
+
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
192
192
|
|
193
193
|
[[package]]
|
194
194
|
name = "itsi-scheduler"
|
@@ -507,9 +507,9 @@ dependencies = [
|
|
507
507
|
|
508
508
|
[[package]]
|
509
509
|
name = "redox_syscall"
|
510
|
-
version = "0.5.
|
510
|
+
version = "0.5.9"
|
511
511
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
512
|
-
checksum = "
|
512
|
+
checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
|
513
513
|
dependencies = [
|
514
514
|
"bitflags",
|
515
515
|
]
|
@@ -560,9 +560,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|
560
560
|
|
561
561
|
[[package]]
|
562
562
|
name = "ring"
|
563
|
-
version = "0.17.
|
563
|
+
version = "0.17.14"
|
564
564
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
565
|
-
checksum = "
|
565
|
+
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
566
566
|
dependencies = [
|
567
567
|
"cc",
|
568
568
|
"cfg-if",
|
@@ -586,9 +586,9 @@ checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
|
|
586
586
|
|
587
587
|
[[package]]
|
588
588
|
name = "ryu"
|
589
|
-
version = "1.0.
|
589
|
+
version = "1.0.19"
|
590
590
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
591
|
-
checksum = "
|
591
|
+
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
592
592
|
|
593
593
|
[[package]]
|
594
594
|
name = "scopeguard"
|
@@ -624,9 +624,9 @@ dependencies = [
|
|
624
624
|
|
625
625
|
[[package]]
|
626
626
|
name = "serde_json"
|
627
|
-
version = "1.0.
|
627
|
+
version = "1.0.139"
|
628
628
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
629
|
-
checksum = "
|
629
|
+
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
630
630
|
dependencies = [
|
631
631
|
"itoa",
|
632
632
|
"memchr",
|
@@ -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 {
|