itsi-scheduler 0.1.5 → 0.2.2

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +120 -52
  3. data/README.md +57 -24
  4. data/Rakefile +0 -4
  5. data/ext/itsi_acme/Cargo.toml +86 -0
  6. data/ext/itsi_acme/examples/high_level.rs +63 -0
  7. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  8. data/ext/itsi_acme/examples/low_level.rs +87 -0
  9. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  10. data/ext/itsi_acme/src/acceptor.rs +81 -0
  11. data/ext/itsi_acme/src/acme.rs +354 -0
  12. data/ext/itsi_acme/src/axum.rs +86 -0
  13. data/ext/itsi_acme/src/cache.rs +39 -0
  14. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  15. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  16. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  17. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  18. data/ext/itsi_acme/src/caches/no.rs +78 -0
  19. data/ext/itsi_acme/src/caches/test.rs +136 -0
  20. data/ext/itsi_acme/src/config.rs +172 -0
  21. data/ext/itsi_acme/src/https_helper.rs +69 -0
  22. data/ext/itsi_acme/src/incoming.rs +142 -0
  23. data/ext/itsi_acme/src/jose.rs +161 -0
  24. data/ext/itsi_acme/src/lib.rs +142 -0
  25. data/ext/itsi_acme/src/resolver.rs +59 -0
  26. data/ext/itsi_acme/src/state.rs +424 -0
  27. data/ext/itsi_error/Cargo.toml +1 -0
  28. data/ext/itsi_error/src/lib.rs +106 -7
  29. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  30. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  31. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  32. data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  33. data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
  34. data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
  35. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
  36. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
  37. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
  38. data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
  39. data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
  40. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
  41. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
  42. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
  43. data/ext/itsi_rb_helpers/Cargo.toml +1 -0
  44. data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
  45. data/ext/itsi_rb_helpers/src/lib.rs +63 -12
  46. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  47. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  48. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  49. 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
  50. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
  51. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
  52. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
  53. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
  54. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
  55. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
  56. data/ext/itsi_scheduler/Cargo.toml +1 -1
  57. data/ext/itsi_scheduler/src/itsi_scheduler.rs +9 -3
  58. data/ext/itsi_scheduler/src/lib.rs +1 -0
  59. data/ext/itsi_server/Cargo.lock +2956 -0
  60. data/ext/itsi_server/Cargo.toml +73 -29
  61. data/ext/itsi_server/src/default_responses/mod.rs +11 -0
  62. data/ext/itsi_server/src/env.rs +43 -0
  63. data/ext/itsi_server/src/lib.rs +114 -75
  64. data/ext/itsi_server/src/prelude.rs +2 -0
  65. data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  66. data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +29 -8
  67. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +344 -0
  68. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +264 -0
  69. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +362 -0
  70. data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +84 -40
  71. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +233 -0
  72. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +565 -0
  73. data/ext/itsi_server/src/ruby_types/itsi_server.rs +86 -0
  74. data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
  75. data/ext/itsi_server/src/server/{bind.rs → binds/bind.rs} +59 -24
  76. data/ext/itsi_server/src/server/binds/listener.rs +444 -0
  77. data/ext/itsi_server/src/server/binds/mod.rs +4 -0
  78. data/ext/itsi_server/src/server/{tls → binds/tls}/locked_dir_cache.rs +57 -19
  79. data/ext/itsi_server/src/server/{tls.rs → binds/tls.rs} +120 -31
  80. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  81. data/ext/itsi_server/src/server/http_message_types.rs +97 -0
  82. data/ext/itsi_server/src/server/io_stream.rs +2 -1
  83. data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
  84. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
  85. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
  86. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
  87. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +94 -0
  88. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
  89. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
  90. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +316 -0
  91. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +301 -0
  92. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
  93. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
  94. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +192 -0
  95. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +171 -0
  96. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +198 -0
  97. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
  98. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +209 -0
  99. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  100. data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  101. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +116 -0
  102. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +411 -0
  103. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +142 -0
  104. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +55 -0
  105. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
  106. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -0
  107. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +126 -0
  108. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +187 -0
  109. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +55 -0
  110. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +173 -0
  111. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
  112. data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
  113. data/ext/itsi_server/src/server/mod.rs +7 -5
  114. data/ext/itsi_server/src/server/process_worker.rs +65 -14
  115. data/ext/itsi_server/src/server/redirect_type.rs +26 -0
  116. data/ext/itsi_server/src/server/request_job.rs +11 -0
  117. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +150 -50
  118. data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
  119. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +399 -165
  120. data/ext/itsi_server/src/server/signal.rs +33 -26
  121. data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
  122. data/ext/itsi_server/src/server/thread_worker.rs +218 -107
  123. data/ext/itsi_server/src/services/cache_store.rs +74 -0
  124. data/ext/itsi_server/src/services/itsi_http_service.rs +257 -0
  125. data/ext/itsi_server/src/services/mime_types.rs +1416 -0
  126. data/ext/itsi_server/src/services/mod.rs +6 -0
  127. data/ext/itsi_server/src/services/password_hasher.rs +83 -0
  128. data/ext/itsi_server/src/services/rate_limiter.rs +580 -0
  129. data/ext/itsi_server/src/services/static_file_server.rs +1340 -0
  130. data/ext/itsi_tracing/Cargo.toml +1 -0
  131. data/ext/itsi_tracing/src/lib.rs +362 -33
  132. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
  133. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
  134. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
  135. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
  136. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
  137. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
  138. data/itsi-scheduler-100.png +0 -0
  139. data/lib/itsi/scheduler/version.rb +1 -1
  140. data/lib/itsi/scheduler.rb +11 -6
  141. metadata +117 -24
  142. data/CHANGELOG.md +0 -5
  143. data/CODE_OF_CONDUCT.md +0 -132
  144. data/LICENSE.txt +0 -21
  145. data/ext/itsi_error/src/from.rs +0 -71
  146. data/ext/itsi_server/extconf.rb +0 -6
  147. data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  148. data/ext/itsi_server/src/request/itsi_request.rs +0 -277
  149. data/ext/itsi_server/src/request/mod.rs +0 -1
  150. data/ext/itsi_server/src/response/mod.rs +0 -1
  151. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -13
  152. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -5
  153. data/ext/itsi_server/src/server/itsi_server.rs +0 -244
  154. data/ext/itsi_server/src/server/listener.rs +0 -327
  155. /data/ext/itsi_server/src/server/{bind_protocol.rs → binds/bind_protocol.rs} +0 -0
