itsi-scheduler 0.2.22-aarch64-linux

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.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +8 -0
  3. data/Cargo.lock +997 -0
  4. data/Cargo.toml +7 -0
  5. data/Rakefile +39 -0
  6. data/ext/itsi_acme/Cargo.toml +86 -0
  7. data/ext/itsi_acme/examples/high_level.rs +63 -0
  8. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  9. data/ext/itsi_acme/examples/low_level.rs +87 -0
  10. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  11. data/ext/itsi_acme/src/acceptor.rs +81 -0
  12. data/ext/itsi_acme/src/acme.rs +354 -0
  13. data/ext/itsi_acme/src/axum.rs +86 -0
  14. data/ext/itsi_acme/src/cache.rs +39 -0
  15. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  16. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  17. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  18. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  19. data/ext/itsi_acme/src/caches/no.rs +78 -0
  20. data/ext/itsi_acme/src/caches/test.rs +136 -0
  21. data/ext/itsi_acme/src/config.rs +172 -0
  22. data/ext/itsi_acme/src/https_helper.rs +69 -0
  23. data/ext/itsi_acme/src/incoming.rs +142 -0
  24. data/ext/itsi_acme/src/jose.rs +161 -0
  25. data/ext/itsi_acme/src/lib.rs +142 -0
  26. data/ext/itsi_acme/src/resolver.rs +59 -0
  27. data/ext/itsi_acme/src/state.rs +424 -0
  28. data/ext/itsi_error/Cargo.lock +368 -0
  29. data/ext/itsi_error/Cargo.toml +12 -0
  30. data/ext/itsi_error/src/lib.rs +140 -0
  31. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  32. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  33. data/ext/itsi_rb_helpers/Cargo.lock +355 -0
  34. data/ext/itsi_rb_helpers/Cargo.toml +11 -0
  35. data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
  36. data/ext/itsi_rb_helpers/src/lib.rs +232 -0
  37. data/ext/itsi_scheduler/Cargo.toml +24 -0
  38. data/ext/itsi_scheduler/extconf.rb +11 -0
  39. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  40. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  41. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  42. data/ext/itsi_scheduler/src/itsi_scheduler.rs +320 -0
  43. data/ext/itsi_scheduler/src/lib.rs +39 -0
  44. data/ext/itsi_server/Cargo.lock +2956 -0
  45. data/ext/itsi_server/Cargo.toml +94 -0
  46. data/ext/itsi_server/src/default_responses/mod.rs +14 -0
  47. data/ext/itsi_server/src/env.rs +43 -0
  48. data/ext/itsi_server/src/lib.rs +154 -0
  49. data/ext/itsi_server/src/prelude.rs +2 -0
  50. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +116 -0
  51. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +149 -0
  52. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +346 -0
  53. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +265 -0
  54. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +399 -0
  55. data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +447 -0
  56. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +545 -0
  57. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +650 -0
  58. data/ext/itsi_server/src/ruby_types/itsi_server.rs +102 -0
  59. data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
  60. data/ext/itsi_server/src/server/binds/bind.rs +204 -0
  61. data/ext/itsi_server/src/server/binds/bind_protocol.rs +37 -0
  62. data/ext/itsi_server/src/server/binds/listener.rs +485 -0
  63. data/ext/itsi_server/src/server/binds/mod.rs +4 -0
  64. data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +132 -0
  65. data/ext/itsi_server/src/server/binds/tls.rs +278 -0
  66. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  67. data/ext/itsi_server/src/server/frame_stream.rs +143 -0
  68. data/ext/itsi_server/src/server/http_message_types.rs +230 -0
  69. data/ext/itsi_server/src/server/io_stream.rs +128 -0
  70. data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
  71. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
  72. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
  73. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
  74. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +93 -0
  75. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
  76. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
  77. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +329 -0
  78. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +300 -0
  79. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
  80. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
  81. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +188 -0
  82. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +168 -0
  83. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +183 -0
  84. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
  85. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +209 -0
  86. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +133 -0
  87. data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  88. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +122 -0
  89. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +407 -0
  90. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +155 -0
  91. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +54 -0
  92. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
  93. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -0
  94. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +138 -0
  95. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +269 -0
  96. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +62 -0
  97. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +218 -0
  98. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
  99. data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
  100. data/ext/itsi_server/src/server/mod.rs +14 -0
  101. data/ext/itsi_server/src/server/process_worker.rs +247 -0
  102. data/ext/itsi_server/src/server/redirect_type.rs +26 -0
  103. data/ext/itsi_server/src/server/request_job.rs +11 -0
  104. data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +100 -0
  105. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +411 -0
  106. data/ext/itsi_server/src/server/serve_strategy/mod.rs +31 -0
  107. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +449 -0
  108. data/ext/itsi_server/src/server/signal.rs +129 -0
  109. data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
  110. data/ext/itsi_server/src/server/thread_worker.rs +504 -0
  111. data/ext/itsi_server/src/services/cache_store.rs +74 -0
  112. data/ext/itsi_server/src/services/itsi_http_service.rs +270 -0
  113. data/ext/itsi_server/src/services/mime_types.rs +2896 -0
  114. data/ext/itsi_server/src/services/mod.rs +6 -0
  115. data/ext/itsi_server/src/services/password_hasher.rs +89 -0
  116. data/ext/itsi_server/src/services/rate_limiter.rs +609 -0
  117. data/ext/itsi_server/src/services/static_file_server.rs +1400 -0
  118. data/ext/itsi_tracing/Cargo.lock +274 -0
  119. data/ext/itsi_tracing/Cargo.toml +17 -0
  120. data/ext/itsi_tracing/src/lib.rs +370 -0
  121. data/itsi-scheduler-100.png +0 -0
  122. data/lib/itsi/schedule_refinement.rb +96 -0
  123. data/lib/itsi/scheduler/3.1/itsi_scheduler.so +0 -0
  124. data/lib/itsi/scheduler/3.2/itsi_scheduler.so +0 -0
  125. data/lib/itsi/scheduler/3.3/itsi_scheduler.so +0 -0
  126. data/lib/itsi/scheduler/3.4/itsi_scheduler.so +0 -0
  127. data/lib/itsi/scheduler/4.0/itsi_scheduler.so +0 -0
  128. data/lib/itsi/scheduler/native_extension.rb +34 -0
  129. data/lib/itsi/scheduler/version.rb +7 -0
  130. data/lib/itsi/scheduler.rb +153 -0
  131. data/vendor/rb-sys-build/.cargo-ok +1 -0
  132. data/vendor/rb-sys-build/.cargo_vcs_info.json +6 -0
  133. data/vendor/rb-sys-build/Cargo.lock +294 -0
  134. data/vendor/rb-sys-build/Cargo.toml +71 -0
  135. data/vendor/rb-sys-build/Cargo.toml.orig +32 -0
  136. data/vendor/rb-sys-build/LICENSE-APACHE +190 -0
  137. data/vendor/rb-sys-build/LICENSE-MIT +21 -0
  138. data/vendor/rb-sys-build/src/bindings/sanitizer.rs +185 -0
  139. data/vendor/rb-sys-build/src/bindings/stable_api.rs +247 -0
  140. data/vendor/rb-sys-build/src/bindings/wrapper.h +71 -0
  141. data/vendor/rb-sys-build/src/bindings.rs +280 -0
  142. data/vendor/rb-sys-build/src/cc.rs +421 -0
  143. data/vendor/rb-sys-build/src/lib.rs +12 -0
  144. data/vendor/rb-sys-build/src/rb_config/flags.rs +101 -0
  145. data/vendor/rb-sys-build/src/rb_config/library.rs +132 -0
  146. data/vendor/rb-sys-build/src/rb_config/search_path.rs +57 -0
  147. data/vendor/rb-sys-build/src/rb_config.rs +906 -0
  148. data/vendor/rb-sys-build/src/utils.rs +53 -0
  149. metadata +210 -0
