itsi-scheduler 0.1.5 → 0.2.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 +187 -62
- data/README.md +57 -24
- data/Rakefile +0 -4
- data/ext/itsi_acme/Cargo.toml +86 -0
- data/ext/itsi_acme/examples/high_level.rs +63 -0
- data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
- data/ext/itsi_acme/examples/low_level.rs +87 -0
- data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
- data/ext/itsi_acme/src/acceptor.rs +81 -0
- data/ext/itsi_acme/src/acme.rs +354 -0
- data/ext/itsi_acme/src/axum.rs +86 -0
- data/ext/itsi_acme/src/cache.rs +39 -0
- data/ext/itsi_acme/src/caches/boxed.rs +80 -0
- data/ext/itsi_acme/src/caches/composite.rs +69 -0
- data/ext/itsi_acme/src/caches/dir.rs +106 -0
- data/ext/itsi_acme/src/caches/mod.rs +11 -0
- data/ext/itsi_acme/src/caches/no.rs +78 -0
- data/ext/itsi_acme/src/caches/test.rs +136 -0
- data/ext/itsi_acme/src/config.rs +172 -0
- data/ext/itsi_acme/src/https_helper.rs +69 -0
- data/ext/itsi_acme/src/incoming.rs +142 -0
- data/ext/itsi_acme/src/jose.rs +161 -0
- data/ext/itsi_acme/src/lib.rs +142 -0
- data/ext/itsi_acme/src/resolver.rs +59 -0
- data/ext/itsi_acme/src/state.rs +424 -0
- data/ext/itsi_error/Cargo.toml +1 -0
- data/ext/itsi_error/src/lib.rs +106 -7
- 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 +63 -12
- 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 +1 -1
- data/ext/itsi_scheduler/src/itsi_scheduler.rs +9 -3
- data/ext/itsi_scheduler/src/lib.rs +1 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +73 -29
- 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 +114 -75
- data/ext/itsi_server/src/prelude.rs +2 -0
- 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} +29 -8
- 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 +362 -0
- data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +84 -40
- data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +233 -0
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +588 -0
- data/ext/itsi_server/src/ruby_types/itsi_server.rs +86 -0
- data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
- data/ext/itsi_server/src/server/{bind.rs → binds/bind.rs} +59 -24
- data/ext/itsi_server/src/server/binds/listener.rs +444 -0
- data/ext/itsi_server/src/server/binds/mod.rs +4 -0
- data/ext/itsi_server/src/server/{tls → binds/tls}/locked_dir_cache.rs +57 -19
- data/ext/itsi_server/src/server/{tls.rs → binds/tls.rs} +120 -31
- 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 +2 -1
- data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
- data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +94 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +316 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +301 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +192 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +171 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +198 -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 +209 -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 +116 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +411 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +142 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +55 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -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 +187 -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 +173 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
- data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
- data/ext/itsi_server/src/server/mod.rs +7 -5
- data/ext/itsi_server/src/server/process_worker.rs +65 -14
- data/ext/itsi_server/src/server/redirect_type.rs +26 -0
- data/ext/itsi_server/src/server/request_job.rs +11 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +150 -50
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +399 -165
- data/ext/itsi_server/src/server/signal.rs +33 -26
- data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
- data/ext/itsi_server/src/server/thread_worker.rs +218 -107
- data/ext/itsi_server/src/services/cache_store.rs +74 -0
- data/ext/itsi_server/src/services/itsi_http_service.rs +257 -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 +580 -0
- data/ext/itsi_server/src/services/static_file_server.rs +1340 -0
- data/ext/itsi_tracing/Cargo.toml +1 -0
- data/ext/itsi_tracing/src/lib.rs +362 -33
- 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/itsi-scheduler-100.png +0 -0
- data/lib/itsi/scheduler/version.rb +1 -1
- data/lib/itsi/scheduler.rb +11 -6
- metadata +119 -26
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -132
- data/LICENSE.txt +0 -21
- data/ext/itsi_error/src/from.rs +0 -71
- 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/itsi_request.rs +0 -277
- 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/src/server/listener.rs +0 -327
- /data/ext/itsi_server/src/server/{bind_protocol.rs → binds/bind_protocol.rs} +0 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
use magnus::{value::Lazy, Module, RClass, RModule};
|
2
|
+
|
3
|
+
pub mod itsi_body_proxy;
|
4
|
+
pub mod itsi_grpc_call;
|
5
|
+
pub mod itsi_grpc_response_stream;
|
6
|
+
pub mod itsi_http_request;
|
7
|
+
pub mod itsi_http_response;
|
8
|
+
pub mod itsi_server;
|
9
|
+
|
10
|
+
pub static ITSI_MODULE: Lazy<RModule> = Lazy::new(|ruby| ruby.define_module("Itsi").unwrap());
|
11
|
+
pub static ITSI_SERVER: Lazy<RClass> = Lazy::new(|ruby| {
|
12
|
+
ruby.get_inner(&ITSI_MODULE)
|
13
|
+
.define_class("Server", ruby.class_object())
|
14
|
+
.unwrap()
|
15
|
+
});
|
16
|
+
|
17
|
+
pub static ITSI_SERVER_CONFIG: Lazy<RModule> =
|
18
|
+
Lazy::new(|ruby| ruby.get_inner(&ITSI_SERVER).const_get("Config").unwrap());
|
19
|
+
|
20
|
+
pub static ITSI_REQUEST: Lazy<RClass> = Lazy::new(|ruby| {
|
21
|
+
ruby.get_inner(&ITSI_MODULE)
|
22
|
+
.define_class("HttpRequest", ruby.class_object())
|
23
|
+
.unwrap()
|
24
|
+
});
|
25
|
+
|
26
|
+
pub static ITSI_RESPONSE: Lazy<RClass> = Lazy::new(|ruby| {
|
27
|
+
ruby.get_inner(&ITSI_MODULE)
|
28
|
+
.define_class("HttpResponse", ruby.class_object())
|
29
|
+
.unwrap()
|
30
|
+
});
|
31
|
+
|
32
|
+
pub static ITSI_BODY_PROXY: Lazy<RClass> = Lazy::new(|ruby| {
|
33
|
+
ruby.get_inner(&ITSI_MODULE)
|
34
|
+
.define_class("BodyProxy", ruby.class_object())
|
35
|
+
.unwrap()
|
36
|
+
});
|
37
|
+
|
38
|
+
pub static ITSI_GRPC_CALL: Lazy<RClass> = Lazy::new(|ruby| {
|
39
|
+
ruby.get_inner(&ITSI_MODULE)
|
40
|
+
.define_class("GrpcCall", ruby.class_object())
|
41
|
+
.unwrap()
|
42
|
+
});
|
43
|
+
|
44
|
+
pub static ITSI_GRPC_RESPONSE_STREAM: Lazy<RClass> = Lazy::new(|ruby| {
|
45
|
+
ruby.get_inner(&ITSI_MODULE)
|
46
|
+
.define_class("GrpcResponseStream", ruby.class_object())
|
47
|
+
.unwrap()
|
48
|
+
});
|
@@ -2,6 +2,7 @@ use super::{
|
|
2
2
|
bind_protocol::BindProtocol,
|
3
3
|
tls::{configure_tls, ItsiTlsAcceptor},
|
4
4
|
};
|
5
|
+
use crate::prelude::*;
|
5
6
|
use itsi_error::ItsiError;
|
6
7
|
use std::{
|
7
8
|
collections::HashMap,
|
@@ -9,6 +10,8 @@ use std::{
|
|
9
10
|
path::PathBuf,
|
10
11
|
str::FromStr,
|
11
12
|
};
|
13
|
+
use tracing::{instrument, Level};
|
14
|
+
|
12
15
|
#[derive(Debug, Clone)]
|
13
16
|
pub enum BindAddress {
|
14
17
|
Ip(IpAddr),
|
@@ -27,16 +30,36 @@ pub struct Bind {
|
|
27
30
|
pub address: BindAddress,
|
28
31
|
pub port: Option<u16>, // None for Unix Sockets
|
29
32
|
pub protocol: BindProtocol,
|
30
|
-
pub tls_config: Option<
|
33
|
+
pub tls_config: Option<TlsOptions>,
|
34
|
+
}
|
35
|
+
|
36
|
+
#[derive(Default, Clone)]
|
37
|
+
pub struct TlsOptions {
|
38
|
+
pub host: String,
|
39
|
+
pub options: HashMap<String, String>,
|
40
|
+
}
|
41
|
+
|
42
|
+
impl TlsOptions {
|
43
|
+
pub fn build_acceptor(&self) -> Result<ItsiTlsAcceptor> {
|
44
|
+
configure_tls(&self.host, &self.options)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
impl Bind {
|
49
|
+
pub fn listener_address_string(&self) -> String {
|
50
|
+
match &self.address {
|
51
|
+
BindAddress::Ip(ip) => format!("tcp://{}:{}", ip.to_canonical(), self.port.unwrap()),
|
52
|
+
BindAddress::UnixSocket(path) => {
|
53
|
+
format!("unix://{}", path.as_path().to_str().unwrap())
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
31
57
|
}
|
32
58
|
|
33
59
|
impl std::fmt::Debug for Bind {
|
34
60
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
35
61
|
match &self.address {
|
36
62
|
BindAddress::Ip(ip) => match self.protocol {
|
37
|
-
BindProtocol::Unix | BindProtocol::Unixs => {
|
38
|
-
write!(f, "{}://{}", self.protocol, ip)
|
39
|
-
}
|
40
63
|
BindProtocol::Https if self.port == Some(443) => {
|
41
64
|
write!(f, "{}://{}", self.protocol, ip)
|
42
65
|
}
|
@@ -64,7 +87,8 @@ impl std::fmt::Debug for Bind {
|
|
64
87
|
impl FromStr for Bind {
|
65
88
|
type Err = ItsiError;
|
66
89
|
|
67
|
-
|
90
|
+
#[instrument(ret(level = Level::DEBUG))]
|
91
|
+
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
68
92
|
let (protocol, remainder) = if let Some((proto, rest)) = s.split_once("://") {
|
69
93
|
(proto.parse::<BindProtocol>()?, rest)
|
70
94
|
} else {
|
@@ -100,7 +124,7 @@ impl FromStr for Bind {
|
|
100
124
|
"IPv6 addresses must use [ ] when specifying a port".to_owned(),
|
101
125
|
));
|
102
126
|
} else {
|
103
|
-
(h,
|
127
|
+
(h, p.parse::<u16>().ok()) // Treat as a hostname
|
104
128
|
}
|
105
129
|
} else {
|
106
130
|
(url, None)
|
@@ -109,33 +133,43 @@ impl FromStr for Bind {
|
|
109
133
|
let address = if let Ok(ip) = host.parse::<IpAddr>() {
|
110
134
|
BindAddress::Ip(ip)
|
111
135
|
} else {
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
136
|
+
match protocol {
|
137
|
+
BindProtocol::Https | BindProtocol::Http => resolve_hostname(host)
|
138
|
+
.map(BindAddress::Ip)
|
139
|
+
.ok_or(ItsiError::ArgumentError(format!(
|
140
|
+
"Failed to resolve hostname {}",
|
141
|
+
host
|
142
|
+
)))?,
|
143
|
+
BindProtocol::Unix | BindProtocol::Unixs => BindAddress::UnixSocket(host.into()),
|
144
|
+
}
|
118
145
|
};
|
119
|
-
|
120
|
-
|
121
|
-
BindProtocol::
|
122
|
-
BindProtocol::
|
123
|
-
BindProtocol::
|
146
|
+
|
147
|
+
let port = match protocol {
|
148
|
+
BindProtocol::Http => port.or(Some(80)),
|
149
|
+
BindProtocol::Https => port.or(Some(443)),
|
150
|
+
BindProtocol::Unix => None,
|
151
|
+
BindProtocol::Unixs => None,
|
124
152
|
};
|
125
153
|
|
126
154
|
let tls_config = match protocol {
|
127
155
|
BindProtocol::Http => None,
|
128
|
-
BindProtocol::Https => Some(
|
156
|
+
BindProtocol::Https => Some(TlsOptions {
|
157
|
+
host: host.to_owned(),
|
158
|
+
options,
|
159
|
+
}),
|
129
160
|
BindProtocol::Unix => None,
|
130
|
-
BindProtocol::Unixs => Some(
|
161
|
+
BindProtocol::Unixs => Some(TlsOptions {
|
162
|
+
host: host.to_owned(),
|
163
|
+
options,
|
164
|
+
}),
|
131
165
|
};
|
132
|
-
|
133
|
-
Ok(Self {
|
166
|
+
let bind = Self {
|
134
167
|
address,
|
135
168
|
port,
|
136
169
|
protocol,
|
137
170
|
tls_config,
|
138
|
-
}
|
171
|
+
};
|
172
|
+
Ok(bind)
|
139
173
|
}
|
140
174
|
}
|
141
175
|
|
@@ -148,13 +182,14 @@ fn parse_bind_options(query: &str) -> HashMap<String, String> {
|
|
148
182
|
}
|
149
183
|
|
150
184
|
/// Attempts to resolve a hostname into an IP address.
|
185
|
+
#[instrument(ret(level = Level::DEBUG))]
|
151
186
|
fn resolve_hostname(hostname: &str) -> Option<IpAddr> {
|
152
187
|
(hostname, 0)
|
153
188
|
.to_socket_addrs()
|
154
189
|
.ok()?
|
155
190
|
.find_map(|addr| {
|
156
|
-
if addr.
|
157
|
-
Some(addr.ip()) // Prefer
|
191
|
+
if addr.is_ipv4() {
|
192
|
+
Some(addr.ip()) // Prefer IPv4
|
158
193
|
} else {
|
159
194
|
None
|
160
195
|
}
|
@@ -0,0 +1,444 @@
|
|
1
|
+
use crate::prelude::*;
|
2
|
+
use crate::ruby_types::itsi_server::itsi_server_config::SocketOpts;
|
3
|
+
use crate::server::io_stream::IoStream;
|
4
|
+
use crate::server::serve_strategy::single_mode::RunningPhase;
|
5
|
+
|
6
|
+
use super::bind::{Bind, BindAddress};
|
7
|
+
use super::bind_protocol::BindProtocol;
|
8
|
+
|
9
|
+
use super::tls::ItsiTlsAcceptor;
|
10
|
+
use itsi_error::{ItsiError, Result};
|
11
|
+
use itsi_tracing::info;
|
12
|
+
use socket2::{Domain, Protocol, Socket, Type};
|
13
|
+
use std::fmt::Display;
|
14
|
+
use std::net::{IpAddr, SocketAddr, TcpListener};
|
15
|
+
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
16
|
+
use std::sync::Arc;
|
17
|
+
use std::{os::unix::net::UnixListener, path::PathBuf};
|
18
|
+
use tokio::net::TcpListener as TokioTcpListener;
|
19
|
+
use tokio::net::UnixListener as TokioUnixListener;
|
20
|
+
use tokio::net::{unix, TcpStream, UnixStream};
|
21
|
+
use tokio::sync::watch::Receiver;
|
22
|
+
use tokio_rustls::TlsAcceptor;
|
23
|
+
use tokio_stream::StreamExt;
|
24
|
+
|
25
|
+
pub(crate) enum Listener {
|
26
|
+
Tcp(TcpListener),
|
27
|
+
TcpTls((TcpListener, ItsiTlsAcceptor)),
|
28
|
+
Unix(UnixListener),
|
29
|
+
UnixTls((UnixListener, ItsiTlsAcceptor)),
|
30
|
+
}
|
31
|
+
|
32
|
+
pub(crate) enum TokioListener {
|
33
|
+
Tcp(TokioTcpListener),
|
34
|
+
TcpTls(TokioTcpListener, ItsiTlsAcceptor),
|
35
|
+
Unix(TokioUnixListener),
|
36
|
+
UnixTls(TokioUnixListener, ItsiTlsAcceptor),
|
37
|
+
}
|
38
|
+
|
39
|
+
#[derive(Debug, Clone)]
|
40
|
+
pub struct ListenerInfo {
|
41
|
+
pub host: String,
|
42
|
+
pub port: u16,
|
43
|
+
pub scheme: String,
|
44
|
+
}
|
45
|
+
|
46
|
+
impl TokioListener {
|
47
|
+
pub fn listener_info(&self) -> ListenerInfo {
|
48
|
+
match self {
|
49
|
+
TokioListener::Tcp(listener) => ListenerInfo {
|
50
|
+
host: listener
|
51
|
+
.local_addr()
|
52
|
+
.unwrap()
|
53
|
+
.ip()
|
54
|
+
.to_canonical()
|
55
|
+
.to_string(),
|
56
|
+
port: listener.local_addr().unwrap().port(),
|
57
|
+
scheme: "http".to_string(),
|
58
|
+
},
|
59
|
+
TokioListener::TcpTls(listener, _) => ListenerInfo {
|
60
|
+
host: listener
|
61
|
+
.local_addr()
|
62
|
+
.unwrap()
|
63
|
+
.ip()
|
64
|
+
.to_canonical()
|
65
|
+
.to_string(),
|
66
|
+
port: listener.local_addr().unwrap().port(),
|
67
|
+
scheme: "https".to_string(),
|
68
|
+
},
|
69
|
+
TokioListener::Unix(listener) => ListenerInfo {
|
70
|
+
host: listener
|
71
|
+
.local_addr()
|
72
|
+
.unwrap()
|
73
|
+
.as_pathname()
|
74
|
+
.unwrap()
|
75
|
+
.to_str()
|
76
|
+
.unwrap()
|
77
|
+
.to_owned(),
|
78
|
+
port: 0,
|
79
|
+
scheme: "unix".to_string(),
|
80
|
+
},
|
81
|
+
TokioListener::UnixTls(listener, _) => ListenerInfo {
|
82
|
+
host: listener
|
83
|
+
.local_addr()
|
84
|
+
.unwrap()
|
85
|
+
.as_pathname()
|
86
|
+
.unwrap()
|
87
|
+
.to_str()
|
88
|
+
.unwrap()
|
89
|
+
.to_owned(),
|
90
|
+
port: 0,
|
91
|
+
scheme: "ssl".to_string(),
|
92
|
+
},
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
pub(crate) async fn accept(&self) -> Result<IoStream> {
|
97
|
+
match self {
|
98
|
+
TokioListener::Tcp(listener) => TokioListener::accept_tcp(listener).await,
|
99
|
+
TokioListener::TcpTls(listener, acceptor) => {
|
100
|
+
TokioListener::accept_tls(listener, acceptor).await
|
101
|
+
}
|
102
|
+
TokioListener::Unix(listener) => TokioListener::accept_unix(listener).await,
|
103
|
+
TokioListener::UnixTls(listener, acceptor) => {
|
104
|
+
TokioListener::accept_unix_tls(listener, acceptor).await
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
async fn accept_tcp(listener: &TokioTcpListener) -> Result<IoStream> {
|
110
|
+
let tcp_stream = listener.accept().await?;
|
111
|
+
Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
|
112
|
+
}
|
113
|
+
|
114
|
+
pub async fn spawn_state_task(&self, mut shutdown_receiver: Receiver<RunningPhase>) {
|
115
|
+
if let TokioListener::TcpTls(
|
116
|
+
_,
|
117
|
+
ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
|
118
|
+
) = self
|
119
|
+
{
|
120
|
+
let mut state = state.lock().await;
|
121
|
+
loop {
|
122
|
+
tokio::select! {
|
123
|
+
stream_event = StreamExt::next(&mut *state) => {
|
124
|
+
match stream_event {
|
125
|
+
Some(event) => info!("ACME Event: {:?}", event),
|
126
|
+
None => error!("Received no acme event"),
|
127
|
+
}
|
128
|
+
},
|
129
|
+
_ = shutdown_receiver.changed() => {
|
130
|
+
break;
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
async fn accept_tls(
|
138
|
+
listener: &TokioTcpListener,
|
139
|
+
acceptor: &ItsiTlsAcceptor,
|
140
|
+
) -> Result<IoStream> {
|
141
|
+
let tcp_stream = listener.accept().await?;
|
142
|
+
match acceptor {
|
143
|
+
ItsiTlsAcceptor::Manual(tls_acceptor) => {
|
144
|
+
Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(tls_acceptor)).await
|
145
|
+
}
|
146
|
+
ItsiTlsAcceptor::Automatic(acme_acceptor, _, rustls_config) => {
|
147
|
+
let accept_future = acme_acceptor.accept(tcp_stream.0);
|
148
|
+
match accept_future.await {
|
149
|
+
Ok(None) => Err(ItsiError::Pass),
|
150
|
+
Ok(Some(start_handshake)) => {
|
151
|
+
let tls_stream = start_handshake.into_stream(rustls_config.clone()).await?;
|
152
|
+
Ok(IoStream::TcpTls {
|
153
|
+
stream: tls_stream,
|
154
|
+
addr: SockAddr::Tcp(Arc::new(tcp_stream.1)),
|
155
|
+
})
|
156
|
+
}
|
157
|
+
Err(error) => {
|
158
|
+
error!(error = format!("{:?}", error));
|
159
|
+
Err(ItsiError::Pass)
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
async fn accept_unix(listener: &TokioUnixListener) -> Result<IoStream> {
|
167
|
+
let unix_stream = listener.accept().await?;
|
168
|
+
Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
|
169
|
+
}
|
170
|
+
|
171
|
+
async fn accept_unix_tls(
|
172
|
+
listener: &TokioUnixListener,
|
173
|
+
acceptor: &ItsiTlsAcceptor,
|
174
|
+
) -> Result<IoStream> {
|
175
|
+
let unix_stream = listener.accept().await?;
|
176
|
+
match acceptor {
|
177
|
+
ItsiTlsAcceptor::Manual(tls_acceptor) => {
|
178
|
+
Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(tls_acceptor)).await
|
179
|
+
}
|
180
|
+
ItsiTlsAcceptor::Automatic(_, _, _) => {
|
181
|
+
error!("Automatic TLS not supported on Unix sockets");
|
182
|
+
Err(ItsiError::UnsupportedProtocol(
|
183
|
+
"Automatic TLS on Unix Sockets".to_owned(),
|
184
|
+
))
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
async fn to_tokio_io(
|
190
|
+
input_stream: Stream,
|
191
|
+
tls_acceptor: Option<&TlsAcceptor>,
|
192
|
+
) -> Result<IoStream> {
|
193
|
+
match tls_acceptor {
|
194
|
+
Some(acceptor) => match input_stream {
|
195
|
+
Stream::TcpStream((tcp_stream, socket_address)) => {
|
196
|
+
match acceptor.accept(tcp_stream).await {
|
197
|
+
Ok(tls_stream) => Ok(IoStream::TcpTls {
|
198
|
+
stream: tls_stream,
|
199
|
+
addr: SockAddr::Tcp(Arc::new(socket_address)),
|
200
|
+
}),
|
201
|
+
Err(err) => Err(err.into()),
|
202
|
+
}
|
203
|
+
}
|
204
|
+
Stream::UnixStream((unix_stream, socket_address)) => {
|
205
|
+
match acceptor.accept(unix_stream).await {
|
206
|
+
Ok(tls_stream) => Ok(IoStream::UnixTls {
|
207
|
+
stream: tls_stream,
|
208
|
+
addr: SockAddr::Unix(Arc::new(socket_address)),
|
209
|
+
}),
|
210
|
+
Err(err) => Err(err.into()),
|
211
|
+
}
|
212
|
+
}
|
213
|
+
},
|
214
|
+
None => match input_stream {
|
215
|
+
Stream::TcpStream((tcp_stream, socket_address)) => Ok(IoStream::Tcp {
|
216
|
+
stream: tcp_stream,
|
217
|
+
addr: SockAddr::Tcp(Arc::new(socket_address)),
|
218
|
+
}),
|
219
|
+
Stream::UnixStream((unix_stream, socket_address)) => Ok(IoStream::Unix {
|
220
|
+
stream: unix_stream,
|
221
|
+
addr: SockAddr::Unix(Arc::new(socket_address)),
|
222
|
+
}),
|
223
|
+
},
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
enum Stream {
|
229
|
+
TcpStream((TcpStream, SocketAddr)),
|
230
|
+
UnixStream((UnixStream, unix::SocketAddr)),
|
231
|
+
}
|
232
|
+
|
233
|
+
#[derive(Clone, Debug)]
|
234
|
+
pub enum SockAddr {
|
235
|
+
Tcp(Arc<SocketAddr>),
|
236
|
+
Unix(Arc<unix::SocketAddr>),
|
237
|
+
}
|
238
|
+
impl std::fmt::Display for SockAddr {
|
239
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
240
|
+
match self {
|
241
|
+
SockAddr::Tcp(socket_addr) => write!(f, "{}", socket_addr.ip().to_canonical()),
|
242
|
+
SockAddr::Unix(socket_addr) => match socket_addr.as_pathname() {
|
243
|
+
Some(path) => write!(f, "{:?}", path),
|
244
|
+
None => write!(f, ""),
|
245
|
+
},
|
246
|
+
}
|
247
|
+
}
|
248
|
+
}
|
249
|
+
impl Display for Listener {
|
250
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
251
|
+
match self {
|
252
|
+
Listener::Tcp(listener) | Listener::TcpTls((listener, _)) => write!(
|
253
|
+
f,
|
254
|
+
"{}",
|
255
|
+
listener
|
256
|
+
.local_addr()
|
257
|
+
.map(|addr| addr.to_string())
|
258
|
+
.unwrap_or_else(|_| "".to_string())
|
259
|
+
),
|
260
|
+
|
261
|
+
Listener::Unix(listener) | Listener::UnixTls((listener, _)) => write!(
|
262
|
+
f,
|
263
|
+
"{}",
|
264
|
+
listener
|
265
|
+
.local_addr()
|
266
|
+
.map(|addr| addr
|
267
|
+
.as_pathname()
|
268
|
+
.map(|path| path.to_str().unwrap_or("").to_owned())
|
269
|
+
.unwrap_or_default())
|
270
|
+
.unwrap_or_else(|_| "".to_string())
|
271
|
+
),
|
272
|
+
}
|
273
|
+
}
|
274
|
+
}
|
275
|
+
|
276
|
+
impl Listener {
|
277
|
+
pub fn into_tokio_listener(self) -> TokioListener {
|
278
|
+
match self {
|
279
|
+
Listener::Tcp(listener) => {
|
280
|
+
TokioListener::Tcp(TokioTcpListener::from_std(listener).unwrap())
|
281
|
+
}
|
282
|
+
Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls(
|
283
|
+
TokioTcpListener::from_std(listener).unwrap(),
|
284
|
+
acceptor.clone(),
|
285
|
+
),
|
286
|
+
Listener::Unix(listener) => {
|
287
|
+
TokioListener::Unix(TokioUnixListener::from_std(listener).unwrap())
|
288
|
+
}
|
289
|
+
Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls(
|
290
|
+
TokioUnixListener::from_std(listener).unwrap(),
|
291
|
+
acceptor.clone(),
|
292
|
+
),
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
/// Handover information when using exec to hand over the listener to a replacement process.
|
297
|
+
pub fn handover(&self) -> Result<(String, i32)> {
|
298
|
+
match self {
|
299
|
+
Listener::Tcp(listener) => {
|
300
|
+
let addr = listener.local_addr()?;
|
301
|
+
Ok((
|
302
|
+
format!("tcp://{}:{}", addr.ip().to_canonical(), addr.port()),
|
303
|
+
listener.as_raw_fd(),
|
304
|
+
))
|
305
|
+
}
|
306
|
+
Listener::TcpTls((listener, _)) => {
|
307
|
+
let addr = listener.local_addr()?;
|
308
|
+
Ok((
|
309
|
+
format!("tcp://{}:{}", addr.ip().to_canonical(), addr.port()),
|
310
|
+
listener.as_raw_fd(),
|
311
|
+
))
|
312
|
+
}
|
313
|
+
Listener::Unix(listener) => {
|
314
|
+
let addr = listener.local_addr()?;
|
315
|
+
Ok((
|
316
|
+
format!("unix://{}", addr.as_pathname().unwrap().to_str().unwrap()),
|
317
|
+
listener.as_raw_fd(),
|
318
|
+
))
|
319
|
+
}
|
320
|
+
Listener::UnixTls((listener, _)) => {
|
321
|
+
let addr = listener.local_addr()?;
|
322
|
+
Ok((
|
323
|
+
format!("unix://{}", addr.as_pathname().unwrap().to_str().unwrap()),
|
324
|
+
listener.as_raw_fd(),
|
325
|
+
))
|
326
|
+
}
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
pub fn inherit_fd(bind: Bind, fd: RawFd, socket_opts: &SocketOpts) -> Result<Self> {
|
331
|
+
let bound = match bind.address {
|
332
|
+
BindAddress::Ip(_) => match bind.protocol {
|
333
|
+
BindProtocol::Http => Listener::Tcp(revive_tcp_socket(fd, socket_opts)?),
|
334
|
+
BindProtocol::Https => {
|
335
|
+
let tcp_listener = revive_tcp_socket(fd, socket_opts)?;
|
336
|
+
Listener::TcpTls((
|
337
|
+
tcp_listener,
|
338
|
+
bind.tls_config.unwrap().build_acceptor().unwrap(),
|
339
|
+
))
|
340
|
+
}
|
341
|
+
_ => unreachable!(),
|
342
|
+
},
|
343
|
+
BindAddress::UnixSocket(_) => match bind.tls_config {
|
344
|
+
Some(tls_config) => Listener::UnixTls((
|
345
|
+
revive_unix_socket(fd, socket_opts)?,
|
346
|
+
tls_config.build_acceptor().unwrap(),
|
347
|
+
)),
|
348
|
+
None => Listener::Unix(revive_unix_socket(fd, socket_opts)?),
|
349
|
+
},
|
350
|
+
};
|
351
|
+
Ok(bound)
|
352
|
+
}
|
353
|
+
}
|
354
|
+
|
355
|
+
impl Listener {
|
356
|
+
pub fn build(bind: Bind, socket_opts: &SocketOpts) -> Result<Self> {
|
357
|
+
let bound = match bind.address {
|
358
|
+
BindAddress::Ip(addr) => match bind.protocol {
|
359
|
+
BindProtocol::Http => {
|
360
|
+
Listener::Tcp(connect_tcp_socket(addr, bind.port.unwrap(), socket_opts)?)
|
361
|
+
}
|
362
|
+
BindProtocol::Https => {
|
363
|
+
let tcp_listener = connect_tcp_socket(addr, bind.port.unwrap(), socket_opts)?;
|
364
|
+
Listener::TcpTls((
|
365
|
+
tcp_listener,
|
366
|
+
bind.tls_config.unwrap().build_acceptor().unwrap(),
|
367
|
+
))
|
368
|
+
}
|
369
|
+
_ => unreachable!(),
|
370
|
+
},
|
371
|
+
BindAddress::UnixSocket(path) => match bind.tls_config {
|
372
|
+
Some(tls_config) => Listener::UnixTls((
|
373
|
+
connect_unix_socket(&path, socket_opts)?,
|
374
|
+
tls_config.build_acceptor().unwrap(),
|
375
|
+
)),
|
376
|
+
None => Listener::Unix(connect_unix_socket(&path, socket_opts)?),
|
377
|
+
},
|
378
|
+
};
|
379
|
+
Ok(bound)
|
380
|
+
}
|
381
|
+
}
|
382
|
+
|
383
|
+
fn revive_tcp_socket(fd: RawFd, socket_opts: &SocketOpts) -> Result<TcpListener> {
|
384
|
+
let socket = unsafe { Socket::from_raw_fd(fd) };
|
385
|
+
socket.set_reuse_port(socket_opts.reuse_port).ok();
|
386
|
+
socket.set_reuse_address(socket_opts.reuse_address).ok();
|
387
|
+
socket.set_nonblocking(true).ok();
|
388
|
+
socket.set_nodelay(socket_opts.nodelay).ok();
|
389
|
+
socket
|
390
|
+
.set_recv_buffer_size(socket_opts.recv_buffer_size)
|
391
|
+
.ok();
|
392
|
+
socket.set_cloexec(true)?;
|
393
|
+
socket.listen(socket_opts.listen_backlog as i32)?;
|
394
|
+
Ok(socket.into())
|
395
|
+
}
|
396
|
+
|
397
|
+
fn revive_unix_socket(fd: RawFd, socket_opts: &SocketOpts) -> Result<UnixListener> {
|
398
|
+
let socket = unsafe { Socket::from_raw_fd(fd) };
|
399
|
+
socket.set_nonblocking(true).ok();
|
400
|
+
socket.listen(socket_opts.listen_backlog as i32)?;
|
401
|
+
socket.set_cloexec(true)?;
|
402
|
+
|
403
|
+
Ok(socket.into())
|
404
|
+
}
|
405
|
+
|
406
|
+
fn connect_tcp_socket(addr: IpAddr, port: u16, socket_opts: &SocketOpts) -> Result<TcpListener> {
|
407
|
+
let domain = match addr {
|
408
|
+
IpAddr::V4(_) => Domain::IPV4,
|
409
|
+
IpAddr::V6(_) => Domain::IPV6,
|
410
|
+
};
|
411
|
+
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
|
412
|
+
let socket_address: SocketAddr = SocketAddr::new(addr, port);
|
413
|
+
socket.set_reuse_address(socket_opts.reuse_address).ok();
|
414
|
+
socket.set_reuse_port(socket_opts.reuse_port).ok();
|
415
|
+
socket.set_nonblocking(true).ok();
|
416
|
+
socket.set_nodelay(socket_opts.nodelay).ok();
|
417
|
+
socket
|
418
|
+
.set_recv_buffer_size(socket_opts.recv_buffer_size)
|
419
|
+
.ok();
|
420
|
+
socket.set_only_v6(false).ok();
|
421
|
+
if let Err(e) = socket.bind(&socket_address.into()) {
|
422
|
+
error!("Failed to bind socket: {}", e);
|
423
|
+
};
|
424
|
+
if let Err(e) = socket.listen(socket_opts.listen_backlog as i32) {
|
425
|
+
error!("Failed to listen on socket: {}", e);
|
426
|
+
};
|
427
|
+
Ok(socket.into())
|
428
|
+
}
|
429
|
+
|
430
|
+
fn connect_unix_socket(path: &PathBuf, socket_opts: &SocketOpts) -> Result<UnixListener> {
|
431
|
+
let _ = std::fs::remove_file(path);
|
432
|
+
let socket = Socket::new(Domain::UNIX, Type::STREAM, None)?;
|
433
|
+
socket.set_nonblocking(true).ok();
|
434
|
+
|
435
|
+
let socket_address = socket2::SockAddr::unix(path)?;
|
436
|
+
|
437
|
+
if let Err(e) = socket.bind(&socket_address) {
|
438
|
+
error!("Failed to bind socket: {}", e);
|
439
|
+
};
|
440
|
+
if let Err(e) = socket.listen(socket_opts.listen_backlog as i32) {
|
441
|
+
error!("Failed to listen on socket: {}", e);
|
442
|
+
};
|
443
|
+
Ok(socket.into())
|
444
|
+
}
|