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
@@ -0,0 +1,74 @@
|
|
1
|
+
use async_trait::async_trait;
|
2
|
+
use redis::aio::ConnectionManager;
|
3
|
+
use redis::{Client, RedisError, Script};
|
4
|
+
use std::sync::Arc;
|
5
|
+
use std::time::Duration;
|
6
|
+
|
7
|
+
#[derive(Debug)]
|
8
|
+
pub enum CacheError {
|
9
|
+
RedisError(RedisError),
|
10
|
+
// Other error variants as needed.
|
11
|
+
}
|
12
|
+
/// A general-purpose cache trait with an atomic “increment with timeout” operation.
|
13
|
+
#[async_trait]
|
14
|
+
pub trait CacheStore: Send + Sync + std::fmt::Debug {
|
15
|
+
/// Increments the counter associated with `key` and sets (or extends) its expiration.
|
16
|
+
/// Returns the new counter value.
|
17
|
+
async fn increment(&self, key: &str, timeout: Duration) -> Result<u64, CacheError>;
|
18
|
+
}
|
19
|
+
|
20
|
+
/// A Redis-backed cache store using an async connection manager.
|
21
|
+
/// This uses a TLS-enabled connection when the URL is prefixed with "rediss://".
|
22
|
+
#[derive(Clone)]
|
23
|
+
pub struct RedisCacheStore {
|
24
|
+
connection: Arc<ConnectionManager>,
|
25
|
+
}
|
26
|
+
|
27
|
+
impl std::fmt::Debug for RedisCacheStore {
|
28
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
29
|
+
f.debug_struct("RedisCacheStore").finish()
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
impl RedisCacheStore {
|
34
|
+
/// Constructs a new RedisCacheStore.
|
35
|
+
///
|
36
|
+
/// Use a connection URL like "rediss://host:port" to enable TLS (with rustls under the hood).
|
37
|
+
/// This constructor is async because it sets up the connection manager.
|
38
|
+
pub async fn new(connection_url: &str) -> Result<Self, CacheError> {
|
39
|
+
let client = Client::open(connection_url).map_err(CacheError::RedisError)?;
|
40
|
+
let connection_manager = ConnectionManager::new(client)
|
41
|
+
.await
|
42
|
+
.map_err(CacheError::RedisError)?;
|
43
|
+
Ok(Self {
|
44
|
+
connection: Arc::new(connection_manager),
|
45
|
+
})
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
#[async_trait]
|
50
|
+
impl CacheStore for RedisCacheStore {
|
51
|
+
async fn increment(&self, key: &str, timeout: Duration) -> Result<u64, CacheError> {
|
52
|
+
let timeout_secs = timeout.as_secs();
|
53
|
+
// Lua script to:
|
54
|
+
// 1. INCR the key.
|
55
|
+
// 2. If the key doesn't have a TTL, set it.
|
56
|
+
let script = r#"
|
57
|
+
local current = redis.call('INCR', KEYS[1])
|
58
|
+
if redis.call('TTL', KEYS[1]) < 0 then
|
59
|
+
redis.call('EXPIRE', KEYS[1], ARGV[1])
|
60
|
+
end
|
61
|
+
return current
|
62
|
+
"#;
|
63
|
+
let script = Script::new(script);
|
64
|
+
// The ConnectionManager is cloneable and can be used concurrently.
|
65
|
+
let mut connection = (*self.connection).clone();
|
66
|
+
let value: i64 = script
|
67
|
+
.key(key)
|
68
|
+
.arg(timeout_secs)
|
69
|
+
.invoke_async(&mut connection)
|
70
|
+
.await
|
71
|
+
.map_err(CacheError::RedisError)?;
|
72
|
+
Ok(value as u64)
|
73
|
+
}
|
74
|
+
}
|
@@ -0,0 +1,172 @@
|
|
1
|
+
use super::listener::ListenerInfo;
|
2
|
+
use super::middleware_stack::CompressionAlgorithm;
|
3
|
+
use super::middleware_stack::MiddlewareLayer;
|
4
|
+
use super::request_job::RequestJob;
|
5
|
+
use super::serve_strategy::single_mode::RunningPhase;
|
6
|
+
use super::types::HttpRequest;
|
7
|
+
use super::types::HttpResponse;
|
8
|
+
use crate::ruby_types::itsi_server::itsi_server_config::ServerParams;
|
9
|
+
use chrono;
|
10
|
+
use chrono::Local;
|
11
|
+
use either::Either;
|
12
|
+
use hyper::service::Service;
|
13
|
+
use itsi_error::ItsiError;
|
14
|
+
use regex::Regex;
|
15
|
+
use std::sync::OnceLock;
|
16
|
+
use std::{future::Future, ops::Deref, pin::Pin, sync::Arc};
|
17
|
+
use tokio::sync::watch::{self};
|
18
|
+
|
19
|
+
#[derive(Clone)]
|
20
|
+
pub struct ItsiService {
|
21
|
+
pub inner: Arc<IstiServiceInner>,
|
22
|
+
}
|
23
|
+
|
24
|
+
impl Deref for ItsiService {
|
25
|
+
type Target = Arc<IstiServiceInner>;
|
26
|
+
|
27
|
+
fn deref(&self) -> &Self::Target {
|
28
|
+
&self.inner
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
pub struct IstiServiceInner {
|
33
|
+
pub sender: async_channel::Sender<RequestJob>,
|
34
|
+
pub server_params: Arc<ServerParams>,
|
35
|
+
pub listener: Arc<ListenerInfo>,
|
36
|
+
pub addr: String,
|
37
|
+
pub shutdown_channel: watch::Receiver<RunningPhase>,
|
38
|
+
}
|
39
|
+
|
40
|
+
#[derive(Clone)]
|
41
|
+
pub struct RequestContext {
|
42
|
+
inner: Arc<RequestContextInner>,
|
43
|
+
}
|
44
|
+
|
45
|
+
impl Deref for RequestContext {
|
46
|
+
type Target = Arc<RequestContextInner>;
|
47
|
+
|
48
|
+
fn deref(&self) -> &Self::Target {
|
49
|
+
&self.inner
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
impl Deref for RequestContextInner {
|
54
|
+
type Target = ItsiService;
|
55
|
+
|
56
|
+
fn deref(&self) -> &Self::Target {
|
57
|
+
&self.service
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
pub struct RequestContextInner {
|
62
|
+
pub request_id: i128,
|
63
|
+
pub service: ItsiService,
|
64
|
+
pub matching_pattern: Option<Arc<Regex>>,
|
65
|
+
pub compression_method: OnceLock<CompressionAlgorithm>,
|
66
|
+
pub origin: OnceLock<Option<String>>,
|
67
|
+
pub start_time: chrono::DateTime<chrono::Utc>,
|
68
|
+
pub request: Option<Arc<HttpRequest>>,
|
69
|
+
pub request_start_time: OnceLock<chrono::DateTime<Local>>,
|
70
|
+
pub if_none_match: OnceLock<Option<String>>,
|
71
|
+
pub etag_value: OnceLock<Option<String>>,
|
72
|
+
}
|
73
|
+
|
74
|
+
impl RequestContext {
|
75
|
+
fn new(service: ItsiService, matching_pattern: Option<Arc<Regex>>) -> Self {
|
76
|
+
RequestContext {
|
77
|
+
inner: Arc::new(RequestContextInner {
|
78
|
+
request_id: rand::random::<i128>(),
|
79
|
+
service,
|
80
|
+
matching_pattern,
|
81
|
+
compression_method: OnceLock::new(),
|
82
|
+
origin: OnceLock::new(),
|
83
|
+
start_time: chrono::Utc::now(),
|
84
|
+
request: None,
|
85
|
+
request_start_time: OnceLock::new(),
|
86
|
+
if_none_match: OnceLock::new(),
|
87
|
+
etag_value: OnceLock::new(),
|
88
|
+
}),
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
pub fn set_compression_method(&self, method: CompressionAlgorithm) {
|
93
|
+
self.inner.compression_method.set(method).unwrap();
|
94
|
+
}
|
95
|
+
|
96
|
+
pub fn set_origin(&self, origin: Option<String>) {
|
97
|
+
self.inner.origin.set(origin).unwrap();
|
98
|
+
}
|
99
|
+
|
100
|
+
pub fn set_if_none_match(&self, value: Option<String>) {
|
101
|
+
self.inner.if_none_match.set(value).unwrap();
|
102
|
+
}
|
103
|
+
|
104
|
+
pub fn get_if_none_match(&self) -> Option<String> {
|
105
|
+
self.inner.if_none_match.get().cloned().flatten()
|
106
|
+
}
|
107
|
+
|
108
|
+
pub fn request_id(&self) -> String {
|
109
|
+
self.inner.request_id.to_string()
|
110
|
+
}
|
111
|
+
|
112
|
+
pub fn track_start_time(&self) {
|
113
|
+
self.inner
|
114
|
+
.request_start_time
|
115
|
+
.get_or_init(chrono::Local::now);
|
116
|
+
}
|
117
|
+
|
118
|
+
pub fn start_time(&self) -> Option<chrono::DateTime<Local>> {
|
119
|
+
self.inner.request_start_time.get().cloned()
|
120
|
+
}
|
121
|
+
|
122
|
+
pub fn get_response_time(&self) -> Option<chrono::TimeDelta> {
|
123
|
+
self.inner
|
124
|
+
.request_start_time
|
125
|
+
.get()
|
126
|
+
.map(|instant| Local::now() - instant)
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
impl Service<HttpRequest> for ItsiService {
|
131
|
+
type Response = HttpResponse;
|
132
|
+
type Error = ItsiError;
|
133
|
+
type Future = Pin<Box<dyn Future<Output = itsi_error::Result<HttpResponse>> + Send>>;
|
134
|
+
|
135
|
+
// This is called once per incoming Request.
|
136
|
+
fn call(&self, req: HttpRequest) -> Self::Future {
|
137
|
+
let params = self.server_params.clone();
|
138
|
+
let self_clone = self.clone();
|
139
|
+
Box::pin(async move {
|
140
|
+
let mut req = req;
|
141
|
+
let mut resp: Option<HttpResponse> = None;
|
142
|
+
let (stack, matching_pattern) = params.middleware.get().unwrap().stack_for(&req);
|
143
|
+
let mut context = RequestContext::new(self_clone, matching_pattern);
|
144
|
+
let mut depth = 0;
|
145
|
+
for (index, elm) in stack.iter().enumerate() {
|
146
|
+
match elm.before(req, &mut context).await {
|
147
|
+
Ok(Either::Left(r)) => req = r,
|
148
|
+
Ok(Either::Right(r)) => {
|
149
|
+
resp = Some(r);
|
150
|
+
depth = index;
|
151
|
+
break;
|
152
|
+
}
|
153
|
+
Err(e) => return Err(e.into()),
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
let mut resp = match resp {
|
158
|
+
Some(r) => r,
|
159
|
+
None => {
|
160
|
+
return Err(ItsiError::InternalServerError(
|
161
|
+
"No response returned from middleware stack".to_string(),
|
162
|
+
))
|
163
|
+
}
|
164
|
+
};
|
165
|
+
for elm in stack.iter().rev().skip(stack.len() - depth - 1) {
|
166
|
+
resp = elm.after(resp, &mut context).await;
|
167
|
+
}
|
168
|
+
|
169
|
+
Ok(resp)
|
170
|
+
})
|
171
|
+
}
|
172
|
+
}
|
@@ -1,16 +1,20 @@
|
|
1
1
|
use super::bind::{Bind, BindAddress};
|
2
2
|
use super::bind_protocol::BindProtocol;
|
3
3
|
use super::io_stream::IoStream;
|
4
|
+
use super::serve_strategy::single_mode::RunningPhase;
|
4
5
|
use super::tls::ItsiTlsAcceptor;
|
5
6
|
use itsi_error::{ItsiError, Result};
|
6
7
|
use itsi_tracing::info;
|
7
8
|
use socket2::{Domain, Protocol, Socket, Type};
|
9
|
+
use std::fmt::Display;
|
8
10
|
use std::net::{IpAddr, SocketAddr, TcpListener};
|
11
|
+
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
9
12
|
use std::sync::Arc;
|
10
13
|
use std::{os::unix::net::UnixListener, path::PathBuf};
|
11
14
|
use tokio::net::TcpListener as TokioTcpListener;
|
12
15
|
use tokio::net::UnixListener as TokioUnixListener;
|
13
16
|
use tokio::net::{unix, TcpStream, UnixStream};
|
17
|
+
use tokio::sync::watch::Receiver;
|
14
18
|
use tokio_rustls::TlsAcceptor;
|
15
19
|
use tokio_stream::StreamExt;
|
16
20
|
use tracing::error;
|
@@ -23,45 +27,79 @@ pub(crate) enum Listener {
|
|
23
27
|
}
|
24
28
|
|
25
29
|
pub(crate) enum TokioListener {
|
26
|
-
Tcp
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
Unix {
|
38
|
-
listener: TokioUnixListener,
|
39
|
-
},
|
40
|
-
UnixTls {
|
41
|
-
listener: TokioUnixListener,
|
42
|
-
acceptor: ItsiTlsAcceptor,
|
43
|
-
},
|
30
|
+
Tcp(TokioTcpListener),
|
31
|
+
TcpTls(TokioTcpListener, ItsiTlsAcceptor),
|
32
|
+
Unix(TokioUnixListener),
|
33
|
+
UnixTls(TokioUnixListener, ItsiTlsAcceptor),
|
34
|
+
}
|
35
|
+
|
36
|
+
#[derive(Debug, Clone)]
|
37
|
+
pub struct ListenerInfo {
|
38
|
+
pub host: String,
|
39
|
+
pub port: u16,
|
40
|
+
pub scheme: String,
|
44
41
|
}
|
45
42
|
|
46
43
|
impl TokioListener {
|
47
|
-
pub fn
|
44
|
+
pub fn listener_info(&self) -> ListenerInfo {
|
48
45
|
match self {
|
49
|
-
TokioListener::Tcp
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
46
|
+
TokioListener::Tcp(listener) => ListenerInfo {
|
47
|
+
host: listener
|
48
|
+
.local_addr()
|
49
|
+
.unwrap()
|
50
|
+
.ip()
|
51
|
+
.to_canonical()
|
52
|
+
.to_string(),
|
53
|
+
port: listener.local_addr().unwrap().port(),
|
54
|
+
scheme: "http".to_string(),
|
55
|
+
},
|
56
|
+
TokioListener::TcpTls(listener, _) => ListenerInfo {
|
57
|
+
host: listener
|
58
|
+
.local_addr()
|
59
|
+
.unwrap()
|
60
|
+
.ip()
|
61
|
+
.to_canonical()
|
62
|
+
.to_string(),
|
63
|
+
port: listener.local_addr().unwrap().port(),
|
64
|
+
scheme: "https".to_string(),
|
65
|
+
},
|
66
|
+
TokioListener::Unix(listener) => ListenerInfo {
|
67
|
+
host: listener
|
68
|
+
.local_addr()
|
69
|
+
.unwrap()
|
70
|
+
.as_pathname()
|
71
|
+
.unwrap()
|
72
|
+
.to_str()
|
73
|
+
.unwrap()
|
74
|
+
.to_owned(),
|
75
|
+
port: 0,
|
76
|
+
scheme: "unix".to_string(),
|
77
|
+
},
|
78
|
+
TokioListener::UnixTls(listener, _) => ListenerInfo {
|
79
|
+
host: listener
|
80
|
+
.local_addr()
|
81
|
+
.unwrap()
|
82
|
+
.as_pathname()
|
83
|
+
.unwrap()
|
84
|
+
.to_str()
|
85
|
+
.unwrap()
|
86
|
+
.to_owned(),
|
87
|
+
port: 0,
|
88
|
+
scheme: "ssl".to_string(),
|
89
|
+
},
|
90
|
+
}
|
54
91
|
}
|
92
|
+
|
55
93
|
pub(crate) async fn accept(&self) -> Result<IoStream> {
|
56
94
|
match self {
|
57
|
-
TokioListener::Tcp
|
58
|
-
TokioListener::TcpTls {
|
59
|
-
listener, acceptor
|
60
|
-
}
|
61
|
-
TokioListener::Unix
|
62
|
-
TokioListener::UnixTls {
|
63
|
-
listener, acceptor
|
64
|
-
}
|
95
|
+
TokioListener::Tcp(listener) => TokioListener::accept_tcp(listener).await,
|
96
|
+
TokioListener::TcpTls(listener, acceptor) => {
|
97
|
+
TokioListener::accept_tls(listener, acceptor).await
|
98
|
+
}
|
99
|
+
TokioListener::Unix(listener) => TokioListener::accept_unix(listener).await,
|
100
|
+
TokioListener::UnixTls(listener, acceptor) => {
|
101
|
+
TokioListener::accept_unix_tls(listener, acceptor).await
|
102
|
+
}
|
65
103
|
}
|
66
104
|
}
|
67
105
|
|
@@ -70,17 +108,24 @@ impl TokioListener {
|
|
70
108
|
Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
|
71
109
|
}
|
72
110
|
|
73
|
-
pub async fn spawn_state_task(&self) {
|
74
|
-
if let TokioListener::TcpTls
|
75
|
-
|
76
|
-
|
77
|
-
|
111
|
+
pub async fn spawn_state_task(&self, mut shutdown_receiver: Receiver<RunningPhase>) {
|
112
|
+
if let TokioListener::TcpTls(
|
113
|
+
_,
|
114
|
+
ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
|
115
|
+
) = self
|
78
116
|
{
|
79
117
|
let mut state = state.lock().await;
|
80
118
|
loop {
|
81
|
-
|
82
|
-
|
83
|
-
|
119
|
+
tokio::select! {
|
120
|
+
stream_event = StreamExt::next(&mut *state) => {
|
121
|
+
match stream_event {
|
122
|
+
Some(event) => info!("ACME Event: {:?}", event),
|
123
|
+
None => error!("Received no acme event"),
|
124
|
+
}
|
125
|
+
},
|
126
|
+
_ = shutdown_receiver.changed() => {
|
127
|
+
break;
|
128
|
+
}
|
84
129
|
}
|
85
130
|
}
|
86
131
|
}
|
@@ -175,33 +220,6 @@ impl TokioListener {
|
|
175
220
|
},
|
176
221
|
}
|
177
222
|
}
|
178
|
-
|
179
|
-
pub(crate) fn scheme(&self) -> String {
|
180
|
-
match self {
|
181
|
-
TokioListener::Tcp { .. } => "http".to_string(),
|
182
|
-
TokioListener::TcpTls { .. } => "https".to_string(),
|
183
|
-
TokioListener::Unix { .. } => "http".to_string(),
|
184
|
-
TokioListener::UnixTls { .. } => "https".to_string(),
|
185
|
-
}
|
186
|
-
}
|
187
|
-
|
188
|
-
pub(crate) fn port(&self) -> u16 {
|
189
|
-
match self {
|
190
|
-
TokioListener::Tcp { port, .. } => *port,
|
191
|
-
TokioListener::TcpTls { port, .. } => *port,
|
192
|
-
TokioListener::Unix { .. } => 0,
|
193
|
-
TokioListener::UnixTls { .. } => 0,
|
194
|
-
}
|
195
|
-
}
|
196
|
-
|
197
|
-
pub(crate) fn host(&self) -> String {
|
198
|
-
match self {
|
199
|
-
TokioListener::Tcp { host, .. } => host.to_string(),
|
200
|
-
TokioListener::TcpTls { host, .. } => host.to_string(),
|
201
|
-
TokioListener::Unix { .. } => "unix".to_string(),
|
202
|
-
TokioListener::UnixTls { .. } => "unix".to_string(),
|
203
|
-
}
|
204
|
-
}
|
205
223
|
}
|
206
224
|
|
207
225
|
enum Stream {
|
@@ -225,51 +243,103 @@ impl std::fmt::Display for SockAddr {
|
|
225
243
|
}
|
226
244
|
}
|
227
245
|
}
|
246
|
+
impl Display for Listener {
|
247
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
248
|
+
match self {
|
249
|
+
Listener::Tcp(listener) | Listener::TcpTls((listener, _)) => write!(
|
250
|
+
f,
|
251
|
+
"{}",
|
252
|
+
listener
|
253
|
+
.local_addr()
|
254
|
+
.map(|addr| addr.to_string())
|
255
|
+
.unwrap_or_else(|_| "".to_string())
|
256
|
+
),
|
257
|
+
|
258
|
+
Listener::Unix(listener) | Listener::UnixTls((listener, _)) => write!(
|
259
|
+
f,
|
260
|
+
"{}",
|
261
|
+
listener
|
262
|
+
.local_addr()
|
263
|
+
.map(|addr| addr
|
264
|
+
.as_pathname()
|
265
|
+
.map(|path| path.to_str().unwrap_or("").to_owned())
|
266
|
+
.unwrap_or_default())
|
267
|
+
.unwrap_or_else(|_| "".to_string())
|
268
|
+
),
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
228
272
|
|
229
273
|
impl Listener {
|
230
|
-
pub fn
|
274
|
+
pub fn into_tokio_listener(self) -> TokioListener {
|
231
275
|
match self {
|
232
|
-
Listener::Tcp(listener) =>
|
233
|
-
|
234
|
-
|
235
|
-
Listener::
|
236
|
-
|
276
|
+
Listener::Tcp(listener) => {
|
277
|
+
TokioListener::Tcp(TokioTcpListener::from_std(listener).unwrap())
|
278
|
+
}
|
279
|
+
Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls(
|
280
|
+
TokioTcpListener::from_std(listener).unwrap(),
|
281
|
+
acceptor.clone(),
|
282
|
+
),
|
283
|
+
Listener::Unix(listener) => {
|
284
|
+
TokioListener::Unix(TokioUnixListener::from_std(listener).unwrap())
|
285
|
+
}
|
286
|
+
Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls(
|
287
|
+
TokioUnixListener::from_std(listener).unwrap(),
|
288
|
+
acceptor.clone(),
|
289
|
+
),
|
290
|
+
}
|
237
291
|
}
|
238
|
-
|
292
|
+
|
293
|
+
/// Handover information when using exec to hand over the listener to a replacement process.
|
294
|
+
pub fn handover(&self) -> Result<(String, i32)> {
|
239
295
|
match self {
|
240
|
-
Listener::Tcp(listener) =>
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
.
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
.unwrap()
|
258
|
-
.
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
.
|
296
|
+
Listener::Tcp(listener) => {
|
297
|
+
let addr = listener.local_addr()?;
|
298
|
+
Ok((
|
299
|
+
format!("tcp://{}:{}", addr.ip().to_canonical(), addr.port()),
|
300
|
+
listener.as_raw_fd(),
|
301
|
+
))
|
302
|
+
}
|
303
|
+
Listener::TcpTls((listener, _)) => {
|
304
|
+
let addr = listener.local_addr()?;
|
305
|
+
Ok((
|
306
|
+
format!("tcp://{}:{}", addr.ip().to_canonical(), addr.port()),
|
307
|
+
listener.as_raw_fd(),
|
308
|
+
))
|
309
|
+
}
|
310
|
+
Listener::Unix(listener) => {
|
311
|
+
let addr = listener.local_addr()?;
|
312
|
+
Ok((
|
313
|
+
format!("unix://{}", addr.as_pathname().unwrap().to_str().unwrap()),
|
314
|
+
listener.as_raw_fd(),
|
315
|
+
))
|
316
|
+
}
|
317
|
+
Listener::UnixTls((listener, _)) => {
|
318
|
+
let addr = listener.local_addr()?;
|
319
|
+
Ok((
|
320
|
+
format!("unix://{}", addr.as_pathname().unwrap().to_str().unwrap()),
|
321
|
+
listener.as_raw_fd(),
|
322
|
+
))
|
323
|
+
}
|
324
|
+
}
|
325
|
+
}
|
326
|
+
|
327
|
+
pub fn inherit_fd(bind: Bind, fd: RawFd) -> Result<Self> {
|
328
|
+
let bound = match bind.address {
|
329
|
+
BindAddress::Ip(_) => match bind.protocol {
|
330
|
+
BindProtocol::Http => Listener::Tcp(revive_tcp_socket(fd)?),
|
331
|
+
BindProtocol::Https => {
|
332
|
+
let tcp_listener = revive_tcp_socket(fd)?;
|
333
|
+
Listener::TcpTls((tcp_listener, bind.tls_config.unwrap()))
|
334
|
+
}
|
335
|
+
_ => unreachable!(),
|
266
336
|
},
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
acceptor: acceptor.clone(),
|
337
|
+
BindAddress::UnixSocket(_) => match bind.tls_config {
|
338
|
+
Some(tls_config) => Listener::UnixTls((revive_unix_socket(fd)?, tls_config)),
|
339
|
+
None => Listener::Unix(revive_unix_socket(fd)?),
|
271
340
|
},
|
272
|
-
}
|
341
|
+
};
|
342
|
+
Ok(bound)
|
273
343
|
}
|
274
344
|
}
|
275
345
|
|
@@ -295,6 +365,27 @@ impl TryFrom<Bind> for Listener {
|
|
295
365
|
}
|
296
366
|
}
|
297
367
|
|
368
|
+
fn revive_tcp_socket(fd: RawFd) -> Result<TcpListener> {
|
369
|
+
let socket = unsafe { Socket::from_raw_fd(fd) };
|
370
|
+
socket.set_reuse_port(true).ok();
|
371
|
+
socket.set_reuse_address(true).ok();
|
372
|
+
socket.set_nonblocking(true).ok();
|
373
|
+
socket.set_nodelay(true).ok();
|
374
|
+
socket.set_recv_buffer_size(262_144).ok();
|
375
|
+
socket.set_cloexec(true)?;
|
376
|
+
socket.listen(1024)?;
|
377
|
+
Ok(socket.into())
|
378
|
+
}
|
379
|
+
|
380
|
+
fn revive_unix_socket(fd: RawFd) -> Result<UnixListener> {
|
381
|
+
let socket = unsafe { Socket::from_raw_fd(fd) };
|
382
|
+
socket.set_nonblocking(true).ok();
|
383
|
+
socket.listen(1024)?;
|
384
|
+
socket.set_cloexec(true)?;
|
385
|
+
|
386
|
+
Ok(socket.into())
|
387
|
+
}
|
388
|
+
|
298
389
|
fn connect_tcp_socket(addr: IpAddr, port: u16) -> Result<TcpListener> {
|
299
390
|
let domain = match addr {
|
300
391
|
IpAddr::V4(_) => Domain::IPV4,
|
@@ -307,6 +398,7 @@ fn connect_tcp_socket(addr: IpAddr, port: u16) -> Result<TcpListener> {
|
|
307
398
|
socket.set_nonblocking(true).ok();
|
308
399
|
socket.set_nodelay(true).ok();
|
309
400
|
socket.set_recv_buffer_size(262_144).ok();
|
401
|
+
socket.set_only_v6(false).ok();
|
310
402
|
socket.bind(&socket_address.into())?;
|
311
403
|
socket.listen(1024)?;
|
312
404
|
Ok(socket.into())
|
@@ -319,7 +411,6 @@ fn connect_unix_socket(path: &PathBuf) -> Result<UnixListener> {
|
|
319
411
|
|
320
412
|
let socket_address = socket2::SockAddr::unix(path)?;
|
321
413
|
|
322
|
-
info!("Binding to {:?}", path);
|
323
414
|
socket.bind(&socket_address)?;
|
324
415
|
socket.listen(1024)?;
|
325
416
|
|