itsi-scheduler 0.1.5 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of itsi-scheduler might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +7 -0
- data/Cargo.lock +83 -22
- data/README.md +5 -0
- data/_index.md +7 -0
- data/ext/itsi_error/src/from.rs +26 -29
- data/ext/itsi_error/src/lib.rs +10 -1
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
- data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
- data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
- data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
- data/ext/itsi_rb_helpers/Cargo.toml +1 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
- data/ext/itsi_rb_helpers/src/lib.rs +59 -9
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
- data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
- 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
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
- data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +69 -26
- data/ext/itsi_server/src/env.rs +43 -0
- data/ext/itsi_server/src/lib.rs +81 -75
- data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
- data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +22 -3
- data/ext/itsi_server/src/ruby_types/itsi_grpc_request.rs +147 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_response.rs +19 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_stream/mod.rs +216 -0
- data/ext/itsi_server/src/{request/itsi_request.rs → ruby_types/itsi_http_request.rs} +108 -103
- data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +79 -38
- data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +355 -0
- data/ext/itsi_server/src/ruby_types/itsi_server.rs +82 -0
- data/ext/itsi_server/src/ruby_types/mod.rs +55 -0
- data/ext/itsi_server/src/server/bind.rs +33 -20
- data/ext/itsi_server/src/server/byte_frame.rs +32 -0
- data/ext/itsi_server/src/server/cache_store.rs +74 -0
- data/ext/itsi_server/src/server/itsi_service.rs +172 -0
- data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
- data/ext/itsi_server/src/server/listener.rs +197 -106
- data/ext/itsi_server/src/server/middleware_stack/middleware.rs +153 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +47 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +58 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +264 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +139 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +300 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +287 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +48 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +127 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +191 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +72 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +85 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +195 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +216 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +124 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +43 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +34 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +93 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +162 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +158 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
- data/ext/itsi_server/src/server/middleware_stack/mod.rs +315 -0
- data/ext/itsi_server/src/server/mod.rs +8 -1
- data/ext/itsi_server/src/server/process_worker.rs +44 -11
- data/ext/itsi_server/src/server/rate_limiter.rs +565 -0
- data/ext/itsi_server/src/server/request_job.rs +11 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +129 -46
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +337 -163
- data/ext/itsi_server/src/server/signal.rs +25 -2
- data/ext/itsi_server/src/server/static_file_server.rs +984 -0
- data/ext/itsi_server/src/server/thread_worker.rs +164 -88
- data/ext/itsi_server/src/server/tls/locked_dir_cache.rs +55 -17
- data/ext/itsi_server/src/server/tls.rs +104 -28
- data/ext/itsi_server/src/server/types.rs +43 -0
- data/ext/itsi_tracing/Cargo.toml +1 -0
- data/ext/itsi_tracing/src/lib.rs +222 -34
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
- data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
- data/lib/itsi/scheduler/version.rb +1 -1
- data/lib/itsi/scheduler.rb +2 -2
- metadata +79 -14
- data/ext/itsi_server/extconf.rb +0 -6
- data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
- data/ext/itsi_server/src/request/mod.rs +0 -1
- data/ext/itsi_server/src/response/mod.rs +0 -1
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -13
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -5
- data/ext/itsi_server/src/server/itsi_server.rs +0 -244
data/ext/itsi_server/Cargo.toml
CHANGED
@@ -10,37 +10,80 @@ publish = false
|
|
10
10
|
crate-type = ["cdylib"]
|
11
11
|
|
12
12
|
[dependencies]
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
rcgen = { version = "0.13.2", features = ["x509-parser", "pem"] }
|
13
|
+
async-compression = { version = "0.4.21", features = [
|
14
|
+
"tokio",
|
15
|
+
"zstd",
|
16
|
+
"brotli",
|
17
|
+
"deflate",
|
18
|
+
"gzip",
|
19
|
+
] }
|
20
|
+
async-channel = "2.3.1"
|
21
|
+
async-trait = "0.1.87"
|
23
22
|
base64 = "0.22.1"
|
23
|
+
bytes = "1.3"
|
24
|
+
chrono = "0.4.35"
|
25
|
+
crossbeam = "0.8.4"
|
26
|
+
dashmap = "6.1.0"
|
27
|
+
derive_more = { version = "2.0.1", features = ["debug"] }
|
28
|
+
dirs = "6.0.0"
|
29
|
+
either = "1.15.0"
|
30
|
+
fnv = "1.0.7"
|
31
|
+
fs2 = "0.4.3"
|
32
|
+
futures = "0.3.31"
|
33
|
+
globset = "0.4.16"
|
34
|
+
hmac = "0.12.1"
|
35
|
+
http = "1.3.1"
|
24
36
|
http-body-util = "0.1.2"
|
37
|
+
httpdate = "1.0.3"
|
38
|
+
httparse = "1.10.1"
|
25
39
|
hyper = { version = "1.5.0", features = ["full", "server", "http1", "http2"] }
|
26
|
-
|
40
|
+
hyper-staticfile = "0.10.1"
|
27
41
|
hyper-util = { version = "0.1.10", features = ["full"] }
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
42
|
+
itsi_error = { path = "../itsi_error" }
|
43
|
+
itsi_rb_helpers = { path = "../itsi_rb_helpers" }
|
44
|
+
itsi_tracing = { path = "../itsi_tracing" }
|
45
|
+
jsonwebtoken = "9.3.1"
|
46
|
+
magnus = { version = "0.7.1", features = ["bytes", "rb-sys"] }
|
47
|
+
moka = { version = "0.12.10", features = ["sync"] }
|
48
|
+
notify = { version = "8.0.0" }
|
49
|
+
nix = { version = "0.29.0", features = [
|
50
|
+
"socket",
|
51
|
+
"uio",
|
52
|
+
"signal",
|
53
|
+
"fs",
|
54
|
+
"process",
|
55
|
+
] }
|
56
|
+
num_cpus = "1.16.0"
|
57
|
+
parking_lot = "0.12.3"
|
33
58
|
pin-project = "1.1.9"
|
59
|
+
rand = "0.9.0"
|
34
60
|
rb-sys = "0.9.111"
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
61
|
+
rcgen = { version = "0.13.2", features = ["x509-parser", "pem"] }
|
62
|
+
regex = "1.11.1"
|
63
|
+
reqwest = { version = "0.12.15", features = ["stream"] }
|
64
|
+
ring = "0.17.14"
|
65
|
+
route-recognizer = "0.3.1"
|
66
|
+
redis = { version = "0.29.2", features = [
|
67
|
+
"tokio-comp",
|
68
|
+
"r2d2",
|
69
|
+
"tokio-rustls-comp",
|
70
|
+
"connection-manager",
|
71
|
+
] }
|
72
|
+
rustls = "0.23.23"
|
73
|
+
rustls-pemfile = "2.2.0"
|
74
|
+
serde = "1.0.219"
|
75
|
+
serde_json = "1.0.140"
|
76
|
+
serde_magnus = "0.9.0"
|
77
|
+
sha2 = "0.10.8"
|
78
|
+
socket2 = "0.5.8"
|
41
79
|
sysinfo = "0.33.1"
|
80
|
+
tempfile = "3.18.0"
|
81
|
+
tokio = { version = "1.44.1", features = ["full"] }
|
82
|
+
tokio-rustls = "0.26.2"
|
42
83
|
tokio-rustls-acme = "0.6.0"
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
84
|
+
tokio-stream = "0.1.17"
|
85
|
+
tokio-util = "0.7.13"
|
86
|
+
tracing = "0.1.41"
|
87
|
+
url = "2.5.4"
|
88
|
+
uuid = "1.16.0"
|
89
|
+
md5 = "0.7.0"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
use std::{
|
2
|
+
env::{var, VarError},
|
3
|
+
path::PathBuf,
|
4
|
+
sync::LazyLock,
|
5
|
+
};
|
6
|
+
|
7
|
+
type StringVar = LazyLock<String>;
|
8
|
+
type MaybeStringVar = LazyLock<Result<String, VarError>>;
|
9
|
+
type PathVar = LazyLock<PathBuf>;
|
10
|
+
|
11
|
+
/// ACME Configuration for auto-generating production certificates
|
12
|
+
/// *ITSI_ACME_CACHE_DIR* - Directory to store cached certificates
|
13
|
+
/// so that these are not regenerated every time the server starts
|
14
|
+
pub static ITSI_ACME_CACHE_DIR: StringVar = LazyLock::new(|| {
|
15
|
+
var("ITSI_ACME_CACHE_DIR").unwrap_or_else(|_| "./.rustls_acme_cache".to_string())
|
16
|
+
});
|
17
|
+
|
18
|
+
/// *ITSI_ACME_CONTACT_EMAIL* - Contact Email address to provide to ACME server during certificate renewal
|
19
|
+
pub static ITSI_ACME_CONTACT_EMAIL: MaybeStringVar =
|
20
|
+
LazyLock::new(|| var("ITSI_ACME_CONTACT_EMAIL"));
|
21
|
+
|
22
|
+
/// *ITSI_ACME_CA_PEM_PATH* - Optional CA Pem path, used for testing with non-trusted CAs for certifcate generation.
|
23
|
+
pub static ITSI_ACME_CA_PEM_PATH: MaybeStringVar = LazyLock::new(|| var("ITSI_ACME_CA_PEM_PATH"));
|
24
|
+
|
25
|
+
/// *ITSI_ACME_DIRECTORY_URL* - Directory URL to use for ACME certificate generation.
|
26
|
+
pub static ITSI_ACME_DIRECTORY_URL: StringVar = LazyLock::new(|| {
|
27
|
+
var("ITSI_ACME_DIRECTORY_URL")
|
28
|
+
.unwrap_or_else(|_| "https://acme-v02.api.letsencrypt.org/directory".to_string())
|
29
|
+
});
|
30
|
+
|
31
|
+
/// *ITSI_ACME_LOCK_FILE_NAME* - Name of the lock file used to prevent concurrent certificate generation.
|
32
|
+
pub static ITSI_ACME_LOCK_FILE_NAME: StringVar =
|
33
|
+
LazyLock::new(|| var("ITSI_ACME_LOCK_FILE_NAME").unwrap_or(".acme.lock".to_string()));
|
34
|
+
|
35
|
+
pub static ITSI_LOCAL_CA_DIR: PathVar = LazyLock::new(|| {
|
36
|
+
var("ITSI_LOCAL_CA_DIR")
|
37
|
+
.map(PathBuf::from)
|
38
|
+
.unwrap_or_else(|_| {
|
39
|
+
dirs::home_dir()
|
40
|
+
.expect("Failed to find HOME directory when initializing ITSI_LOCAL_CA_DIR")
|
41
|
+
.join(".itsi")
|
42
|
+
})
|
43
|
+
});
|
data/ext/itsi_server/src/lib.rs
CHANGED
@@ -1,58 +1,17 @@
|
|
1
|
-
use
|
2
|
-
use
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
use magnus::{error::Result, function, method, Module, Object, Ruby};
|
2
|
+
use ruby_types::{
|
3
|
+
itsi_body_proxy::ItsiBodyProxy, itsi_grpc_request::ItsiGrpcRequest,
|
4
|
+
itsi_grpc_stream::ItsiGrpcStream, itsi_http_request::ItsiHttpRequest,
|
5
|
+
itsi_http_response::ItsiHttpResponse, itsi_server::ItsiServer, ITSI_BODY_PROXY,
|
6
|
+
ITSI_GRPC_REQUEST, ITSI_GRPC_RESPONSE, ITSI_GRPC_STREAM, ITSI_MODULE, ITSI_REQUEST,
|
7
|
+
ITSI_RESPONSE, ITSI_SERVER,
|
8
|
+
};
|
9
|
+
use server::signal::reset_signal_handlers;
|
6
10
|
use tracing::*;
|
7
|
-
|
8
|
-
pub mod
|
9
|
-
pub mod request;
|
10
|
-
pub mod response;
|
11
|
+
pub mod env;
|
12
|
+
pub mod ruby_types;
|
11
13
|
pub mod server;
|
12
14
|
|
13
|
-
pub static ITSI_MODULE: Lazy<RModule> = Lazy::new(|ruby| ruby.define_module("Itsi").unwrap());
|
14
|
-
pub static ITSI_SERVER: Lazy<RClass> = Lazy::new(|ruby| {
|
15
|
-
ruby.get_inner(&ITSI_MODULE)
|
16
|
-
.define_class("Server", ruby.class_object())
|
17
|
-
.unwrap()
|
18
|
-
});
|
19
|
-
pub static ITSI_REQUEST: Lazy<RClass> = Lazy::new(|ruby| {
|
20
|
-
ruby.get_inner(&ITSI_MODULE)
|
21
|
-
.define_class("Request", ruby.class_object())
|
22
|
-
.unwrap()
|
23
|
-
});
|
24
|
-
|
25
|
-
pub static ITSI_RESPONSE: Lazy<RClass> = Lazy::new(|ruby| {
|
26
|
-
ruby.get_inner(&ITSI_MODULE)
|
27
|
-
.define_class("Response", ruby.class_object())
|
28
|
-
.unwrap()
|
29
|
-
});
|
30
|
-
|
31
|
-
pub static ITSI_BODY_PROXY: Lazy<RClass> = Lazy::new(|ruby| {
|
32
|
-
ruby.get_inner(&ITSI_MODULE)
|
33
|
-
.define_class("BodyProxy", ruby.class_object())
|
34
|
-
.unwrap()
|
35
|
-
});
|
36
|
-
|
37
|
-
pub static ITSI_SERVER_SCHEDULER_TASK: Lazy<RClass> = Lazy::new(|ruby| {
|
38
|
-
ruby.get_inner(&ITSI_MODULE)
|
39
|
-
.define_class("ServerSchedulerTask", ruby.class_object())
|
40
|
-
.unwrap()
|
41
|
-
});
|
42
|
-
|
43
|
-
pub fn log_debug(msg: String) {
|
44
|
-
debug!(msg);
|
45
|
-
}
|
46
|
-
pub fn log_info(msg: String) {
|
47
|
-
info!(msg);
|
48
|
-
}
|
49
|
-
pub fn log_warn(msg: String) {
|
50
|
-
warn!(msg);
|
51
|
-
}
|
52
|
-
pub fn log_error(msg: String) {
|
53
|
-
error!(msg);
|
54
|
-
}
|
55
|
-
|
56
15
|
#[magnus::init]
|
57
16
|
fn init(ruby: &Ruby) -> Result<()> {
|
58
17
|
itsi_tracing::init();
|
@@ -67,24 +26,29 @@ fn init(ruby: &Ruby) -> Result<()> {
|
|
67
26
|
itsi.define_singleton_method("log_error", function!(log_error, 1))?;
|
68
27
|
|
69
28
|
let server = ruby.get_inner(&ITSI_SERVER);
|
70
|
-
server.define_singleton_method("new", function!(
|
29
|
+
server.define_singleton_method("new", function!(ItsiServer::new, 3))?;
|
71
30
|
server.define_singleton_method("reset_signal_handlers", function!(reset_signal_handlers, 0))?;
|
72
|
-
server.define_method("start", method!(
|
31
|
+
server.define_method("start", method!(ItsiServer::start, 0))?;
|
32
|
+
server.define_method("stop", method!(ItsiServer::stop, 0))?;
|
73
33
|
|
74
34
|
let request = ruby.get_inner(&ITSI_REQUEST);
|
75
|
-
request.define_method("path", method!(
|
76
|
-
request.define_method("script_name", method!(
|
77
|
-
request.define_method("query_string", method!(
|
78
|
-
request.define_method("method", method!(
|
79
|
-
request.define_method("version", method!(
|
80
|
-
request.define_method("rack_protocol", method!(
|
81
|
-
request.define_method("host", method!(
|
82
|
-
request.define_method("headers", method!(
|
83
|
-
request.define_method("
|
84
|
-
request.define_method("
|
85
|
-
request.define_method("
|
86
|
-
request.define_method("
|
87
|
-
request.define_method("
|
35
|
+
request.define_method("path", method!(ItsiHttpRequest::path, 0))?;
|
36
|
+
request.define_method("script_name", method!(ItsiHttpRequest::script_name, 0))?;
|
37
|
+
request.define_method("query_string", method!(ItsiHttpRequest::query_string, 0))?;
|
38
|
+
request.define_method("method", method!(ItsiHttpRequest::method, 0))?;
|
39
|
+
request.define_method("version", method!(ItsiHttpRequest::version, 0))?;
|
40
|
+
request.define_method("rack_protocol", method!(ItsiHttpRequest::rack_protocol, 0))?;
|
41
|
+
request.define_method("host", method!(ItsiHttpRequest::host, 0))?;
|
42
|
+
request.define_method("headers", method!(ItsiHttpRequest::headers, 0))?;
|
43
|
+
request.define_method("header", method!(ItsiHttpRequest::header, 1))?;
|
44
|
+
request.define_method("[]", method!(ItsiHttpRequest::header, 1))?;
|
45
|
+
request.define_method("scheme", method!(ItsiHttpRequest::scheme, 0))?;
|
46
|
+
request.define_method("remote_addr", method!(ItsiHttpRequest::remote_addr, 0))?;
|
47
|
+
request.define_method("port", method!(ItsiHttpRequest::port, 0))?;
|
48
|
+
request.define_method("body", method!(ItsiHttpRequest::body, 0))?;
|
49
|
+
request.define_method("response", method!(ItsiHttpRequest::response, 0))?;
|
50
|
+
request.define_method("json?", method!(ItsiHttpRequest::is_json, 0))?;
|
51
|
+
request.define_method("html?", method!(ItsiHttpRequest::is_html, 0))?;
|
88
52
|
|
89
53
|
let body_proxy = ruby.get_inner(&ITSI_BODY_PROXY);
|
90
54
|
body_proxy.define_method("gets", method!(ItsiBodyProxy::gets, 0))?;
|
@@ -93,14 +57,56 @@ fn init(ruby: &Ruby) -> Result<()> {
|
|
93
57
|
body_proxy.define_method("close", method!(ItsiBodyProxy::close, 0))?;
|
94
58
|
|
95
59
|
let response = ruby.get_inner(&ITSI_RESPONSE);
|
96
|
-
response.define_method("
|
97
|
-
response.define_method("
|
98
|
-
response.define_method("
|
99
|
-
response.define_method("
|
100
|
-
response.define_method("
|
101
|
-
response.define_method("
|
102
|
-
response.define_method("
|
103
|
-
response.define_method("
|
60
|
+
response.define_method("[]=", method!(ItsiHttpResponse::add_header, 2))?;
|
61
|
+
response.define_method("add_header", method!(ItsiHttpResponse::add_header, 2))?;
|
62
|
+
response.define_method("add_headers", method!(ItsiHttpResponse::add_headers, 1))?;
|
63
|
+
response.define_method("status=", method!(ItsiHttpResponse::set_status, 1))?;
|
64
|
+
response.define_method("send_frame", method!(ItsiHttpResponse::send_frame, 1))?;
|
65
|
+
response.define_method("<<", method!(ItsiHttpResponse::send_frame, 1))?;
|
66
|
+
response.define_method("write", method!(ItsiHttpResponse::send_frame, 1))?;
|
67
|
+
response.define_method("read", method!(ItsiHttpResponse::recv_frame, 0))?;
|
68
|
+
response.define_method(
|
69
|
+
"send_and_close",
|
70
|
+
method!(ItsiHttpResponse::send_and_close, 1),
|
71
|
+
)?;
|
72
|
+
response.define_method("close_write", method!(ItsiHttpResponse::close_write, 0))?;
|
73
|
+
response.define_method("close_read", method!(ItsiHttpResponse::close_read, 0))?;
|
74
|
+
response.define_method("close", method!(ItsiHttpResponse::close, 0))?;
|
75
|
+
response.define_method("hijack", method!(ItsiHttpResponse::hijack, 1))?;
|
76
|
+
response.define_method("json?", method!(ItsiHttpResponse::is_json, 0))?;
|
77
|
+
response.define_method("html?", method!(ItsiHttpResponse::is_html, 0))?;
|
78
|
+
|
79
|
+
let grpc_request = ruby.get_inner(&ITSI_GRPC_REQUEST);
|
80
|
+
|
81
|
+
grpc_request.define_method("service_name", method!(ItsiGrpcRequest::service_name, 0))?;
|
82
|
+
grpc_request.define_method("method_name", method!(ItsiGrpcRequest::method_name, 0))?;
|
83
|
+
grpc_request.define_method("stream", method!(ItsiGrpcRequest::stream, 0))?;
|
84
|
+
grpc_request.define_method("json?", method!(ItsiGrpcRequest::is_json, 0))?;
|
85
|
+
grpc_request.define_method(
|
86
|
+
"content_type",
|
87
|
+
method!(ItsiGrpcRequest::content_type_str, 0),
|
88
|
+
)?;
|
89
|
+
|
90
|
+
let grpc_stream = ruby.get_inner(&ITSI_GRPC_STREAM);
|
91
|
+
grpc_stream.define_method("read", method!(ItsiGrpcStream::read, 1))?;
|
92
|
+
grpc_stream.define_method("write", method!(ItsiGrpcStream::write, 1))?;
|
93
|
+
grpc_stream.define_method("flush", method!(ItsiGrpcStream::flush, 0))?;
|
94
|
+
grpc_stream.define_method("send_trailers", method!(ItsiGrpcStream::send_trailers, 1))?;
|
95
|
+
|
96
|
+
let _grpc_response = ruby.get_inner(&ITSI_GRPC_RESPONSE);
|
104
97
|
|
105
98
|
Ok(())
|
106
99
|
}
|
100
|
+
|
101
|
+
pub fn log_debug(msg: String) {
|
102
|
+
debug!(msg);
|
103
|
+
}
|
104
|
+
pub fn log_info(msg: String) {
|
105
|
+
info!(msg);
|
106
|
+
}
|
107
|
+
pub fn log_warn(msg: String) {
|
108
|
+
warn!(msg);
|
109
|
+
}
|
110
|
+
pub fn log_error(msg: String) {
|
111
|
+
error!(msg);
|
112
|
+
}
|
@@ -6,7 +6,7 @@ use tempfile::NamedTempFile;
|
|
6
6
|
|
7
7
|
const THRESHOLD: usize = 1024 * 1024; // 1 MB
|
8
8
|
|
9
|
-
///
|
9
|
+
/// A container that will hold data in memory if it’s small, or in a temporary file on disk if it exceeds THRESHOLD.
|
10
10
|
/// Used for providing Rack input data.
|
11
11
|
pub enum BigBytes {
|
12
12
|
InMemory(Vec<u8>),
|
@@ -27,7 +27,7 @@ impl BigBytes {
|
|
27
27
|
BigBytes::InMemory(Vec::new())
|
28
28
|
}
|
29
29
|
|
30
|
-
///
|
30
|
+
/// Returns either the raw bytes, or the file path of the BigBytes
|
31
31
|
///
|
32
32
|
/// - If stored in memory, returns a clone of the bytes.
|
33
33
|
/// - If stored on disk, returns the file path of the temporary file.
|
@@ -42,17 +42,22 @@ impl BigBytes {
|
|
42
42
|
}
|
43
43
|
}
|
44
44
|
|
45
|
-
|
45
|
+
/// Turn this into a value that can be used in Ruby.
|
46
|
+
pub fn as_value(&self) -> Option<Value> {
|
46
47
|
match self {
|
47
48
|
BigBytes::InMemory(bytes) => {
|
48
49
|
let bytes = Bytes::from(bytes.to_owned());
|
49
|
-
bytes.
|
50
|
+
if bytes.is_empty() {
|
51
|
+
None
|
52
|
+
} else {
|
53
|
+
Some(bytes.into_value())
|
54
|
+
}
|
50
55
|
}
|
51
56
|
BigBytes::OnDisk(path) => {
|
52
57
|
let ruby = Ruby::get().unwrap();
|
53
58
|
let rarray = ruby.ary_new();
|
54
59
|
rarray.push(path.path().to_str().unwrap().into_value()).ok();
|
55
|
-
rarray.into_value()
|
60
|
+
Some(rarray.into_value())
|
56
61
|
}
|
57
62
|
}
|
58
63
|
}
|
data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs}
RENAMED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
pub mod big_bytes;
|
2
|
+
use big_bytes::BigBytes;
|
2
3
|
use bytes::Bytes;
|
3
4
|
use futures::executor::block_on;
|
4
5
|
use http_body_util::{BodyDataStream, BodyExt};
|
@@ -25,10 +26,10 @@ pub enum ItsiBody {
|
|
25
26
|
}
|
26
27
|
|
27
28
|
impl ItsiBody {
|
28
|
-
pub fn into_value(&self) -> Value {
|
29
|
+
pub fn into_value(&self) -> Option<Value> {
|
29
30
|
match self {
|
30
31
|
ItsiBody::Buffered(bytes) => bytes.as_value(),
|
31
|
-
ItsiBody::Stream(proxy) => proxy.clone().into_value(),
|
32
|
+
ItsiBody::Stream(proxy) => Some(proxy.clone().into_value()),
|
32
33
|
}
|
33
34
|
}
|
34
35
|
}
|
@@ -98,6 +99,24 @@ impl ItsiBodyProxy {
|
|
98
99
|
Ok(Some(output_string))
|
99
100
|
}
|
100
101
|
|
102
|
+
pub fn to_bytes(&self) -> MagnusResult<Vec<u8>> {
|
103
|
+
self.verify_open()?;
|
104
|
+
let mut stream = self.incoming.lock();
|
105
|
+
let mut buf = self.buf.lock();
|
106
|
+
|
107
|
+
while let Some(chunk) = block_on(stream.next()) {
|
108
|
+
let chunk = chunk.map_err(|err| {
|
109
|
+
magnus::Error::new(
|
110
|
+
magnus::exception::exception(),
|
111
|
+
format!("Error reading body {:?}", err),
|
112
|
+
)
|
113
|
+
})?;
|
114
|
+
buf.extend_from_slice(&chunk);
|
115
|
+
}
|
116
|
+
|
117
|
+
Ok(buf.clone())
|
118
|
+
}
|
119
|
+
|
101
120
|
/// Equivalent to calling gets and yielding it, until we reach EOF
|
102
121
|
pub fn each(ruby: &Ruby, rbself: &Self) -> MagnusResult<()> {
|
103
122
|
let proc = ruby.block_proc()?;
|
@@ -0,0 +1,147 @@
|
|
1
|
+
use derive_more::Debug;
|
2
|
+
use http::{request::Parts, Response, StatusCode};
|
3
|
+
use http_body_util::{combinators::BoxBody, BodyExt, Empty};
|
4
|
+
use itsi_error::from::CLIENT_CONNECTION_CLOSED;
|
5
|
+
use itsi_rb_helpers::{print_rb_backtrace, HeapValue};
|
6
|
+
use itsi_tracing::debug;
|
7
|
+
use magnus::{
|
8
|
+
block::Proc,
|
9
|
+
error::{ErrorType, Result as MagnusResult},
|
10
|
+
Error,
|
11
|
+
};
|
12
|
+
use magnus::{
|
13
|
+
value::{LazyId, ReprValue},
|
14
|
+
Ruby, Value,
|
15
|
+
};
|
16
|
+
use std::{sync::Arc, time::Instant};
|
17
|
+
use tokio::sync::mpsc::{self};
|
18
|
+
use tracing::error;
|
19
|
+
|
20
|
+
use super::itsi_grpc_stream::ItsiGrpcStream;
|
21
|
+
use crate::server::{
|
22
|
+
byte_frame::ByteFrame,
|
23
|
+
itsi_service::RequestContext,
|
24
|
+
request_job::RequestJob,
|
25
|
+
types::{HttpRequest, HttpResponse},
|
26
|
+
};
|
27
|
+
|
28
|
+
static ID_MESSAGE: LazyId = LazyId::new("message");
|
29
|
+
|
30
|
+
#[derive(Debug)]
|
31
|
+
#[magnus::wrap(class = "Itsi::GrpcRequest", free_immediately, size)]
|
32
|
+
pub struct ItsiGrpcRequest {
|
33
|
+
pub parts: Parts,
|
34
|
+
pub start: Instant,
|
35
|
+
#[debug(skip)]
|
36
|
+
pub context: RequestContext,
|
37
|
+
#[debug(skip)]
|
38
|
+
pub stream: ItsiGrpcStream,
|
39
|
+
}
|
40
|
+
|
41
|
+
impl ItsiGrpcRequest {
|
42
|
+
pub fn service_name(&self) -> MagnusResult<String> {
|
43
|
+
let path = self.parts.uri.path();
|
44
|
+
Ok(path.split('/').nth_back(1).unwrap().to_string())
|
45
|
+
}
|
46
|
+
|
47
|
+
pub fn method_name(&self) -> MagnusResult<String> {
|
48
|
+
let path = self.parts.uri.path();
|
49
|
+
Ok(path.split('/').nth_back(0).unwrap().to_string())
|
50
|
+
}
|
51
|
+
|
52
|
+
pub fn stream(&self) -> MagnusResult<ItsiGrpcStream> {
|
53
|
+
Ok(self.stream.clone())
|
54
|
+
}
|
55
|
+
|
56
|
+
pub fn content_type_str(&self) -> &str {
|
57
|
+
self.parts
|
58
|
+
.headers
|
59
|
+
.get("Content-Type")
|
60
|
+
.and_then(|hv| hv.to_str().ok())
|
61
|
+
.unwrap_or("application/x-www-form-urlencoded")
|
62
|
+
}
|
63
|
+
|
64
|
+
pub fn is_json(&self) -> bool {
|
65
|
+
self.content_type_str() == "application/json"
|
66
|
+
}
|
67
|
+
|
68
|
+
pub fn process(self, ruby: &Ruby, app_proc: Arc<HeapValue<Proc>>) -> magnus::error::Result<()> {
|
69
|
+
let response = self.stream.clone();
|
70
|
+
let result = app_proc.call::<_, Value>((self,));
|
71
|
+
if let Err(err) = result {
|
72
|
+
Self::internal_error(ruby, response, err);
|
73
|
+
}
|
74
|
+
Ok(())
|
75
|
+
}
|
76
|
+
|
77
|
+
pub fn internal_error(_ruby: &Ruby, stream: ItsiGrpcStream, err: Error) {
|
78
|
+
if let Some(rb_err) = err.value() {
|
79
|
+
print_rb_backtrace(rb_err);
|
80
|
+
stream.internal_server_error(err.to_string());
|
81
|
+
} else {
|
82
|
+
stream.internal_server_error(err.to_string());
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
pub(crate) async fn process_request(
|
87
|
+
app: Arc<HeapValue<Proc>>,
|
88
|
+
hyper_request: HttpRequest,
|
89
|
+
context: &RequestContext,
|
90
|
+
) -> itsi_error::Result<HttpResponse> {
|
91
|
+
let (request, mut receiver) = ItsiGrpcRequest::new(hyper_request, context).await;
|
92
|
+
let shutdown_channel = context.service.shutdown_channel.clone();
|
93
|
+
let response_stream = request.stream.clone();
|
94
|
+
match context
|
95
|
+
.sender
|
96
|
+
.send(RequestJob::ProcessGrpcRequest(request, app))
|
97
|
+
.await
|
98
|
+
{
|
99
|
+
Err(err) => {
|
100
|
+
error!("Error occurred: {}", err);
|
101
|
+
let mut response = Response::new(BoxBody::new(Empty::new()));
|
102
|
+
*response.status_mut() = StatusCode::BAD_REQUEST;
|
103
|
+
Ok(response)
|
104
|
+
}
|
105
|
+
_ => match receiver.recv().await {
|
106
|
+
Some(first_frame) => Ok(response_stream
|
107
|
+
.build_response(first_frame, receiver, shutdown_channel)
|
108
|
+
.await),
|
109
|
+
None => Ok(Response::new(BoxBody::new(Empty::new()))),
|
110
|
+
},
|
111
|
+
}
|
112
|
+
}
|
113
|
+
pub fn is_connection_closed_err(ruby: &Ruby, err: &Error) -> bool {
|
114
|
+
match err.error_type() {
|
115
|
+
ErrorType::Jump(_) => false,
|
116
|
+
ErrorType::Error(_, _) => false,
|
117
|
+
ErrorType::Exception(exception) => {
|
118
|
+
exception.is_kind_of(ruby.exception_eof_error())
|
119
|
+
&& err
|
120
|
+
.value()
|
121
|
+
.map(|v| {
|
122
|
+
v.funcall::<_, _, String>(*ID_MESSAGE, ())
|
123
|
+
.unwrap_or("".to_string())
|
124
|
+
.eq(CLIENT_CONNECTION_CLOSED)
|
125
|
+
})
|
126
|
+
.unwrap_or(false)
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
pub(crate) async fn new(
|
132
|
+
request: HttpRequest,
|
133
|
+
context: &RequestContext,
|
134
|
+
) -> (ItsiGrpcRequest, mpsc::Receiver<ByteFrame>) {
|
135
|
+
let (parts, body) = request.into_parts();
|
136
|
+
let response_channel = mpsc::channel::<ByteFrame>(100);
|
137
|
+
(
|
138
|
+
Self {
|
139
|
+
context: context.clone(),
|
140
|
+
start: Instant::now(),
|
141
|
+
parts,
|
142
|
+
stream: ItsiGrpcStream::new(response_channel.0, body.into_data_stream()),
|
143
|
+
},
|
144
|
+
response_channel.1,
|
145
|
+
)
|
146
|
+
}
|
147
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
use derive_more::Debug;
|
2
|
+
use http::request::Parts;
|
3
|
+
use tokio::sync::mpsc::Sender;
|
4
|
+
|
5
|
+
use crate::server::byte_frame::ByteFrame;
|
6
|
+
|
7
|
+
#[derive(Debug, Clone)]
|
8
|
+
#[magnus::wrap(class = "Itsi::GrpcResponse", free_immediately, size)]
|
9
|
+
pub struct ItsiGrpcResponse {
|
10
|
+
pub parts: Parts,
|
11
|
+
#[debug(skip)]
|
12
|
+
pub sender: Sender<ByteFrame>,
|
13
|
+
}
|
14
|
+
|
15
|
+
impl ItsiGrpcResponse {
|
16
|
+
pub fn new(parts: Parts, sender: Sender<ByteFrame>) -> Self {
|
17
|
+
Self { parts, sender }
|
18
|
+
}
|
19
|
+
}
|