itsi-server 0.1.1 → 0.1.13
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-server might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +7 -0
- data/Cargo.lock +4417 -0
- data/Cargo.toml +7 -0
- data/README.md +4 -0
- data/Rakefile +8 -1
- data/_index.md +6 -0
- data/exe/itsi +94 -45
- data/ext/itsi_error/Cargo.toml +2 -0
- data/ext/itsi_error/src/from.rs +68 -0
- data/ext/itsi_error/src/lib.rs +18 -34
- 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_instrument_entry/Cargo.toml +15 -0
- data/ext/itsi_instrument_entry/src/lib.rs +31 -0
- data/ext/itsi_rb_helpers/Cargo.toml +3 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
- data/ext/itsi_rb_helpers/src/lib.rs +140 -10
- 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 +24 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
- data/ext/itsi_scheduler/src/lib.rs +38 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +73 -13
- data/ext/itsi_server/extconf.rb +1 -1
- data/ext/itsi_server/src/env.rs +43 -0
- data/ext/itsi_server/src/lib.rs +100 -40
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +109 -0
- data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +141 -0
- 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/ruby_types/itsi_http_request.rs +282 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +388 -0
- 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 +75 -31
- data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
- 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/io_stream.rs +104 -0
- data/ext/itsi_server/src/server/itsi_service.rs +172 -0
- data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
- data/ext/itsi_server/src/server/listener.rs +332 -132
- 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 +321 -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 +15 -2
- data/ext/itsi_server/src/server/process_worker.rs +229 -0
- 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 +337 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +30 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +421 -0
- data/ext/itsi_server/src/server/signal.rs +93 -0
- data/ext/itsi_server/src/server/static_file_server.rs +984 -0
- data/ext/itsi_server/src/server/thread_worker.rs +444 -0
- data/ext/itsi_server/src/server/tls/locked_dir_cache.rs +132 -0
- data/ext/itsi_server/src/server/tls.rs +187 -60
- data/ext/itsi_server/src/server/types.rs +43 -0
- data/ext/itsi_tracing/Cargo.toml +5 -0
- data/ext/itsi_tracing/src/lib.rs +225 -7
- 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/http_request.rb +87 -0
- data/lib/itsi/http_response.rb +39 -0
- data/lib/itsi/server/Itsi.rb +119 -0
- data/lib/itsi/server/config/dsl.rb +506 -0
- data/lib/itsi/server/config.rb +131 -0
- data/lib/itsi/server/default_app/default_app.rb +38 -0
- data/lib/itsi/server/default_app/index.html +91 -0
- data/lib/itsi/server/grpc_interface.rb +213 -0
- data/lib/itsi/server/rack/handler/itsi.rb +27 -0
- data/lib/itsi/server/rack_interface.rb +94 -0
- data/lib/itsi/server/scheduler_interface.rb +21 -0
- data/lib/itsi/server/scheduler_mode.rb +10 -0
- data/lib/itsi/server/signal_trap.rb +29 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +90 -9
- data/lib/itsi/standard_headers.rb +86 -0
- metadata +122 -31
- data/ext/itsi_server/src/request/itsi_request.rs +0 -143
- data/ext/itsi_server/src/request/mod.rs +0 -1
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -32
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -52
- data/ext/itsi_server/src/server/itsi_server.rs +0 -182
- data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
- data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
- data/lib/itsi/request.rb +0 -39
@@ -0,0 +1,32 @@
|
|
1
|
+
use std::ops::Deref;
|
2
|
+
|
3
|
+
use bytes::Bytes;
|
4
|
+
|
5
|
+
#[derive(Debug)]
|
6
|
+
pub enum ByteFrame {
|
7
|
+
Data(Bytes),
|
8
|
+
End(Bytes),
|
9
|
+
Empty,
|
10
|
+
}
|
11
|
+
|
12
|
+
impl Deref for ByteFrame {
|
13
|
+
type Target = Bytes;
|
14
|
+
|
15
|
+
fn deref(&self) -> &Self::Target {
|
16
|
+
match self {
|
17
|
+
ByteFrame::Data(data) => data,
|
18
|
+
ByteFrame::End(data) => data,
|
19
|
+
ByteFrame::Empty => unreachable!(),
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
impl From<ByteFrame> for Bytes {
|
25
|
+
fn from(frame: ByteFrame) -> Self {
|
26
|
+
match frame {
|
27
|
+
ByteFrame::Data(data) => data,
|
28
|
+
ByteFrame::End(data) => data,
|
29
|
+
ByteFrame::Empty => unreachable!(),
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
@@ -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,104 @@
|
|
1
|
+
use super::listener::SockAddr;
|
2
|
+
use pin_project::pin_project;
|
3
|
+
use tokio::net::{TcpStream, UnixStream};
|
4
|
+
use tokio_rustls::server::TlsStream;
|
5
|
+
|
6
|
+
use std::os::unix::io::{AsRawFd, RawFd};
|
7
|
+
use std::pin::Pin;
|
8
|
+
use std::task::{Context, Poll};
|
9
|
+
use tokio::io::{AsyncRead, AsyncWrite};
|
10
|
+
|
11
|
+
#[pin_project(project = IoStreamEnumProj)]
|
12
|
+
pub enum IoStream {
|
13
|
+
Tcp {
|
14
|
+
#[pin]
|
15
|
+
stream: TcpStream,
|
16
|
+
addr: SockAddr,
|
17
|
+
},
|
18
|
+
TcpTls {
|
19
|
+
#[pin]
|
20
|
+
stream: TlsStream<TcpStream>,
|
21
|
+
addr: SockAddr,
|
22
|
+
},
|
23
|
+
Unix {
|
24
|
+
#[pin]
|
25
|
+
stream: UnixStream,
|
26
|
+
addr: SockAddr,
|
27
|
+
},
|
28
|
+
UnixTls {
|
29
|
+
#[pin]
|
30
|
+
stream: TlsStream<UnixStream>,
|
31
|
+
addr: SockAddr,
|
32
|
+
},
|
33
|
+
}
|
34
|
+
|
35
|
+
impl IoStream {
|
36
|
+
pub fn addr(&self) -> SockAddr {
|
37
|
+
match self {
|
38
|
+
IoStream::Tcp { addr, .. } => addr.clone(),
|
39
|
+
IoStream::TcpTls { addr, .. } => addr.clone(),
|
40
|
+
IoStream::Unix { addr, .. } => addr.clone(),
|
41
|
+
IoStream::UnixTls { addr, .. } => addr.clone(),
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
impl AsyncRead for IoStream {
|
47
|
+
fn poll_read(
|
48
|
+
self: Pin<&mut Self>,
|
49
|
+
cx: &mut Context<'_>,
|
50
|
+
buf: &mut tokio::io::ReadBuf<'_>,
|
51
|
+
) -> Poll<std::io::Result<()>> {
|
52
|
+
match self.project() {
|
53
|
+
IoStreamEnumProj::Tcp { stream, .. } => stream.poll_read(cx, buf),
|
54
|
+
IoStreamEnumProj::TcpTls { stream, .. } => stream.poll_read(cx, buf),
|
55
|
+
IoStreamEnumProj::Unix { stream, .. } => stream.poll_read(cx, buf),
|
56
|
+
IoStreamEnumProj::UnixTls { stream, .. } => stream.poll_read(cx, buf),
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
impl AsyncWrite for IoStream {
|
62
|
+
fn poll_write(
|
63
|
+
self: Pin<&mut Self>,
|
64
|
+
cx: &mut Context<'_>,
|
65
|
+
buf: &[u8],
|
66
|
+
) -> Poll<std::io::Result<usize>> {
|
67
|
+
match self.project() {
|
68
|
+
IoStreamEnumProj::Tcp { stream, .. } => stream.poll_write(cx, buf),
|
69
|
+
IoStreamEnumProj::TcpTls { stream, .. } => stream.poll_write(cx, buf),
|
70
|
+
IoStreamEnumProj::Unix { stream, .. } => stream.poll_write(cx, buf),
|
71
|
+
IoStreamEnumProj::UnixTls { stream, .. } => stream.poll_write(cx, buf),
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
76
|
+
match self.project() {
|
77
|
+
IoStreamEnumProj::Tcp { stream, .. } => stream.poll_flush(cx),
|
78
|
+
IoStreamEnumProj::TcpTls { stream, .. } => stream.poll_flush(cx),
|
79
|
+
IoStreamEnumProj::Unix { stream, .. } => stream.poll_flush(cx),
|
80
|
+
IoStreamEnumProj::UnixTls { stream, .. } => stream.poll_flush(cx),
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
85
|
+
match self.project() {
|
86
|
+
IoStreamEnumProj::Tcp { stream, .. } => stream.poll_shutdown(cx),
|
87
|
+
IoStreamEnumProj::TcpTls { stream, .. } => stream.poll_shutdown(cx),
|
88
|
+
IoStreamEnumProj::Unix { stream, .. } => stream.poll_shutdown(cx),
|
89
|
+
IoStreamEnumProj::UnixTls { stream, .. } => stream.poll_shutdown(cx),
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
impl AsRawFd for IoStream {
|
95
|
+
fn as_raw_fd(&self) -> RawFd {
|
96
|
+
// For immutable access, we can simply pattern-match on self.
|
97
|
+
match self {
|
98
|
+
IoStream::Tcp { stream, .. } => stream.as_raw_fd(),
|
99
|
+
IoStream::TcpTls { stream, .. } => stream.get_ref().0.as_raw_fd(),
|
100
|
+
IoStream::Unix { stream, .. } => stream.as_raw_fd(),
|
101
|
+
IoStream::UnixTls { stream, .. } => stream.get_ref().0.as_raw_fd(),
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
@@ -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
|
+
}
|