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.

Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +7 -0
  3. data/Cargo.lock +83 -22
  4. data/README.md +5 -0
  5. data/_index.md +7 -0
  6. data/ext/itsi_error/src/from.rs +26 -29
  7. data/ext/itsi_error/src/lib.rs +10 -1
  8. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  9. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  10. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  11. data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  12. data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
  13. data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
  14. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
  15. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
  16. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
  17. data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
  18. data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
  19. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
  20. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
  21. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
  22. data/ext/itsi_rb_helpers/Cargo.toml +1 -0
  23. data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
  24. data/ext/itsi_rb_helpers/src/lib.rs +59 -9
  25. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  26. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  27. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  28. 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
  29. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
  30. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
  31. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
  32. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
  33. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
  34. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
  35. data/ext/itsi_server/Cargo.lock +2956 -0
  36. data/ext/itsi_server/Cargo.toml +69 -26
  37. data/ext/itsi_server/src/env.rs +43 -0
  38. data/ext/itsi_server/src/lib.rs +81 -75
  39. data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  40. data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +22 -3
  41. data/ext/itsi_server/src/ruby_types/itsi_grpc_request.rs +147 -0
  42. data/ext/itsi_server/src/ruby_types/itsi_grpc_response.rs +19 -0
  43. data/ext/itsi_server/src/ruby_types/itsi_grpc_stream/mod.rs +216 -0
  44. data/ext/itsi_server/src/{request/itsi_request.rs → ruby_types/itsi_http_request.rs} +108 -103
  45. data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +79 -38
  46. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
  47. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +355 -0
  48. data/ext/itsi_server/src/ruby_types/itsi_server.rs +82 -0
  49. data/ext/itsi_server/src/ruby_types/mod.rs +55 -0
  50. data/ext/itsi_server/src/server/bind.rs +33 -20
  51. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  52. data/ext/itsi_server/src/server/cache_store.rs +74 -0
  53. data/ext/itsi_server/src/server/itsi_service.rs +172 -0
  54. data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
  55. data/ext/itsi_server/src/server/listener.rs +197 -106
  56. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +153 -0
  57. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +47 -0
  58. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +58 -0
  59. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +82 -0
  60. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +264 -0
  61. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +139 -0
  62. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +300 -0
  63. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +287 -0
  64. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +48 -0
  65. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +127 -0
  66. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +191 -0
  67. data/ext/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +72 -0
  68. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +85 -0
  69. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +195 -0
  70. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  71. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +82 -0
  72. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +216 -0
  73. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +124 -0
  74. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
  75. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +43 -0
  76. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +34 -0
  77. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +93 -0
  78. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +162 -0
  79. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +158 -0
  80. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
  81. data/ext/itsi_server/src/server/middleware_stack/mod.rs +315 -0
  82. data/ext/itsi_server/src/server/mod.rs +8 -1
  83. data/ext/itsi_server/src/server/process_worker.rs +44 -11
  84. data/ext/itsi_server/src/server/rate_limiter.rs +565 -0
  85. data/ext/itsi_server/src/server/request_job.rs +11 -0
  86. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +129 -46
  87. data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
  88. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +337 -163
  89. data/ext/itsi_server/src/server/signal.rs +25 -2
  90. data/ext/itsi_server/src/server/static_file_server.rs +984 -0
  91. data/ext/itsi_server/src/server/thread_worker.rs +164 -88
  92. data/ext/itsi_server/src/server/tls/locked_dir_cache.rs +55 -17
  93. data/ext/itsi_server/src/server/tls.rs +104 -28
  94. data/ext/itsi_server/src/server/types.rs +43 -0
  95. data/ext/itsi_tracing/Cargo.toml +1 -0
  96. data/ext/itsi_tracing/src/lib.rs +222 -34
  97. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
  98. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
  99. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
  100. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
  101. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
  102. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
  103. data/lib/itsi/scheduler/version.rb +1 -1
  104. data/lib/itsi/scheduler.rb +2 -2
  105. metadata +79 -14
  106. data/ext/itsi_server/extconf.rb +0 -6
  107. data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  108. data/ext/itsi_server/src/request/mod.rs +0 -1
  109. data/ext/itsi_server/src/response/mod.rs +0 -1
  110. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -13
  111. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -5
  112. 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
+ }
@@ -3,7 +3,10 @@ pub enum LifecycleEvent {
3
3
  Start,
4
4
  Shutdown,
5
5
  Restart,
6
+ Reload,
6
7
  IncreaseWorkers,
7
8
  DecreaseWorkers,
8
9
  ForceShutdown,
10
+ PrintInfo,
11
+ ChildTerminated,
9
12
  }
@@ -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
- listener: TokioTcpListener,
28
- host: String,
29
- port: u16,
30
- },
31
- TcpTls {
32
- listener: TokioTcpListener,
33
- acceptor: ItsiTlsAcceptor,
34
- host: String,
35
- port: u16,
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 unbind(self) {
44
+ pub fn listener_info(&self) -> ListenerInfo {
48
45
  match self {
49
- TokioListener::Tcp { listener, .. } => drop(listener.into_std().unwrap()),
50
- TokioListener::TcpTls { listener, .. } => drop(listener.into_std().unwrap()),
51
- TokioListener::Unix { listener } => drop(listener.into_std().unwrap()),
52
- TokioListener::UnixTls { listener, .. } => drop(listener.into_std().unwrap()),
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 { listener, .. } => TokioListener::accept_tcp(listener).await,
58
- TokioListener::TcpTls {
59
- listener, acceptor, ..
60
- } => TokioListener::accept_tls(listener, acceptor).await,
61
- TokioListener::Unix { listener, .. } => TokioListener::accept_unix(listener).await,
62
- TokioListener::UnixTls {
63
- listener, acceptor, ..
64
- } => TokioListener::accept_unix_tls(listener, acceptor).await,
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
- acceptor: ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
76
- ..
77
- } = self
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
- match StreamExt::next(&mut *state).await {
82
- Some(event) => info!("Received acme event: {:?}", event),
83
- None => error!("Received no acme event"),
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 unbind(self) {
274
+ pub fn into_tokio_listener(self) -> TokioListener {
231
275
  match self {
232
- Listener::Tcp(listener) => drop(listener),
233
- Listener::TcpTls((listener, _)) => drop(listener),
234
- Listener::Unix(listener) => drop(listener),
235
- Listener::UnixTls((listener, _)) => drop(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
- pub fn to_tokio_listener(&self) -> TokioListener {
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) => TokioListener::Tcp {
241
- listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
242
- .unwrap(),
243
- host: listener
244
- .local_addr()
245
- .unwrap()
246
- .ip()
247
- .to_canonical()
248
- .to_string(),
249
- port: listener.local_addr().unwrap().port(),
250
- },
251
- Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls {
252
- listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
253
- .unwrap(),
254
- acceptor: acceptor.clone(),
255
- host: listener
256
- .local_addr()
257
- .unwrap()
258
- .ip()
259
- .to_canonical()
260
- .to_string(),
261
- port: listener.local_addr().unwrap().port(),
262
- },
263
- Listener::Unix(listener) => TokioListener::Unix {
264
- listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
265
- .unwrap(),
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
- Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls {
268
- listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
269
- .unwrap(),
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