itsi-server 0.1.1 → 0.1.18
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/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +7 -0
- data/Cargo.lock +3937 -0
- data/Cargo.toml +7 -0
- data/README.md +4 -0
- data/Rakefile +8 -1
- data/_index.md +6 -0
- data/exe/itsi +141 -46
- data/ext/itsi_error/Cargo.toml +3 -0
- data/ext/itsi_error/src/lib.rs +98 -24
- 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_instrument_entry/Cargo.toml +15 -0
- data/ext/itsi_instrument_entry/src/lib.rs +31 -0
- data/ext/itsi_rb_helpers/Cargo.toml +3 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
- data/ext/itsi_rb_helpers/src/lib.rs +140 -10
- 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_scheduler/Cargo.toml +24 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
- data/ext/itsi_scheduler/src/lib.rs +38 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +72 -14
- data/ext/itsi_server/extconf.rb +1 -1
- data/ext/itsi_server/src/default_responses/html/401.html +68 -0
- data/ext/itsi_server/src/default_responses/html/403.html +68 -0
- data/ext/itsi_server/src/default_responses/html/404.html +68 -0
- data/ext/itsi_server/src/default_responses/html/413.html +71 -0
- data/ext/itsi_server/src/default_responses/html/429.html +68 -0
- data/ext/itsi_server/src/default_responses/html/500.html +71 -0
- data/ext/itsi_server/src/default_responses/html/502.html +71 -0
- data/ext/itsi_server/src/default_responses/html/503.html +68 -0
- data/ext/itsi_server/src/default_responses/html/504.html +69 -0
- data/ext/itsi_server/src/default_responses/html/index.html +238 -0
- data/ext/itsi_server/src/default_responses/json/401.json +6 -0
- data/ext/itsi_server/src/default_responses/json/403.json +6 -0
- data/ext/itsi_server/src/default_responses/json/404.json +6 -0
- data/ext/itsi_server/src/default_responses/json/413.json +6 -0
- data/ext/itsi_server/src/default_responses/json/429.json +6 -0
- data/ext/itsi_server/src/default_responses/json/500.json +6 -0
- data/ext/itsi_server/src/default_responses/json/502.json +6 -0
- data/ext/itsi_server/src/default_responses/json/503.json +6 -0
- data/ext/itsi_server/src/default_responses/json/504.json +6 -0
- data/ext/itsi_server/src/default_responses/mod.rs +11 -0
- data/ext/itsi_server/src/env.rs +43 -0
- data/ext/itsi_server/src/lib.rs +132 -40
- data/ext/itsi_server/src/prelude.rs +2 -0
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +109 -0
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +143 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +344 -0
- data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +264 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +345 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +391 -0
- 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 +375 -0
- data/ext/itsi_server/src/ruby_types/itsi_server.rs +83 -0
- data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
- data/ext/itsi_server/src/server/binds/bind.rs +201 -0
- data/ext/itsi_server/src/server/binds/bind_protocol.rs +37 -0
- data/ext/itsi_server/src/server/binds/listener.rs +432 -0
- data/ext/itsi_server/src/server/binds/mod.rs +4 -0
- data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +132 -0
- data/ext/itsi_server/src/server/binds/tls.rs +270 -0
- data/ext/itsi_server/src/server/byte_frame.rs +32 -0
- data/ext/itsi_server/src/server/http_message_types.rs +97 -0
- data/ext/itsi_server/src/server/io_stream.rs +105 -0
- data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
- data/ext/itsi_server/src/server/middleware_stack/middleware.rs +165 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +56 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +87 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +86 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +285 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +142 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +289 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +292 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +55 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +190 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +157 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +195 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +201 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +87 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +414 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +131 -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 +44 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +36 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +126 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +180 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +55 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +163 -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 +347 -0
- data/ext/itsi_server/src/server/mod.rs +12 -5
- data/ext/itsi_server/src/server/process_worker.rs +247 -0
- data/ext/itsi_server/src/server/request_job.rs +11 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +342 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +30 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +421 -0
- data/ext/itsi_server/src/server/signal.rs +76 -0
- data/ext/itsi_server/src/server/size_limited_incoming.rs +101 -0
- data/ext/itsi_server/src/server/thread_worker.rs +475 -0
- data/ext/itsi_server/src/services/cache_store.rs +74 -0
- data/ext/itsi_server/src/services/itsi_http_service.rs +239 -0
- data/ext/itsi_server/src/services/mime_types.rs +1416 -0
- data/ext/itsi_server/src/services/mod.rs +6 -0
- data/ext/itsi_server/src/services/password_hasher.rs +83 -0
- data/ext/itsi_server/src/services/rate_limiter.rs +569 -0
- data/ext/itsi_server/src/services/static_file_server.rs +1324 -0
- data/ext/itsi_tracing/Cargo.toml +5 -0
- data/ext/itsi_tracing/src/lib.rs +315 -7
- 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/http_request/response_status_shortcodes.rb +74 -0
- data/lib/itsi/http_request.rb +186 -0
- data/lib/itsi/http_response.rb +41 -0
- data/lib/itsi/passfile.rb +109 -0
- data/lib/itsi/server/config/dsl.rb +565 -0
- data/lib/itsi/server/config.rb +166 -0
- data/lib/itsi/server/default_app/default_app.rb +34 -0
- data/lib/itsi/server/default_app/index.html +115 -0
- data/lib/itsi/server/default_config/Itsi-rackup.rb +119 -0
- data/lib/itsi/server/default_config/Itsi.rb +107 -0
- data/lib/itsi/server/grpc/grpc_call.rb +246 -0
- data/lib/itsi/server/grpc/grpc_interface.rb +100 -0
- data/lib/itsi/server/grpc/reflection/v1/reflection_pb.rb +26 -0
- data/lib/itsi/server/grpc/reflection/v1/reflection_services_pb.rb +122 -0
- data/lib/itsi/server/rack/handler/itsi.rb +27 -0
- data/lib/itsi/server/rack_interface.rb +94 -0
- data/lib/itsi/server/route_tester.rb +107 -0
- data/lib/itsi/server/scheduler_interface.rb +21 -0
- data/lib/itsi/server/scheduler_mode.rb +10 -0
- data/lib/itsi/server/signal_trap.rb +29 -0
- data/lib/itsi/server/typed_handlers/param_parser.rb +200 -0
- data/lib/itsi/server/typed_handlers/source_parser.rb +55 -0
- data/lib/itsi/server/typed_handlers.rb +17 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +160 -9
- data/lib/itsi/standard_headers.rb +86 -0
- data/lib/ruby_lsp/itsi/addon.rb +111 -0
- data/lib/shell_completions/completions.rb +26 -0
- metadata +182 -25
- data/ext/itsi_server/src/request/itsi_request.rs +0 -143
- data/ext/itsi_server/src/request/mod.rs +0 -1
- data/ext/itsi_server/src/server/bind.rs +0 -138
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -32
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -52
- data/ext/itsi_server/src/server/itsi_server.rs +0 -182
- data/ext/itsi_server/src/server/listener.rs +0 -218
- data/ext/itsi_server/src/server/tls.rs +0 -138
- data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
- data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
- data/lib/itsi/request.rb +0 -39
data/ext/itsi_tracing/Cargo.toml
CHANGED
data/ext/itsi_tracing/src/lib.rs
CHANGED
@@ -1,11 +1,319 @@
|
|
1
|
+
use atty::{Stream, is};
|
2
|
+
use std::{
|
3
|
+
env,
|
4
|
+
sync::{Mutex, OnceLock},
|
5
|
+
};
|
1
6
|
pub use tracing::{debug, error, info, trace, warn};
|
2
|
-
use
|
7
|
+
use tracing_appender::rolling;
|
8
|
+
use tracing_subscriber::fmt::writer::BoxMakeWriter;
|
9
|
+
use tracing_subscriber::{EnvFilter, fmt, prelude::*, reload};
|
10
|
+
use tracing_subscriber::{Layer, Registry, layer::Layered};
|
3
11
|
|
12
|
+
// Global reload handle for changing the level at runtime.
|
13
|
+
static RELOAD_HANDLE: OnceLock<
|
14
|
+
Mutex<Option<reload::Handle<EnvFilter, tracing_subscriber::Registry>>>,
|
15
|
+
> = OnceLock::new();
|
16
|
+
|
17
|
+
// Global reload handle for changing the formatting layer (log target/format) at runtime.
|
18
|
+
type ReloadFmtHandle = reload::Handle<
|
19
|
+
Box<
|
20
|
+
dyn Layer<
|
21
|
+
tracing_subscriber::layer::Layered<
|
22
|
+
reload::Layer<EnvFilter, tracing_subscriber::Registry>,
|
23
|
+
tracing_subscriber::Registry,
|
24
|
+
>,
|
25
|
+
> + Send
|
26
|
+
+ Sync,
|
27
|
+
>,
|
28
|
+
Layered<tracing_subscriber::reload::Layer<EnvFilter, Registry>, Registry>,
|
29
|
+
>;
|
30
|
+
|
31
|
+
static RELOAD_FMT_HANDLE: OnceLock<Mutex<Option<ReloadFmtHandle>>> = OnceLock::new();
|
32
|
+
|
33
|
+
// Global current log configuration for formatting options.
|
34
|
+
static CURRENT_CONFIG: OnceLock<Mutex<LogConfig>> = OnceLock::new();
|
35
|
+
|
36
|
+
/// Log format: Plain or JSON.
|
37
|
+
#[derive(Debug, Clone)]
|
38
|
+
pub enum LogFormat {
|
39
|
+
Plain,
|
40
|
+
Json,
|
41
|
+
}
|
42
|
+
|
43
|
+
/// Log target: STDOUT, File, or Both.
|
44
|
+
#[derive(Debug, Clone)]
|
45
|
+
pub enum LogTarget {
|
46
|
+
Stdout,
|
47
|
+
File(String), // file name (rotated daily)
|
48
|
+
Both(String), // file name (rotated daily) plus STDOUT
|
49
|
+
}
|
50
|
+
|
51
|
+
/// Logger configuration.
|
52
|
+
#[derive(Debug, Clone)]
|
53
|
+
pub struct LogConfig {
|
54
|
+
/// Log level as a string (e.g. "info", "debug").
|
55
|
+
pub level: String,
|
56
|
+
/// Format: Plain (with optional ANSI) or JSON.
|
57
|
+
pub format: LogFormat,
|
58
|
+
/// Target: STDOUT, File, or Both.
|
59
|
+
pub target: LogTarget,
|
60
|
+
/// Whether to enable ANSI coloring (for plain text).
|
61
|
+
pub use_ansi: bool,
|
62
|
+
}
|
63
|
+
|
64
|
+
impl Default for LogConfig {
|
65
|
+
fn default() -> Self {
|
66
|
+
let level = env::var("ITSI_LOG").unwrap_or_else(|_| "info".into());
|
67
|
+
let format = match env::var("ITSI_LOG_FORMAT").as_deref() {
|
68
|
+
Ok("json") => LogFormat::Json,
|
69
|
+
_ => LogFormat::Plain,
|
70
|
+
};
|
71
|
+
let target = match env::var("ITSI_LOG_TARGET").as_deref() {
|
72
|
+
Ok("file") => {
|
73
|
+
let file = env::var("ITSI_LOG_FILE").unwrap_or_else(|_| "app.log".into());
|
74
|
+
LogTarget::File(file)
|
75
|
+
}
|
76
|
+
Ok("both") => {
|
77
|
+
let file = env::var("ITSI_LOG_FILE").unwrap_or_else(|_| "app.log".into());
|
78
|
+
LogTarget::Both(file)
|
79
|
+
}
|
80
|
+
_ => LogTarget::Stdout,
|
81
|
+
};
|
82
|
+
// If ITSI_LOG_ANSI is set, use that; otherwise, use ANSI if stdout is a TTY.
|
83
|
+
let use_ansi = env::var("ITSI_LOG_ANSI")
|
84
|
+
.map(|s| s == "true")
|
85
|
+
.unwrap_or_else(|_| is(Stream::Stdout));
|
86
|
+
Self {
|
87
|
+
level,
|
88
|
+
format,
|
89
|
+
target,
|
90
|
+
use_ansi,
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
/// Build the formatting layer based on the provided configuration.
|
96
|
+
fn build_fmt_layer(
|
97
|
+
config: &LogConfig,
|
98
|
+
) -> Box<
|
99
|
+
dyn Layer<
|
100
|
+
tracing_subscriber::layer::Layered<
|
101
|
+
reload::Layer<EnvFilter, tracing_subscriber::Registry>,
|
102
|
+
tracing_subscriber::Registry,
|
103
|
+
>,
|
104
|
+
> + Send
|
105
|
+
+ Sync,
|
106
|
+
> {
|
107
|
+
match &config.target {
|
108
|
+
LogTarget::Stdout => match config.format {
|
109
|
+
LogFormat::Plain => fmt::layer()
|
110
|
+
.compact()
|
111
|
+
.with_file(false)
|
112
|
+
.with_line_number(false)
|
113
|
+
.with_target(false)
|
114
|
+
.with_thread_ids(false)
|
115
|
+
.with_writer(BoxMakeWriter::new(std::io::stdout))
|
116
|
+
.with_ansi(config.use_ansi)
|
117
|
+
.boxed(),
|
118
|
+
LogFormat::Json => fmt::layer()
|
119
|
+
.compact()
|
120
|
+
.with_file(false)
|
121
|
+
.with_line_number(false)
|
122
|
+
.with_target(false)
|
123
|
+
.with_thread_ids(false)
|
124
|
+
.with_writer(BoxMakeWriter::new(std::io::stdout))
|
125
|
+
.with_ansi(config.use_ansi)
|
126
|
+
.json()
|
127
|
+
.boxed(),
|
128
|
+
},
|
129
|
+
LogTarget::File(file) => {
|
130
|
+
let file_clone = file.clone();
|
131
|
+
match config.format {
|
132
|
+
LogFormat::Plain => fmt::layer()
|
133
|
+
.compact()
|
134
|
+
.with_file(false)
|
135
|
+
.with_line_number(false)
|
136
|
+
.with_target(false)
|
137
|
+
.with_thread_ids(false)
|
138
|
+
.with_writer(BoxMakeWriter::new(move || {
|
139
|
+
rolling::daily(".", file_clone.clone())
|
140
|
+
}))
|
141
|
+
.with_ansi(false)
|
142
|
+
.boxed(),
|
143
|
+
LogFormat::Json => {
|
144
|
+
let file_clone = file.clone();
|
145
|
+
fmt::layer()
|
146
|
+
.compact()
|
147
|
+
.with_file(false)
|
148
|
+
.with_line_number(false)
|
149
|
+
.with_target(false)
|
150
|
+
.with_thread_ids(false)
|
151
|
+
.with_writer(BoxMakeWriter::new(move || {
|
152
|
+
rolling::daily(".", file_clone.clone())
|
153
|
+
}))
|
154
|
+
.with_ansi(false)
|
155
|
+
.json()
|
156
|
+
.boxed()
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
LogTarget::Both(file) => {
|
161
|
+
let file_clone = file.clone();
|
162
|
+
match config.format {
|
163
|
+
LogFormat::Plain => {
|
164
|
+
let stdout_layer = fmt::layer()
|
165
|
+
.compact()
|
166
|
+
.with_file(false)
|
167
|
+
.with_line_number(false)
|
168
|
+
.with_target(false)
|
169
|
+
.with_thread_ids(false)
|
170
|
+
.with_writer(BoxMakeWriter::new(std::io::stdout))
|
171
|
+
.with_ansi(config.use_ansi);
|
172
|
+
let file_layer = fmt::layer()
|
173
|
+
.compact()
|
174
|
+
.with_file(false)
|
175
|
+
.with_line_number(false)
|
176
|
+
.with_target(false)
|
177
|
+
.with_thread_ids(false)
|
178
|
+
.with_writer(BoxMakeWriter::new(move || {
|
179
|
+
rolling::daily(".", file_clone.clone())
|
180
|
+
}))
|
181
|
+
.with_ansi(false);
|
182
|
+
stdout_layer.and_then(file_layer).boxed()
|
183
|
+
}
|
184
|
+
LogFormat::Json => {
|
185
|
+
let stdout_layer = fmt::layer()
|
186
|
+
.compact()
|
187
|
+
.with_file(false)
|
188
|
+
.with_line_number(false)
|
189
|
+
.with_target(false)
|
190
|
+
.with_thread_ids(false)
|
191
|
+
.with_writer(BoxMakeWriter::new(std::io::stdout))
|
192
|
+
.with_ansi(config.use_ansi)
|
193
|
+
.json();
|
194
|
+
let file_layer = fmt::layer()
|
195
|
+
.compact()
|
196
|
+
.with_file(false)
|
197
|
+
.with_line_number(false)
|
198
|
+
.with_target(false)
|
199
|
+
.with_thread_ids(false)
|
200
|
+
.with_writer(BoxMakeWriter::new(move || {
|
201
|
+
rolling::daily(".", file_clone.clone())
|
202
|
+
}))
|
203
|
+
.with_ansi(false)
|
204
|
+
.json();
|
205
|
+
stdout_layer.and_then(file_layer).boxed()
|
206
|
+
}
|
207
|
+
}
|
208
|
+
}
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
/// Update the formatting layer using the current configuration.
|
213
|
+
fn update_fmt_layer(config: &LogConfig) {
|
214
|
+
if let Some(handle) = RELOAD_FMT_HANDLE.get().unwrap().lock().unwrap().as_ref() {
|
215
|
+
let new_layer = build_fmt_layer(config);
|
216
|
+
handle
|
217
|
+
.modify(|layer| {
|
218
|
+
*layer = new_layer;
|
219
|
+
})
|
220
|
+
.expect("Failed to update formatting layer");
|
221
|
+
} else {
|
222
|
+
eprintln!("Reload handle for formatting layer not initialized; call init() first.");
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
/// Initialize the global tracing subscriber with the default configuration.
|
4
227
|
pub fn init() {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
228
|
+
init_with_config(LogConfig::default());
|
229
|
+
}
|
230
|
+
|
231
|
+
/// Initialize the global tracing subscriber with a given configuration.
|
232
|
+
pub fn init_with_config(config: LogConfig) {
|
233
|
+
// Store the current config in a global for future updates.
|
234
|
+
CURRENT_CONFIG.set(Mutex::new(config.clone())).ok();
|
235
|
+
|
236
|
+
// Build an EnvFilter from the configured level.
|
237
|
+
let env_filter = EnvFilter::new(config.clone().level);
|
238
|
+
|
239
|
+
// Build the formatting layer based on the configuration.
|
240
|
+
let fmt_layer = build_fmt_layer(&config);
|
241
|
+
|
242
|
+
// Create a reloadable filter layer so we can update the level at runtime.
|
243
|
+
let (filter_layer, filter_handle) = reload::Layer::new(env_filter);
|
244
|
+
|
245
|
+
// Create a reloadable formatting layer so we can update the target/format at runtime.
|
246
|
+
let (fmt_layer, fmt_handle) = reload::Layer::new(fmt_layer);
|
247
|
+
|
248
|
+
// Build the subscriber registry.
|
249
|
+
let subscriber = tracing_subscriber::registry()
|
250
|
+
.with(filter_layer)
|
251
|
+
.with(fmt_layer);
|
252
|
+
|
253
|
+
tracing::subscriber::set_global_default(subscriber)
|
254
|
+
.expect("Unable to set global tracing subscriber");
|
255
|
+
|
256
|
+
RELOAD_HANDLE.set(Mutex::new(Some(filter_handle))).unwrap();
|
257
|
+
RELOAD_FMT_HANDLE.set(Mutex::new(Some(fmt_handle))).ok();
|
258
|
+
}
|
259
|
+
|
260
|
+
/// Change the log level at runtime.
|
261
|
+
pub fn set_level(new_level: &str) {
|
262
|
+
if let Some(handle) = RELOAD_HANDLE.get().unwrap().lock().unwrap().as_ref() {
|
263
|
+
handle
|
264
|
+
.modify(|filter| *filter = EnvFilter::new(new_level))
|
265
|
+
.expect("Failed to update log level");
|
266
|
+
|
267
|
+
// Also update the stored config.
|
268
|
+
if let Some(config_mutex) = CURRENT_CONFIG.get() {
|
269
|
+
let mut config = config_mutex.lock().unwrap();
|
270
|
+
config.level = new_level.to_string();
|
271
|
+
}
|
272
|
+
} else {
|
273
|
+
eprintln!("Reload handle not initialized; call init() first.");
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
/// Change the log target at runtime.
|
278
|
+
pub fn set_target(new_target: &str) {
|
279
|
+
let target: LogTarget = match new_target {
|
280
|
+
"stdout" => LogTarget::Stdout,
|
281
|
+
path => LogTarget::File(path.to_string()),
|
282
|
+
};
|
283
|
+
if let Some(config_mutex) = CURRENT_CONFIG.get() {
|
284
|
+
let mut config = config_mutex.lock().unwrap();
|
285
|
+
config.target = target;
|
286
|
+
update_fmt_layer(&config);
|
287
|
+
} else {
|
288
|
+
eprintln!("Current configuration not initialized; call init() first.");
|
289
|
+
}
|
290
|
+
}
|
291
|
+
|
292
|
+
/// Change the log format at runtime.
|
293
|
+
pub fn set_format(new_format: &str) {
|
294
|
+
let format = match new_format {
|
295
|
+
"json" => LogFormat::Json,
|
296
|
+
"plain" => LogFormat::Plain,
|
297
|
+
_ => LogFormat::Json,
|
298
|
+
};
|
299
|
+
if let Some(config_mutex) = CURRENT_CONFIG.get() {
|
300
|
+
let mut config = config_mutex.lock().unwrap();
|
301
|
+
config.format = format;
|
302
|
+
update_fmt_layer(&config);
|
303
|
+
} else {
|
304
|
+
eprintln!("Current configuration not initialized; call init() first.");
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
/// Run a function silently by temporarily setting a no-op subscriber.
|
309
|
+
pub fn run_silently<F, R>(f: F) -> R
|
310
|
+
where
|
311
|
+
F: FnOnce() -> R,
|
312
|
+
{
|
313
|
+
let no_op_subscriber = tracing_subscriber::fmt()
|
314
|
+
.with_writer(std::io::sink)
|
315
|
+
.with_max_level(tracing_subscriber::filter::LevelFilter::OFF)
|
316
|
+
.finish();
|
317
|
+
let dispatch = tracing::Dispatch::new(no_op_subscriber);
|
318
|
+
tracing::dispatcher::with_default(&dispatch, f)
|
11
319
|
}
|
data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock
ADDED
File without changes
|
data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock
ADDED
File without changes
|
data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock
ADDED
File without changes
|
data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock
ADDED
File without changes
|
data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock
ADDED
File without changes
|
data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock
ADDED
File without changes
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Itsi
|
2
|
+
class HttpRequest
|
3
|
+
module ResponseStatusShortcodes
|
4
|
+
|
5
|
+
HTTP_STATUS_CODES = {
|
6
|
+
100 => :continue,
|
7
|
+
101 => :switching_protocols,
|
8
|
+
102 => :processing,
|
9
|
+
200 => :ok,
|
10
|
+
201 => :created,
|
11
|
+
202 => :accepted,
|
12
|
+
203 => :non_authoritative_information,
|
13
|
+
204 => :no_content,
|
14
|
+
205 => :reset_content,
|
15
|
+
206 => :partial_content,
|
16
|
+
207 => :multi_status,
|
17
|
+
208 => :already_reported,
|
18
|
+
226 => :im_used,
|
19
|
+
300 => :multiple_choices,
|
20
|
+
301 => :moved_permanently,
|
21
|
+
302 => :found,
|
22
|
+
303 => :see_other,
|
23
|
+
304 => :not_modified,
|
24
|
+
305 => :use_proxy,
|
25
|
+
307 => :temporary_redirect,
|
26
|
+
308 => :permanent_redirect,
|
27
|
+
400 => :bad_request,
|
28
|
+
401 => :unauthorized,
|
29
|
+
402 => :payment_required,
|
30
|
+
403 => :forbidden,
|
31
|
+
404 => :not_found,
|
32
|
+
405 => :method_not_allowed,
|
33
|
+
406 => :not_acceptable,
|
34
|
+
407 => :proxy_authentication_required,
|
35
|
+
408 => :request_timeout,
|
36
|
+
409 => :conflict,
|
37
|
+
410 => :gone,
|
38
|
+
411 => :length_required,
|
39
|
+
412 => :precondition_failed,
|
40
|
+
413 => :payload_too_large,
|
41
|
+
414 => :uri_too_long,
|
42
|
+
415 => :unsupported_media_type,
|
43
|
+
416 => :range_not_satisfiable,
|
44
|
+
417 => :expectation_failed,
|
45
|
+
418 => :im_a_teapot,
|
46
|
+
421 => :misdirected_request,
|
47
|
+
422 => :unprocessable_entity,
|
48
|
+
423 => :locked,
|
49
|
+
424 => :failed_dependency,
|
50
|
+
425 => :too_early,
|
51
|
+
426 => :upgrade_required,
|
52
|
+
428 => :precondition_required,
|
53
|
+
429 => :too_many_requests,
|
54
|
+
431 => :request_header_fields_too_large,
|
55
|
+
451 => :unavailable_for_legal_reasons,
|
56
|
+
500 => :internal_server_error,
|
57
|
+
501 => :not_implemented,
|
58
|
+
502 => :bad_gateway,
|
59
|
+
503 => :service_unavailable,
|
60
|
+
504 => :gateway_timeout,
|
61
|
+
505 => :http_version_not_supported,
|
62
|
+
506 => :variant_also_negotiates,
|
63
|
+
507 => :insufficient_storage,
|
64
|
+
508 => :loop_detected,
|
65
|
+
510 => :not_extended,
|
66
|
+
511 => :network_authentication_required
|
67
|
+
}.freeze
|
68
|
+
|
69
|
+
HTTP_STATUS_CODES.each do |code, name|
|
70
|
+
define_method(name) {|*args, **kwargs| respond(*args, **kwargs, status: code) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
require "socket"
|
5
|
+
require "uri"
|
6
|
+
require_relative 'http_request/response_status_shortcodes'
|
7
|
+
|
8
|
+
module Itsi
|
9
|
+
class HttpRequest
|
10
|
+
include Server::TypedHandlers::ParamParser
|
11
|
+
include ResponseStatusShortcodes
|
12
|
+
attr_accessor :hijacked
|
13
|
+
|
14
|
+
EMPTY_IO = StringIO.new("")
|
15
|
+
RACK_HEADER_MAP = StandardHeaders::ALL.map do |header|
|
16
|
+
rack_form = if header == "content-type"
|
17
|
+
"CONTENT_TYPE"
|
18
|
+
elsif header == "content-length"
|
19
|
+
"CONTENT_LENGTH"
|
20
|
+
else
|
21
|
+
"HTTP_#{header.upcase.gsub(/-/, "_")}"
|
22
|
+
end
|
23
|
+
[header, rack_form]
|
24
|
+
end.to_h.tap do |hm|
|
25
|
+
hm.default_proc = proc { |hsh, key| "HTTP_#{key.upcase.gsub(/-/, "_")}" }
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_rack_env
|
29
|
+
path = self.path
|
30
|
+
host = self.host
|
31
|
+
version = self.version
|
32
|
+
{
|
33
|
+
"SERVER_SOFTWARE" => "Itsi",
|
34
|
+
"SCRIPT_NAME" => script_name,
|
35
|
+
"REQUEST_METHOD" => request_method,
|
36
|
+
"PATH_INFO" => path,
|
37
|
+
"REQUEST_PATH" => path,
|
38
|
+
"QUERY_STRING" => query_string,
|
39
|
+
"REMOTE_ADDR" => remote_addr,
|
40
|
+
"SERVER_PORT" => port.to_s,
|
41
|
+
"SERVER_NAME" => host,
|
42
|
+
"SERVER_PROTOCOL" => version,
|
43
|
+
"HTTP_HOST" => host,
|
44
|
+
"HTTP_VERSION" => version,
|
45
|
+
"itsi.request" => self,
|
46
|
+
"itsi.response" => response,
|
47
|
+
"rack.version" => [version],
|
48
|
+
"rack.url_scheme" => scheme,
|
49
|
+
"rack.input" => build_input_io,
|
50
|
+
"rack.errors" => $stderr,
|
51
|
+
"rack.multithread" => true,
|
52
|
+
"rack.multiprocess" => true,
|
53
|
+
"rack.run_once" => false,
|
54
|
+
"rack.hijack?" => true,
|
55
|
+
"rack.multipart.buffer_size" => 16_384,
|
56
|
+
"rack.hijack" => method(:hijack)
|
57
|
+
}.tap do |r|
|
58
|
+
headers.each do |(k, v)|
|
59
|
+
r[case k
|
60
|
+
when "content-type" then "CONTENT_TYPE"
|
61
|
+
when "content-length" then "CONTENT_LENGTH"
|
62
|
+
when "accept" then "HTTP_ACCEPT"
|
63
|
+
when "accept-encoding" then "HTTP_ACCEPT_ENCODING"
|
64
|
+
when "accept-language" then "HTTP_ACCEPT_LANGUAGE"
|
65
|
+
when "user-agent" then "HTTP_USER_AGENT"
|
66
|
+
when "referer" then "HTTP_REFERER"
|
67
|
+
when "origin" then "HTTP_ORIGIN"
|
68
|
+
when "cookie" then "HTTP_COOKIE"
|
69
|
+
when "authorization" then "HTTP_AUTHORIZATION"
|
70
|
+
when "x-forwarded-for" then "HTTP_X_FORWARDED_FOR"
|
71
|
+
when "x-forwarded-proto" then "HTTP_X_FORWARDED_PROTO"
|
72
|
+
else RACK_HEADER_MAP[k]
|
73
|
+
end
|
74
|
+
] = v
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def respond(
|
80
|
+
_body = nil, _status = 200, _headers = nil,
|
81
|
+
json: nil,
|
82
|
+
html: nil,
|
83
|
+
text: nil,
|
84
|
+
xml: nil,
|
85
|
+
hijack: false,
|
86
|
+
as: nil,
|
87
|
+
status: _status,
|
88
|
+
headers: _headers,
|
89
|
+
body: _body,
|
90
|
+
&blk
|
91
|
+
)
|
92
|
+
|
93
|
+
if json
|
94
|
+
validate!(json, as: as) if as
|
95
|
+
body = json.to_json
|
96
|
+
headers ||= {}
|
97
|
+
headers["Content-Type"] ||= "application/json"
|
98
|
+
elsif html
|
99
|
+
body = html
|
100
|
+
headers ||= {}
|
101
|
+
headers["Content-Type"] ||= "text/html"
|
102
|
+
elsif xml
|
103
|
+
body = xml
|
104
|
+
headers ||= {}
|
105
|
+
headers["Content-Type"] ||= "application/xml"
|
106
|
+
elsif text
|
107
|
+
body = text
|
108
|
+
headers ||= {}
|
109
|
+
headers["Content-Type"] ||= "text/plain"
|
110
|
+
end
|
111
|
+
|
112
|
+
response.respond(status: status, headers: headers, body: body, hijack: hijack, &blk)
|
113
|
+
end
|
114
|
+
|
115
|
+
def hijack
|
116
|
+
self.hijacked = true
|
117
|
+
UNIXSocket.pair.yield_self do |(server_sock, app_sock)|
|
118
|
+
server_sock.autoclose = false
|
119
|
+
self.response.hijack(server_sock.fileno)
|
120
|
+
server_sock.sync = true
|
121
|
+
app_sock.sync = true
|
122
|
+
app_sock
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def build_input_io
|
127
|
+
case body
|
128
|
+
when nil then EMPTY_IO
|
129
|
+
when String then StringIO.new(body)
|
130
|
+
when Array then File.open(body.first, "rb")
|
131
|
+
else body
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def validate!(params, as: nil)
|
136
|
+
as ? apply_schema!(params, as) : params
|
137
|
+
end
|
138
|
+
|
139
|
+
def params(schema=nil)
|
140
|
+
params = case
|
141
|
+
when url_encoded? then URI.decode_www_form(build_input_io.read).to_h
|
142
|
+
when json? then JSON.parse(build_input_io.read)
|
143
|
+
when multipart?
|
144
|
+
Rack::Multipart::Parser.parse(
|
145
|
+
build_input_io,
|
146
|
+
content_length,
|
147
|
+
content_type,
|
148
|
+
Rack::Multipart::Parser::TEMPFILE_FACTORY,
|
149
|
+
Rack::Multipart::Parser::BUFSIZE,
|
150
|
+
Rack::Utils.default_query_parser
|
151
|
+
).params
|
152
|
+
else
|
153
|
+
{}
|
154
|
+
end
|
155
|
+
|
156
|
+
params.merge!(query_params).merge!(url_params)
|
157
|
+
|
158
|
+
yield schema ? apply_schema!(params, schema) : params
|
159
|
+
|
160
|
+
rescue StandardError => e
|
161
|
+
if response.json?
|
162
|
+
respond(json: {error: e.message}, status: 400)
|
163
|
+
else
|
164
|
+
respond(e.message, 400)
|
165
|
+
end
|
166
|
+
ensure
|
167
|
+
clean_temp_files(params)
|
168
|
+
end
|
169
|
+
|
170
|
+
def clean_temp_files(params)
|
171
|
+
case params
|
172
|
+
when Hash
|
173
|
+
if params.key?(:tempfile)
|
174
|
+
params[:tempfile].unlink
|
175
|
+
else
|
176
|
+
params.each_value { |v| clean_temp_files(v) }
|
177
|
+
end
|
178
|
+
when Array then params.each { |v| clean_temp_files(v) }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def query_params
|
183
|
+
URI.decode_www_form(query_string).to_h
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'forwardable'
|
3
|
+
require "stringio"
|
4
|
+
require "socket"
|
5
|
+
|
6
|
+
module Itsi
|
7
|
+
|
8
|
+
class HttpResponse
|
9
|
+
|
10
|
+
def respond _body=nil, _status=200, _header=nil, status: _status, headers: _header, body: _body, hijack: false, &blk
|
11
|
+
self.status = status
|
12
|
+
|
13
|
+
body = body.to_s unless body.is_a?(String)
|
14
|
+
|
15
|
+
if headers
|
16
|
+
headers.each do |key, value|
|
17
|
+
if value.is_a?(Array)
|
18
|
+
value.each { |v| add_header(key, v) }
|
19
|
+
else
|
20
|
+
add_header(key, value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
if body
|
26
|
+
# Common case. Write a single string body.
|
27
|
+
send_and_close(body)
|
28
|
+
elsif block_given?
|
29
|
+
|
30
|
+
# If you call respond with a block, you get a handle to a stream that you can write to.
|
31
|
+
yield self
|
32
|
+
|
33
|
+
# If you hijack the connection, you are responsible for closing it.
|
34
|
+
# Otherwise, the response will be closed automatically.
|
35
|
+
self.close unless hijack
|
36
|
+
else
|
37
|
+
self.close
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|