@@ -0,0 +1,209 @@
1
+ use crate::server::http_message_types::{HttpRequest, HttpResponse, RequestExt};
2
+ use crate::services::itsi_http_service::HttpRequestContext;
3
+ use crate::services::rate_limiter::{
4
+ get_ban_manager, get_rate_limiter, BanManager, RateLimiter, RateLimiterConfig,
5
+ };
6
+
7
+ use super::token_source::TokenSource;
8
+ use super::{ErrorResponse, FromValue, MiddlewareLayer};
9
+
10
+ use async_trait::async_trait;
11
+ use either::Either;
12
+ use itsi_tracing::*;
13
+ use magnus::error::Result;
14
+ use regex::RegexSet;
15
+ use serde::Deserialize;
16
+ use std::time::Duration;
17
+ use std::{
18
+ collections::HashMap,
19
+ sync::{Arc, OnceLock},
20
+ };
21
+
22
+ #[derive(Debug, Clone, Deserialize)]
23
+ pub struct IntrusionProtection {
24
+ #[serde(skip_deserializing)]
25
+ pub banned_url_pattern_matcher: OnceLock<RegexSet>,
26
+ #[serde(default)]
27
+ pub banned_url_patterns: Vec<String>,
28
+ #[serde(skip_deserializing)]
29
+ pub banned_header_pattern_matchers: OnceLock<HashMap<String, RegexSet>>,
30
+ #[serde(default)]
31
+ pub banned_header_patterns: HashMap<String, Vec<String>>,
32
+ pub banned_time_seconds: f64,
33
+ #[serde(skip_deserializing)]
34
+ pub rate_limiter: OnceLock<Arc<dyn RateLimiter>>,
35
+ #[serde(skip_deserializing)]
36
+ pub ban_manager: OnceLock<BanManager>,
37
+ pub store_config: RateLimiterConfig,
38
+ pub trusted_proxies: HashMap<String, TokenSource>,
39
+ #[serde(default = "forbidden_error_response")]
40
+ pub error_response: ErrorResponse,
41
+ }
42
+
43
+ fn forbidden_error_response() -> ErrorResponse {
44
+ ErrorResponse::forbidden()
45
+ }
46
+
47
+ #[async_trait]
48
+ impl MiddlewareLayer for IntrusionProtection {
49
+ async fn initialize(&self) -> Result<()> {
50
+ // Initialize regex matchers for URL patterns
51
+ if !self.banned_url_patterns.is_empty() {
52
+ match RegexSet::new(&self.banned_url_patterns) {
53
+ Ok(regex_set) => {
54
+ debug!(target: "middleware::intrusion_protection", "Compiled URL regex patterns: {} items.", regex_set.len());
55
+ let _ = self.banned_url_pattern_matcher.set(regex_set);
56
+ }
57
+ Err(e) => {
58
+ error!("Failed to compile URL regex patterns: {:?}", e);
59
+ }
60
+ }
61
+ }
62
+
63
+ // Initialize regex matchers for header patterns
64
+ if !self.banned_header_patterns.is_empty() {
65
+ let mut header_matchers = HashMap::new();
66
+ for (header_name, patterns) in &self.banned_header_patterns {
67
+ if !patterns.is_empty() {
68
+ match RegexSet::new(patterns) {
69
+ Ok(regex_set) => {
70
+ debug!(target: "middleware::intrusion_protection", "Compiled header regex patterns for {}: {} items.", header_name, regex_set.len());
71
+ header_matchers.insert(header_name.clone(), regex_set);
72
+ }
73
+ Err(e) => {
74
+ error!(
75
+ "Failed to compile header regex patterns for {}: {:?}",
76
+ header_name, e
77
+ );
78
+ }
79
+ }
80
+ }
81
+ }
82
+ let _ = self.banned_header_pattern_matchers.set(header_matchers);
83
+ }
84
+
85
+ // Initialize rate limiter (used for tracking bans)
86
+ // This will automatically fall back to in-memory if Redis fails
87
+ if let Ok(limiter) = get_rate_limiter(&self.store_config).await {
88
+ debug!(target: "middleware::intrusion_protection", "Initialized rate limiter.");
89
+ let _ = self.rate_limiter.set(limiter);
90
+ }
91
+
92
+ // Initialize ban manager
93
+ // This will automatically fall back to in-memory if Redis fails
94
+ if let Ok(manager) = get_ban_manager(&self.store_config).await {
95
+ debug!(target: "middleware::intrusion_protection", "Initialized ban manager.");
96
+ let _ = self.ban_manager.set(manager);
97
+ }
98
+
99
+ Ok(())
100
+ }
101
+
102
+ async fn before(
103
+ &self,
104
+ req: HttpRequest,
105
+ context: &mut HttpRequestContext,
106
+ ) -> Result<Either<HttpRequest, HttpResponse>> {
107
+ // Get client IP address from context's service
108
+ let client_ip = if self.trusted_proxies.contains_key(&context.addr) {
109
+ let source = self.trusted_proxies.get(&context.addr).unwrap();
110
+ source.extract_token(&req).unwrap_or(&context.addr)
111
+ } else {
112
+ &context.addr
113
+ };
114
+
115
+ // Check if the IP is already banned
116
+ if let Some(ban_manager) = self.ban_manager.get() {
117
+ match ban_manager.is_banned(client_ip).await {
118
+ Ok(Some(_)) => {
119
+ debug!(target: "middleware::intrusion_protection", "IP {} is banned.", client_ip);
120
+ return Ok(Either::Right(
121
+ self.error_response
122
+ .to_http_response(req.accept().into())
123
+ .await,
124
+ ));
125
+ }
126
+ Err(e) => {
127
+ error!("Error checking IP ban status: {:?}", e);
128
+ // Continue processing - fail open
129
+ }
130
+ _ => {}
131
+ }
132
+ } else {
133
+ warn!("No ban manager available for intrusion protection");
134
+ }
135
+
136
+ // Check for banned URL patterns
137
+ if let Some(url_matcher) = self.banned_url_pattern_matcher.get() {
138
+ let path = req.uri().path_and_query().map(|p| p.as_str()).unwrap_or("");
139
+
140
+ if url_matcher.is_match(path) {
141
+ debug!(target: "middleware::intrusion_protection", "Banned URL pattern detected: {}", path);
142
+ if let Some(ban_manager) = self.ban_manager.get() {
143
+ match ban_manager
144
+ .ban_ip(
145
+ client_ip,
146
+ &format!("Banned URL pattern detected: {}", path),
147
+ Duration::from_secs_f64(self.banned_time_seconds),
148
+ )
149
+ .await
150
+ {
151
+ Ok(_) => {}
152
+ Err(e) => error!("Failed to ban IP {}: {:?}", client_ip, e),
153
+ }
154
+ }
155
+
156
+ // Always return the error response even if banning failed
157
+ return Ok(Either::Right(
158
+ self.error_response
159
+ .to_http_response(req.accept().into())
160
+ .await,
161
+ ));
162
+ }
163
+ }
164
+
165
+ // Check for banned header patterns
166
+ if let Some(header_matchers) = self.banned_header_pattern_matchers.get() {
167
+ for (header_name, pattern_set) in header_matchers {
168
+ if let Some(header_value) = req.header(header_name) {
169
+ if pattern_set.is_match(header_value) {
170
+ debug!(target: "middleware::intrusion_protection", "Banned header pattern detected: {} in {}", header_value, header_name);
171
+
172
+ // Ban the IP address if possible
173
+ if let Some(ban_manager) = self.ban_manager.get() {
174
+ match ban_manager
175
+ .ban_ip(
176
+ client_ip,
177
+ &format!(
178
+ "Banned header pattern detected: {} in {}",
179
+ header_value, header_name
180
+ ),
181
+ Duration::from_secs_f64(self.banned_time_seconds),
182
+ )
183
+ .await
184
+ {
185
+ Ok(_) => info!(
186
+ "Successfully banned IP {} for {} seconds",
187
+ client_ip, self.banned_time_seconds
188
+ ),
189
+ Err(e) => error!("Failed to ban IP {}: {:?}", client_ip, e),
190
+ }
191
+ }
192
+
193
+ // Always return the error response even if banning failed
194
+ return Ok(Either::Right(
195
+ self.error_response
196
+ .to_http_response(req.accept().into())
197
+ .await,
198
+ ));
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ // No intrusion detected
205
+ Ok(Either::Left(req))
206
+ }
207
+ }
208
+
209
+ impl FromValue for IntrusionProtection {}
@@ -0,0 +1,82 @@
1
+ use async_trait::async_trait;
2
+ use either::Either;
3
+ use itsi_tracing::*;
4
+ use magnus::error::Result;
5
+ use serde::Deserialize;
6
+
7
+ use crate::server::http_message_types::{HttpRequest, HttpResponse};
8
+ use crate::services::itsi_http_service::HttpRequestContext;
9
+
10
+ use super::string_rewrite::StringRewrite;
11
+ use super::{FromValue, MiddlewareLayer};
12
+
13
+ /// Logging middleware for HTTP requests and responses
14
+ ///
15
+ /// Supports customizable log formats with placeholders
16
+ #[derive(Debug, Clone, Deserialize)]
17
+ pub struct LogRequests {
18
+ pub before: Option<LogConfig>,
19
+ pub after: Option<LogConfig>,
20
+ }
21
+
22
+ #[derive(Debug, Clone, Deserialize)]
23
+ pub struct LogConfig {
24
+ level: LogMiddlewareLevel,
25
+ format: StringRewrite,
26
+ }
27
+
28
+ #[derive(Debug, Clone, Deserialize)]
29
+ pub enum LogMiddlewareLevel {
30
+ #[serde(rename(deserialize = "INFO"))]
31
+ Info,
32
+ #[serde(rename(deserialize = "TRACE"))]
33
+ Trace,
34
+ #[serde(rename(deserialize = "DEBUG"))]
35
+ Debug,
36
+ #[serde(rename(deserialize = "WARN"))]
37
+ Warn,
38
+ #[serde(rename(deserialize = "ERROR"))]
39
+ Error,
40
+ }
41
+
42
+ impl LogMiddlewareLevel {
43
+ pub fn log(&self, message: String) {
44
+ match self {
45
+ LogMiddlewareLevel::Trace => trace!(target: "middleware::log_requests", message),
46
+ LogMiddlewareLevel::Debug => debug!(target: "middleware::log_requests", message),
47
+ LogMiddlewareLevel::Info => info!(target: "middleware::log_requests", message),
48
+ LogMiddlewareLevel::Warn => warn!(target: "middleware::log_requests", message),
49
+ LogMiddlewareLevel::Error => error!(target: "middleware::log_requests", message),
50
+ }
51
+ }
52
+ }
53
+
54
+ #[async_trait]
55
+ impl MiddlewareLayer for LogRequests {
56
+ async fn initialize(&self) -> Result<()> {
57
+ Ok(())
58
+ }
59
+
60
+ async fn before(
61
+ &self,
62
+ req: HttpRequest,
63
+ context: &mut HttpRequestContext,
64
+ ) -> Result<Either<HttpRequest, HttpResponse>> {
65
+ context.track_start_time();
66
+ if let Some(LogConfig { level, format }) = self.before.as_ref() {
67
+ level.log(format.rewrite_request(&req, context));
68
+ }
69
+
70
+ Ok(Either::Left(req))
71
+ }
72
+
73
+ async fn after(&self, resp: HttpResponse, context: &mut HttpRequestContext) -> HttpResponse {
74
+ if let Some(LogConfig { level, format }) = self.after.as_ref() {
75
+ level.log(format.rewrite_response(&resp, context));
76
+ }
77
+
78
+ resp
79
+ }
80
+ }
81
+
82
+ impl FromValue for LogRequests {}
@@ -0,0 +1,47 @@
1
+ use crate::{
2
+ server::http_message_types::{HttpRequest, HttpResponse, RequestExt},
3
+ services::itsi_http_service::HttpRequestContext,
4
+ };
5
+
6
+ use super::{ErrorResponse, FromValue, MiddlewareLayer};
7
+ use async_trait::async_trait;
8
+ use either::Either;
9
+ use http::StatusCode;
10
+ use magnus::error::Result;
11
+ use serde::Deserialize;
12
+ use std::sync::atomic::Ordering;
13
+
14
+ #[derive(Debug, Clone, Deserialize)]
15
+ pub struct MaxBody {
16
+ pub limit_bytes: usize,
17
+ #[serde(default = "payload_too_large_error_response")]
18
+ pub error_response: ErrorResponse,
19
+ }
20
+
21
+ fn payload_too_large_error_response() -> ErrorResponse {
22
+ ErrorResponse::payload_too_large()
23
+ }
24
+
25
+ #[async_trait]
26
+ impl MiddlewareLayer for MaxBody {
27
+ async fn before(
28
+ &self,
29
+ req: HttpRequest,
30
+ context: &mut HttpRequestContext,
31
+ ) -> Result<Either<HttpRequest, HttpResponse>> {
32
+ req.body().limit.store(self.limit_bytes, Ordering::Relaxed);
33
+ context.set_response_format(req.accept().into());
34
+ Ok(Either::Left(req))
35
+ }
36
+
37
+ async fn after(&self, resp: HttpResponse, context: &mut HttpRequestContext) -> HttpResponse {
38
+ if resp.status() == StatusCode::PAYLOAD_TOO_LARGE {
39
+ self.error_response
40
+ .to_http_response(context.response_format().clone())
41
+ .await
42
+ } else {
43
+ resp
44
+ }
45
+ }
46
+ }
47
+ impl FromValue for MaxBody {}
@@ -0,0 +1,116 @@
1
+ mod allow_list;
2
+ mod auth_api_key;
3
+ mod auth_basic;
4
+ mod auth_jwt;
5
+ mod cache_control;
6
+ mod compression;
7
+ mod cors;
8
+ mod csp;
9
+ mod deny_list;
10
+ mod error_response;
11
+ mod etag;
12
+ mod header_interpretation;
13
+ mod intrusion_protection;
14
+ mod log_requests;
15
+ mod max_body;
16
+ mod proxy;
17
+ mod rate_limit;
18
+ mod redirect;
19
+ mod request_headers;
20
+ mod response_headers;
21
+ mod ruby_app;
22
+ mod static_assets;
23
+ mod static_response;
24
+ mod string_rewrite;
25
+ mod token_source;
26
+
27
+ use std::sync::Arc;
28
+ use std::sync::LazyLock;
29
+
30
+ pub use allow_list::AllowList;
31
+ use async_trait::async_trait;
32
+ pub use auth_api_key::AuthAPIKey;
33
+ pub use auth_basic::AuthBasic;
34
+ pub use auth_jwt::AuthJwt;
35
+ pub use cache_control::CacheControl;
36
+ pub use compression::Compression;
37
+ pub use compression::CompressionAlgorithm;
38
+ pub use cors::Cors;
39
+ pub use csp::Csp;
40
+ pub use deny_list::DenyList;
41
+ use either::Either;
42
+ pub use error_response::ErrorResponse;
43
+ pub use etag::ETag;
44
+ pub use intrusion_protection::IntrusionProtection;
45
+ pub use log_requests::LogRequests;
46
+ use magnus::error::Result;
47
+ use magnus::rb_sys::AsRawValue;
48
+ use magnus::Value;
49
+ pub use max_body::MaxBody;
50
+ pub use proxy::Proxy;
51
+ pub use rate_limit::RateLimit;
52
+ pub use redirect::Redirect;
53
+ pub use request_headers::RequestHeaders;
54
+ pub use response_headers::ResponseHeaders;
55
+ pub use ruby_app::RubyApp;
56
+ use serde::Deserialize;
57
+ use serde_magnus::deserialize;
58
+ pub use static_assets::StaticAssets;
59
+ pub use static_response::StaticResponse;
60
+ pub use string_rewrite::StringRewrite;
61
+
62
+ use crate::server::http_message_types::HttpRequest;
63
+ use crate::server::http_message_types::HttpResponse;
64
+ use crate::services::itsi_http_service::HttpRequestContext;
65
+
66
+ use std::collections::HashMap;
67
+ use std::sync::Mutex;
68
+ static CACHE: LazyLock<Mutex<HashMap<u64, Arc<dyn std::any::Any + Send + Sync>>>> =
69
+ LazyLock::new(|| Mutex::new(HashMap::new()));
70
+
71
+ pub fn clear_value_cache() {
72
+ let mut cache = CACHE.lock().unwrap();
73
+ cache.clear();
74
+ }
75
+
76
+ pub trait FromValue: Sized + Send + Sync + 'static {
77
+ fn from_value(value: Value) -> Result<Arc<Self>>
78
+ where
79
+ Self: Deserialize<'static>,
80
+ {
81
+ let raw = value.as_raw();
82
+
83
+ let mut cache = CACHE.lock().unwrap();
84
+
85
+ if let Some(cached) = cache.get(&raw) {
86
+ if let Some(deserialized) = cached.downcast_ref::<Arc<Self>>() {
87
+ return Ok(deserialized.clone());
88
+ }
89
+ }
90
+
91
+ let deserialized: Arc<Self> = Arc::new(deserialize(value)?);
92
+ cache.insert(raw, deserialized.clone());
93
+ Ok(deserialized)
94
+ }
95
+ }
96
+
97
+ #[async_trait]
98
+ pub trait MiddlewareLayer: Sized + Send + Sync + 'static {
99
+ /// Called just once, to initialize the middleware state.
100
+ async fn initialize(&self) -> Result<()> {
101
+ Ok(())
102
+ }
103
+ /// The "before" hook. By default, it passes through the request.
104
+ async fn before(
105
+ &self,
106
+ req: HttpRequest,
107
+ _context: &mut HttpRequestContext,
108
+ ) -> Result<Either<HttpRequest, HttpResponse>> {
109
+ Ok(Either::Left(req))
110
+ }
111
+
112
+ /// The "after" hook. By default, it passes through the response.
113
+ async fn after(&self, resp: HttpResponse, _context: &mut HttpRequestContext) -> HttpResponse {
114
+ resp
115
+ }
116
+ }