itsi-scheduler 0.1.0 → 0.1.3
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 +219 -23
- data/Rakefile +7 -1
- data/ext/itsi_error/Cargo.toml +2 -0
- data/ext/itsi_error/src/from.rs +70 -0
- data/ext/itsi_error/src/lib.rs +10 -37
- 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 +2 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
- data/ext/itsi_rb_helpers/src/lib.rs +90 -10
- data/ext/itsi_scheduler/Cargo.toml +9 -1
- data/ext/itsi_scheduler/extconf.rb +1 -1
- 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 +31 -10
- data/ext/itsi_server/Cargo.toml +41 -0
- data/ext/itsi_server/extconf.rb +6 -0
- data/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
- data/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
- data/ext/itsi_server/src/body_proxy/mod.rs +2 -0
- data/ext/itsi_server/src/lib.rs +103 -0
- data/ext/itsi_server/src/request/itsi_request.rs +277 -0
- data/ext/itsi_server/src/request/mod.rs +1 -0
- data/ext/itsi_server/src/response/itsi_response.rs +347 -0
- data/ext/itsi_server/src/response/mod.rs +1 -0
- data/ext/itsi_server/src/server/bind.rs +168 -0
- data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
- data/ext/itsi_server/src/server/io_stream.rs +104 -0
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +13 -0
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +5 -0
- data/ext/itsi_server/src/server/itsi_server.rs +244 -0
- data/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
- data/ext/itsi_server/src/server/listener.rs +275 -0
- data/ext/itsi_server/src/server/mod.rs +11 -0
- data/ext/itsi_server/src/server/process_worker.rs +196 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +241 -0
- data/ext/itsi_server/src/server/signal.rs +70 -0
- data/ext/itsi_server/src/server/thread_worker.rs +368 -0
- data/ext/itsi_server/src/server/tls.rs +152 -0
- data/ext/itsi_tracing/Cargo.toml +4 -0
- data/ext/itsi_tracing/src/lib.rs +36 -6
- data/lib/itsi/scheduler/version.rb +1 -1
- data/lib/itsi/scheduler.rb +137 -1
- metadata +38 -18
@@ -0,0 +1,152 @@
|
|
1
|
+
use base64::{engine::general_purpose, Engine as _};
|
2
|
+
use itsi_error::Result;
|
3
|
+
use itsi_tracing::{info, warn};
|
4
|
+
use rcgen::{CertificateParams, DnType, KeyPair, SanType};
|
5
|
+
use rustls_pemfile::{certs, pkcs8_private_keys};
|
6
|
+
use std::{collections::HashMap, fs, io::BufReader};
|
7
|
+
use tokio_rustls::rustls::{Certificate, PrivateKey, ServerConfig};
|
8
|
+
|
9
|
+
const ITS_CA_CERT: &str = include_str!("./itsi_ca/itsi_ca.crt");
|
10
|
+
const ITS_CA_KEY: &str = include_str!("./itsi_ca/itsi_ca.key");
|
11
|
+
|
12
|
+
// Generates a TLS configuration based on either :
|
13
|
+
// * Input "cert" and "key" options (either paths or Base64-encoded strings) or
|
14
|
+
// * Performs automatic certificate generation/retrieval. Generated certs use an internal self-signed Isti CA.
|
15
|
+
// If a non-local host or optional domain parameter is provided,
|
16
|
+
// an automated certificate will attempt to be fetched using let's encrypt.
|
17
|
+
pub fn configure_tls(host: &str, query_params: &HashMap<String, String>) -> Result<ServerConfig> {
|
18
|
+
info!("TLS Options {:?}", query_params);
|
19
|
+
let (certs, key) = if let (Some(cert_path), Some(key_path)) =
|
20
|
+
(query_params.get("cert"), query_params.get("key"))
|
21
|
+
{
|
22
|
+
// Load from file or Base64
|
23
|
+
let certs = load_certs(cert_path);
|
24
|
+
let key = load_private_key(key_path);
|
25
|
+
(certs, key)
|
26
|
+
} else {
|
27
|
+
let domains_param = query_params
|
28
|
+
.get("domains")
|
29
|
+
.map(|v| v.split(',').map(String::from).collect());
|
30
|
+
let host_string = host.to_string();
|
31
|
+
let domains = domains_param.or_else(|| {
|
32
|
+
if host_string != "localhost" {
|
33
|
+
Some(vec![host_string])
|
34
|
+
} else {
|
35
|
+
None
|
36
|
+
}
|
37
|
+
});
|
38
|
+
|
39
|
+
if let Some(domains) = domains {
|
40
|
+
retrieve_acme_cert(domains)?
|
41
|
+
} else {
|
42
|
+
generate_ca_signed_cert(vec![host.to_owned()])?
|
43
|
+
}
|
44
|
+
};
|
45
|
+
|
46
|
+
let mut config = ServerConfig::builder()
|
47
|
+
.with_safe_defaults()
|
48
|
+
.with_no_client_auth()
|
49
|
+
.with_single_cert(certs, key)
|
50
|
+
.expect("Failed to build TLS config");
|
51
|
+
|
52
|
+
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
53
|
+
Ok(config)
|
54
|
+
}
|
55
|
+
|
56
|
+
pub fn load_certs(path: &str) -> Vec<Certificate> {
|
57
|
+
let data = if let Some(stripped) = path.strip_prefix("base64:") {
|
58
|
+
general_purpose::STANDARD
|
59
|
+
.decode(stripped)
|
60
|
+
.expect("Invalid base64 certificate")
|
61
|
+
} else {
|
62
|
+
fs::read(path).expect("Failed to read certificate file")
|
63
|
+
};
|
64
|
+
|
65
|
+
if data.starts_with(b"-----BEGIN ") {
|
66
|
+
let mut reader = BufReader::new(&data[..]);
|
67
|
+
let certs_der: Vec<Vec<u8>> = certs(&mut reader)
|
68
|
+
.map(|r| {
|
69
|
+
r.map(|der| der.as_ref().to_vec())
|
70
|
+
.map_err(itsi_error::ItsiError::from)
|
71
|
+
})
|
72
|
+
.collect::<Result<_>>()
|
73
|
+
.expect("Failed to parse certificate file");
|
74
|
+
certs_der.into_iter().map(Certificate).collect()
|
75
|
+
} else {
|
76
|
+
vec![Certificate(data)]
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
/// Loads a private key from a file or Base64.
|
81
|
+
pub fn load_private_key(path: &str) -> PrivateKey {
|
82
|
+
let key_data = if let Some(stripped) = path.strip_prefix("base64:") {
|
83
|
+
general_purpose::STANDARD
|
84
|
+
.decode(stripped)
|
85
|
+
.expect("Invalid base64 private key")
|
86
|
+
} else {
|
87
|
+
fs::read(path).expect("Failed to read private key file")
|
88
|
+
};
|
89
|
+
|
90
|
+
if key_data.starts_with(b"-----BEGIN ") {
|
91
|
+
let mut reader = BufReader::new(&key_data[..]);
|
92
|
+
let keys: Vec<Vec<u8>> = pkcs8_private_keys(&mut reader)
|
93
|
+
.map(|r| {
|
94
|
+
r.map(|key| key.secret_pkcs8_der().to_vec())
|
95
|
+
.map_err(itsi_error::ItsiError::from)
|
96
|
+
})
|
97
|
+
.collect::<Result<_>>()
|
98
|
+
.expect("Failed to parse private key");
|
99
|
+
if !keys.is_empty() {
|
100
|
+
return PrivateKey(keys[0].clone());
|
101
|
+
}
|
102
|
+
}
|
103
|
+
PrivateKey(key_data)
|
104
|
+
}
|
105
|
+
|
106
|
+
pub fn generate_ca_signed_cert(domains: Vec<String>) -> Result<(Vec<Certificate>, PrivateKey)> {
|
107
|
+
info!("Generating New Itsi CA - Self signed Certificate. Use `itsi ca export` to export the CA certificate for import into your local trust store.");
|
108
|
+
|
109
|
+
let ca_kp = KeyPair::from_pem(ITS_CA_KEY).expect("Failed to load embedded CA key");
|
110
|
+
let ca_cert = CertificateParams::from_ca_cert_pem(ITS_CA_CERT)
|
111
|
+
.expect("Failed to parse embedded CA certificate")
|
112
|
+
.self_signed(&ca_kp)
|
113
|
+
.expect("Failed to self-sign embedded CA cert");
|
114
|
+
|
115
|
+
let ee_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
|
116
|
+
let mut ee_params = CertificateParams::default();
|
117
|
+
|
118
|
+
info!(
|
119
|
+
"Generated certificate will be valid for domains {:?}",
|
120
|
+
domains
|
121
|
+
);
|
122
|
+
use std::net::IpAddr;
|
123
|
+
|
124
|
+
ee_params.subject_alt_names = domains
|
125
|
+
.iter()
|
126
|
+
.map(|domain| {
|
127
|
+
if let Ok(ip) = domain.parse::<IpAddr>() {
|
128
|
+
SanType::IpAddress(ip)
|
129
|
+
} else {
|
130
|
+
SanType::DnsName(domain.clone().try_into().unwrap())
|
131
|
+
}
|
132
|
+
})
|
133
|
+
.collect();
|
134
|
+
|
135
|
+
ee_params
|
136
|
+
.distinguished_name
|
137
|
+
.push(DnType::CommonName, domains[0].clone());
|
138
|
+
|
139
|
+
ee_params.use_authority_key_identifier_extension = true;
|
140
|
+
|
141
|
+
let ee_cert = ee_params.signed_by(&ee_key, &ca_cert, &ca_kp).unwrap();
|
142
|
+
let ee_cert_der = ee_cert.der().to_vec();
|
143
|
+
let ee_cert = Certificate(ee_cert_der);
|
144
|
+
let ca_cert = Certificate(ca_cert.der().to_vec());
|
145
|
+
Ok((vec![ee_cert, ca_cert], PrivateKey(ee_key.serialize_der())))
|
146
|
+
}
|
147
|
+
|
148
|
+
/// TODO: Retrieves an ACME certificate for a given domain.
|
149
|
+
pub fn retrieve_acme_cert(domains: Vec<String>) -> Result<(Vec<Certificate>, PrivateKey)> {
|
150
|
+
warn!("Retrieving ACME cert for {}", domains.join(", "));
|
151
|
+
generate_ca_signed_cert(domains)
|
152
|
+
}
|
data/ext/itsi_tracing/Cargo.toml
CHANGED
data/ext/itsi_tracing/src/lib.rs
CHANGED
@@ -1,11 +1,41 @@
|
|
1
|
+
use std::env;
|
2
|
+
|
3
|
+
use atty::{Stream, is};
|
1
4
|
pub use tracing::{debug, error, info, trace, warn};
|
2
|
-
use
|
5
|
+
pub use tracing_attributes::instrument; // Explicitly export from tracing-attributes
|
6
|
+
use tracing_subscriber::{
|
7
|
+
EnvFilter,
|
8
|
+
fmt::{self, format},
|
9
|
+
};
|
3
10
|
|
11
|
+
#[instrument]
|
4
12
|
pub fn init() {
|
5
|
-
let env_filter = EnvFilter::
|
6
|
-
|
7
|
-
|
8
|
-
.
|
13
|
+
let env_filter = EnvFilter::builder()
|
14
|
+
.with_env_var("ITSI_LOG")
|
15
|
+
.try_from_env()
|
16
|
+
.unwrap_or_else(|_| EnvFilter::new("info"));
|
17
|
+
|
18
|
+
let format = fmt::format()
|
19
|
+
.compact()
|
20
|
+
.with_file(false)
|
21
|
+
.with_level(true)
|
22
|
+
.with_line_number(false)
|
23
|
+
.with_source_location(false)
|
24
|
+
.with_target(false)
|
25
|
+
.with_thread_ids(false);
|
26
|
+
|
27
|
+
let is_tty = is(Stream::Stdout);
|
28
|
+
|
29
|
+
let subscriber = tracing_subscriber::fmt()
|
9
30
|
.event_format(format)
|
10
|
-
.
|
31
|
+
.with_env_filter(env_filter);
|
32
|
+
|
33
|
+
if (is_tty && env::var("ITSI_LOG_PLAIN").is_err()) || env::var("ITSI_LOG_ANSI").is_ok() {
|
34
|
+
subscriber.with_ansi(true).init();
|
35
|
+
} else {
|
36
|
+
subscriber
|
37
|
+
.fmt_fields(format::JsonFields::default())
|
38
|
+
.event_format(fmt::format().json())
|
39
|
+
.init();
|
40
|
+
}
|
11
41
|
}
|
data/lib/itsi/scheduler.rb
CHANGED
@@ -6,6 +6,142 @@ require_relative "scheduler/itsi_scheduler"
|
|
6
6
|
module Itsi
|
7
7
|
class Scheduler
|
8
8
|
class Error < StandardError; end
|
9
|
-
|
9
|
+
|
10
|
+
def self.resume_token
|
11
|
+
@resume_token ||= 0
|
12
|
+
@resume_token += 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@join_waiters = {}.compare_by_identity
|
17
|
+
@token_map = {}.compare_by_identity
|
18
|
+
@resume_tokens = {}.compare_by_identity
|
19
|
+
@unblocked = [[], []]
|
20
|
+
@unblock_idx = 0
|
21
|
+
@unblocked_mux = Mutex.new
|
22
|
+
@resume_fiber = method(:resume_fiber).to_proc
|
23
|
+
@resume_fiber_with_readiness = method(:resume_fiber_with_readiness).to_proc
|
24
|
+
@resume_blocked = method(:resume_blocked).to_proc
|
25
|
+
end
|
26
|
+
|
27
|
+
def block(_, timeout, fiber = Fiber.current, token = Scheduler.resume_token)
|
28
|
+
@join_waiters[fiber] = true
|
29
|
+
|
30
|
+
start_timer(timeout, token) if timeout
|
31
|
+
@resume_tokens[token] = fiber
|
32
|
+
@token_map[fiber] = token
|
33
|
+
Fiber.yield
|
34
|
+
ensure
|
35
|
+
@resume_tokens.delete(token)
|
36
|
+
@token_map.delete(fiber)
|
37
|
+
@join_waiters.delete(fiber)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Register an IO waiter.
|
41
|
+
# This will get resumed by our scheduler inside the call to
|
42
|
+
# fetch_events.
|
43
|
+
def io_wait(io, events, duration)
|
44
|
+
fiber = Fiber.current
|
45
|
+
token = Scheduler.resume_token
|
46
|
+
readiness = register_io_wait(io.fileno, events, duration, token)
|
47
|
+
readiness || block(nil, duration, fiber, token)
|
48
|
+
end
|
49
|
+
|
50
|
+
def unblock(_blocker, fiber)
|
51
|
+
@unblocked_mux.synchronize do
|
52
|
+
@unblocked[@unblock_idx] << fiber
|
53
|
+
end
|
54
|
+
wake
|
55
|
+
end
|
56
|
+
|
57
|
+
def kernel_sleep(duration)
|
58
|
+
block nil, duration
|
59
|
+
end
|
60
|
+
|
61
|
+
def tick
|
62
|
+
events = fetch_due_events
|
63
|
+
timers = fetch_due_timers
|
64
|
+
unblocked = switch_unblock_batch
|
65
|
+
events&.each(&@resume_fiber_with_readiness)
|
66
|
+
unblocked.each(&@resume_blocked)
|
67
|
+
unblocked.clear
|
68
|
+
timers&.each(&@resume_fiber)
|
69
|
+
end
|
70
|
+
|
71
|
+
def resume_fiber(token)
|
72
|
+
if (fiber = @resume_tokens.delete(token))
|
73
|
+
fiber.resume
|
74
|
+
end
|
75
|
+
rescue StandardError => e
|
76
|
+
warn "Failed to resume fiber #{fiber}: #{e.message}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def resume_fiber_with_readiness((token, readiness))
|
80
|
+
if (fiber = @resume_tokens.delete(token))
|
81
|
+
fiber.resume(readiness)
|
82
|
+
end
|
83
|
+
rescue StandardError => e
|
84
|
+
warn "Failed to resume fiber #{fiber}: #{e.message}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def resume_blocked(fiber)
|
88
|
+
if (token = @token_map[fiber])
|
89
|
+
resume_fiber(token)
|
90
|
+
elsif fiber.alive?
|
91
|
+
fiber.resume
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def switch_unblock_batch
|
96
|
+
@unblocked_mux.synchronize do
|
97
|
+
current = @unblocked[@unblock_idx]
|
98
|
+
@unblock_idx = (@unblock_idx + 1) % 2
|
99
|
+
current
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Yields upwards to the scheduler, with an intention to
|
104
|
+
# resume the fiber that yielded ASAP.
|
105
|
+
def yield
|
106
|
+
kernel_sleep(0) if work?
|
107
|
+
end
|
108
|
+
|
109
|
+
# Keep running until we've got no timers we're awaiting, no pending IO, no temporary yields,
|
110
|
+
# no pending unblocks.
|
111
|
+
def work?
|
112
|
+
!@unblocked[@unblock_idx].empty? || !@join_waiters.empty? || has_pending_io?
|
113
|
+
end
|
114
|
+
|
115
|
+
# Run until no more work needs doing.
|
116
|
+
def run
|
117
|
+
tick while work?
|
118
|
+
debug "Exit Scheduler"
|
119
|
+
end
|
120
|
+
|
121
|
+
# Hook invoked at the end of the thread.
|
122
|
+
# Will start our scheduler's Reactor.
|
123
|
+
def scheduler_close
|
124
|
+
run
|
125
|
+
ensure
|
126
|
+
@closed ||= true
|
127
|
+
freeze
|
128
|
+
end
|
129
|
+
|
130
|
+
# Need to defer to Process::Status rather than our extension
|
131
|
+
# as we don't have a means of creating our own Process::Status.
|
132
|
+
def process_wait(pid, flags)
|
133
|
+
Thread.new do
|
134
|
+
Process::Status.wait(pid, flags)
|
135
|
+
end.value
|
136
|
+
end
|
137
|
+
|
138
|
+
def closed?
|
139
|
+
@closed
|
140
|
+
end
|
141
|
+
|
142
|
+
# Spin up a new fiber and immediately resume it.
|
143
|
+
def fiber(&blk)
|
144
|
+
Fiber.new(blocking: false, &blk).tap(&:resume)
|
145
|
+
end
|
10
146
|
end
|
11
147
|
end
|
metadata
CHANGED
@@ -1,28 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: itsi-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-03-
|
10
|
+
date: 2025-03-14 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
|
-
- !ruby/object:Gem::Dependency
|
13
|
-
name: libclang
|
14
|
-
requirement: !ruby/object:Gem::Requirement
|
15
|
-
requirements:
|
16
|
-
- - "~>"
|
17
|
-
- !ruby/object:Gem::Version
|
18
|
-
version: '14.0'
|
19
|
-
type: :runtime
|
20
|
-
prerelease: false
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
-
requirements:
|
23
|
-
- - "~>"
|
24
|
-
- !ruby/object:Gem::Version
|
25
|
-
version: '14.0'
|
26
12
|
- !ruby/object:Gem::Dependency
|
27
13
|
name: rb_sys
|
28
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -56,13 +42,47 @@ files:
|
|
56
42
|
- Rakefile
|
57
43
|
- ext/itsi_error/Cargo.lock
|
58
44
|
- ext/itsi_error/Cargo.toml
|
45
|
+
- ext/itsi_error/src/from.rs
|
59
46
|
- ext/itsi_error/src/lib.rs
|
47
|
+
- ext/itsi_instrument_entry/Cargo.toml
|
48
|
+
- ext/itsi_instrument_entry/src/lib.rs
|
60
49
|
- ext/itsi_rb_helpers/Cargo.lock
|
61
50
|
- ext/itsi_rb_helpers/Cargo.toml
|
51
|
+
- ext/itsi_rb_helpers/src/heap_value.rs
|
62
52
|
- ext/itsi_rb_helpers/src/lib.rs
|
63
53
|
- ext/itsi_scheduler/Cargo.toml
|
64
54
|
- ext/itsi_scheduler/extconf.rb
|
55
|
+
- ext/itsi_scheduler/src/itsi_scheduler.rs
|
56
|
+
- ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs
|
57
|
+
- ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs
|
58
|
+
- ext/itsi_scheduler/src/itsi_scheduler/timer.rs
|
65
59
|
- ext/itsi_scheduler/src/lib.rs
|
60
|
+
- ext/itsi_server/Cargo.toml
|
61
|
+
- ext/itsi_server/extconf.rb
|
62
|
+
- ext/itsi_server/src/body_proxy/big_bytes.rs
|
63
|
+
- ext/itsi_server/src/body_proxy/itsi_body_proxy.rs
|
64
|
+
- ext/itsi_server/src/body_proxy/mod.rs
|
65
|
+
- ext/itsi_server/src/lib.rs
|
66
|
+
- ext/itsi_server/src/request/itsi_request.rs
|
67
|
+
- ext/itsi_server/src/request/mod.rs
|
68
|
+
- ext/itsi_server/src/response/itsi_response.rs
|
69
|
+
- ext/itsi_server/src/response/mod.rs
|
70
|
+
- ext/itsi_server/src/server/bind.rs
|
71
|
+
- ext/itsi_server/src/server/bind_protocol.rs
|
72
|
+
- ext/itsi_server/src/server/io_stream.rs
|
73
|
+
- ext/itsi_server/src/server/itsi_ca/itsi_ca.crt
|
74
|
+
- ext/itsi_server/src/server/itsi_ca/itsi_ca.key
|
75
|
+
- ext/itsi_server/src/server/itsi_server.rs
|
76
|
+
- ext/itsi_server/src/server/lifecycle_event.rs
|
77
|
+
- ext/itsi_server/src/server/listener.rs
|
78
|
+
- ext/itsi_server/src/server/mod.rs
|
79
|
+
- ext/itsi_server/src/server/process_worker.rs
|
80
|
+
- ext/itsi_server/src/server/serve_strategy/cluster_mode.rs
|
81
|
+
- ext/itsi_server/src/server/serve_strategy/mod.rs
|
82
|
+
- ext/itsi_server/src/server/serve_strategy/single_mode.rs
|
83
|
+
- ext/itsi_server/src/server/signal.rs
|
84
|
+
- ext/itsi_server/src/server/thread_worker.rs
|
85
|
+
- ext/itsi_server/src/server/tls.rs
|
66
86
|
- ext/itsi_tracing/Cargo.lock
|
67
87
|
- ext/itsi_tracing/Cargo.toml
|
68
88
|
- ext/itsi_tracing/src/lib.rs
|
@@ -83,12 +103,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
103
|
requirements:
|
84
104
|
- - ">="
|
85
105
|
- !ruby/object:Gem::Version
|
86
|
-
version: 3.
|
106
|
+
version: 3.0.0
|
87
107
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
108
|
requirements:
|
89
109
|
- - ">="
|
90
110
|
- !ruby/object:Gem::Version
|
91
|
-
version: 3.
|
111
|
+
version: 3.1.11
|
92
112
|
requirements: []
|
93
113
|
rubygems_version: 3.6.2
|
94
114
|
specification_version: 4
|