@@ -0,0 +1,102 @@
1
+ use crate::server::{
2
+ lifecycle_event::LifecycleEvent,
3
+ serve_strategy::{cluster_mode::ClusterMode, single_mode::SingleMode, ServeStrategy},
4
+ signal::{clear_signal_handlers, reset_signal_handlers, send_lifecycle_event},
5
+ };
6
+ use itsi_rb_helpers::{call_without_gvl, print_rb_backtrace};
7
+ use itsi_server_config::ItsiServerConfig;
8
+ use itsi_tracing::{error, run_silently};
9
+ use magnus::{block::Proc, error::Result, RHash, Ruby};
10
+ use parking_lot::Mutex;
11
+ use std::{path::PathBuf, sync::Arc};
12
+ use tracing::{info, instrument};
13
+ mod file_watcher;
14
+ pub mod itsi_server_config;
15
+ #[magnus::wrap(class = "Itsi::Server", free_immediately, size)]
16
+ #[derive(Clone, Default)]
17
+ pub struct ItsiServer {
18
+ pub config: Arc<Mutex<Option<Arc<ItsiServerConfig>>>>,
19
+ }
20
+
21
+ impl ItsiServer {
22
+ pub fn initialize(
23
+ &self,
24
+ cli_params: RHash,
25
+ itsifile_path: Option<PathBuf>,
26
+ itsi_config_proc: Option<Proc>,
27
+ ) -> Result<()> {
28
+ let ruby = Ruby::get().map_err(|_| {
29
+ magnus::Error::new(
30
+ magnus::Ruby::get().unwrap().exception_runtime_error(),
31
+ "Failed to acquire Ruby VM handle",
32
+ )
33
+ })?;
34
+ let config = Arc::new(ItsiServerConfig::new(
35
+ &ruby,
36
+ cli_params,
37
+ itsifile_path,
38
+ itsi_config_proc,
39
+ )?);
40
+ *self.config.lock() = Some(config);
41
+ Ok(())
42
+ }
43
+
44
+ pub fn stop(&self) -> Result<()> {
45
+ send_lifecycle_event(LifecycleEvent::Shutdown);
46
+ Ok(())
47
+ }
48
+
49
+ fn config(&self) -> Result<Arc<ItsiServerConfig>> {
50
+ self.config.lock().as_ref().cloned().ok_or_else(|| {
51
+ magnus::Error::new(
52
+ magnus::Ruby::get().unwrap().exception_runtime_error(),
53
+ "Itsi::Server not initialized",
54
+ )
55
+ })
56
+ }
57
+
58
+ #[instrument(skip(self))]
59
+ pub fn start(&self) -> Result<()> {
60
+ let server_config = self.config()?;
61
+ server_config.server_params.read().setup_listeners()?;
62
+ let result = if server_config.server_params.read().silence {
63
+ run_silently(|| self.build_and_run_strategy())
64
+ } else {
65
+ info!("Itsi - Rolling into action. ⚪💨");
66
+ self.build_and_run_strategy()
67
+ };
68
+ if let Err(e) = result {
69
+ error!("Error starting server: {:?}", e);
70
+ if let Some(err_value) = e.value() {
71
+ print_rb_backtrace(err_value);
72
+ }
73
+ return Err(e);
74
+ }
75
+ Ok(())
76
+ }
77
+
78
+ pub(crate) fn build_strategy(&self) -> Result<ServeStrategy> {
79
+ let server_config = self.config()?;
80
+ Ok(if server_config.server_params.read().workers > 1 {
81
+ ServeStrategy::Cluster(Arc::new(ClusterMode::new(server_config)))
82
+ } else {
83
+ ServeStrategy::Single(Arc::new(SingleMode::new(server_config, 0)?))
84
+ })
85
+ }
86
+
87
+ fn build_and_run_strategy(&self) -> Result<()> {
88
+ reset_signal_handlers();
89
+ call_without_gvl(move || -> Result<()> {
90
+ let strategy = self.build_strategy()?;
91
+ if let Err(e) = strategy.clone().run() {
92
+ error!("Error running server: {}", e);
93
+ send_lifecycle_event(LifecycleEvent::Shutdown);
94
+ strategy.stop()?;
95
+ }
96
+ Ok(())
97
+ })?;
98
+ clear_signal_handlers();
99
+ info!("Server stopped");
100
+ Ok(())
101
+ }
102
+ }
@@ -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
+ });
@@ -0,0 +1,204 @@
1
+ use super::{
2
+ bind_protocol::BindProtocol,
3
+ tls::{configure_tls, ItsiTlsAcceptor},
4
+ };
5
+ use crate::prelude::*;
6
+ use itsi_error::ItsiError;
7
+ use std::{
8
+ collections::HashMap,
9
+ net::{IpAddr, Ipv4Addr, Ipv6Addr, ToSocketAddrs},
10
+ path::PathBuf,
11
+ str::FromStr,
12
+ };
13
+ use tracing::{instrument, Level};
14
+
15
+ #[derive(Debug, Clone)]
16
+ pub enum BindAddress {
17
+ Ip(IpAddr),
18
+ UnixSocket(PathBuf),
19
+ }
20
+
21
+ impl Default for BindAddress {
22
+ fn default() -> Self {
23
+ BindAddress::Ip(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
24
+ }
25
+ }
26
+
27
+ #[derive(Default, Clone)]
28
+ #[magnus::wrap(class = "Itsi::Bind")]
29
+ pub struct Bind {
30
+ pub address: BindAddress,
31
+ pub port: Option<u16>, // None for Unix Sockets
32
+ pub protocol: BindProtocol,
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
+ }
57
+ }
58
+
59
+ impl std::fmt::Debug for Bind {
60
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61
+ match &self.address {
62
+ BindAddress::Ip(ip) => match self.protocol {
63
+ BindProtocol::Https if self.port == Some(443) => {
64
+ write!(f, "{}://{}", self.protocol, ip)
65
+ }
66
+ BindProtocol::Http if self.port == Some(80) => {
67
+ write!(f, "{}://{}", self.protocol, ip)
68
+ }
69
+ _ => match self.port {
70
+ Some(port) => write!(f, "{}://{}:{}", self.protocol, ip, port),
71
+ None => write!(f, "{}://{}", self.protocol, ip),
72
+ },
73
+ },
74
+ BindAddress::UnixSocket(path) => {
75
+ write!(f, "{}://{}", self.protocol, path.display())
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ /// We can build a Bind from a string in the format `protocol://host:port?options`
82
+ /// E.g.
83
+ /// *`https://example.com:443?tls_cert=/path/to/cert.pem&tls_key=/path/to/key.pem`
84
+ /// *`unix:///path/to/socket.sock`
85
+ /// *`http://example.com:80`
86
+ /// *`https://[::]:80`
87
+ impl FromStr for Bind {
88
+ type Err = ItsiError;
89
+
90
+ #[instrument(ret(level = Level::DEBUG))]
91
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
92
+ let (protocol, remainder) = if let Some((proto, rest)) = s.split_once("://") {
93
+ (proto.parse::<BindProtocol>()?, rest)
94
+ } else {
95
+ (BindProtocol::Https, s)
96
+ };
97
+
98
+ let (url, options) = if let Some((base, options)) = remainder.split_once('?') {
99
+ (base, parse_bind_options(options))
100
+ } else {
101
+ (remainder, HashMap::new())
102
+ };
103
+
104
+ let (host, port) = if url.starts_with('[') {
105
+ // IPv6 with brackets `[::]:port`
106
+ if let Some(end) = url.find(']') {
107
+ let host = &url[1..end]; // Extract `::`
108
+ let port = url[end + 1..]
109
+ .strip_prefix(':')
110
+ .and_then(|p| p.parse().ok());
111
+ (host, port)
112
+ } else {
113
+ return Err(ItsiError::InvalidInput(
114
+ "Invalid IPv6 address format".to_owned(),
115
+ ));
116
+ }
117
+ } else if let Some((h, p)) = url.rsplit_once(':') {
118
+ // Check if `h` is an IPv6 address before assuming it's a port
119
+ if h.contains('.') || h.parse::<Ipv4Addr>().is_ok() {
120
+ (h, p.parse::<u16>().ok()) // IPv4 case
121
+ } else if h.parse::<Ipv6Addr>().is_ok() {
122
+ // If it's IPv6, require brackets for port
123
+ return Err(ItsiError::InvalidInput(
124
+ "IPv6 addresses must use [ ] when specifying a port".to_owned(),
125
+ ));
126
+ } else {
127
+ (h, p.parse::<u16>().ok()) // Treat as a hostname
128
+ }
129
+ } else {
130
+ (url, None)
131
+ };
132
+
133
+ let address = if let Ok(ip) = host.parse::<IpAddr>() {
134
+ BindAddress::Ip(ip)
135
+ } else {
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
+ }
145
+ };
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,
152
+ };
153
+
154
+ let tls_config = match protocol {
155
+ BindProtocol::Http => None,
156
+ BindProtocol::Https => Some(TlsOptions {
157
+ host: host.to_owned(),
158
+ options,
159
+ }),
160
+ BindProtocol::Unix => None,
161
+ BindProtocol::Unixs => Some(TlsOptions {
162
+ host: host.to_owned(),
163
+ options,
164
+ }),
165
+ };
166
+ let bind = Self {
167
+ address,
168
+ port,
169
+ protocol,
170
+ tls_config,
171
+ };
172
+ Ok(bind)
173
+ }
174
+ }
175
+
176
+ fn parse_bind_options(query: &str) -> HashMap<String, String> {
177
+ query
178
+ .split('&')
179
+ .filter_map(|pair| pair.split_once('='))
180
+ .map(|(k, v)| (k.to_owned(), v.to_owned()))
181
+ .collect()
182
+ }
183
+
184
+ /// Attempts to resolve a hostname into an IP address.
185
+ #[instrument(ret(level = Level::DEBUG))]
186
+ fn resolve_hostname(hostname: &str) -> Option<IpAddr> {
187
+ (hostname, 0)
188
+ .to_socket_addrs()
189
+ .ok()?
190
+ .find_map(|addr| {
191
+ if addr.is_ipv4() {
192
+ Some(addr.ip()) // Prefer IPv4
193
+ } else {
194
+ None
195
+ }
196
+ })
197
+ .or_else(|| {
198
+ (hostname, 0)
199
+ .to_socket_addrs()
200
+ .ok()?
201
+ .map(|addr| addr.ip())
202
+ .next()
203
+ }) // Fallback to IPv4
204
+ }
@@ -0,0 +1,37 @@
1
+ use itsi_error::ItsiError;
2
+ use std::str::FromStr;
3
+
4
+ #[derive(Debug, Default, Clone)]
5
+ pub enum BindProtocol {
6
+ #[default]
7
+ Https,
8
+ Http,
9
+ Unix,
10
+ Unixs,
11
+ }
12
+
13
+ impl FromStr for BindProtocol {
14
+ type Err = ItsiError;
15
+
16
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
17
+ match s {
18
+ "http" => Ok(BindProtocol::Http),
19
+ "https" => Ok(BindProtocol::Https),
20
+ "unix" => Ok(BindProtocol::Unix),
21
+ "tls" => Ok(BindProtocol::Unixs),
22
+ _ => Err(ItsiError::UnsupportedProtocol(s.to_string())),
23
+ }
24
+ }
25
+ }
26
+
27
+ impl std::fmt::Display for BindProtocol {
28
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29
+ let s = match self {
30
+ BindProtocol::Https => "https",
31
+ BindProtocol::Http => "http",
32
+ BindProtocol::Unix => "unix",
33
+ BindProtocol::Unixs => "tls",
34
+ };
35
+ write!(f, "{}", s)
36
+ }
37
+ }