itsi 0.1.14 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Cargo.lock +124 -109
- data/Cargo.toml +6 -0
- data/crates/itsi_error/Cargo.toml +1 -0
- data/crates/itsi_error/src/lib.rs +100 -10
- data/crates/itsi_scheduler/src/itsi_scheduler.rs +1 -1
- data/crates/itsi_server/Cargo.toml +8 -10
- data/crates/itsi_server/src/default_responses/html/401.html +68 -0
- data/crates/itsi_server/src/default_responses/html/403.html +68 -0
- data/crates/itsi_server/src/default_responses/html/404.html +68 -0
- data/crates/itsi_server/src/default_responses/html/413.html +71 -0
- data/crates/itsi_server/src/default_responses/html/429.html +68 -0
- data/crates/itsi_server/src/default_responses/html/500.html +71 -0
- data/crates/itsi_server/src/default_responses/html/502.html +71 -0
- data/crates/itsi_server/src/default_responses/html/503.html +68 -0
- data/crates/itsi_server/src/default_responses/html/504.html +69 -0
- data/crates/itsi_server/src/default_responses/html/index.html +238 -0
- data/crates/itsi_server/src/default_responses/json/401.json +6 -0
- data/crates/itsi_server/src/default_responses/json/403.json +6 -0
- data/crates/itsi_server/src/default_responses/json/404.json +6 -0
- data/crates/itsi_server/src/default_responses/json/413.json +6 -0
- data/crates/itsi_server/src/default_responses/json/429.json +6 -0
- data/crates/itsi_server/src/default_responses/json/500.json +6 -0
- data/crates/itsi_server/src/default_responses/json/502.json +6 -0
- data/crates/itsi_server/src/default_responses/json/503.json +6 -0
- data/crates/itsi_server/src/default_responses/json/504.json +6 -0
- data/crates/itsi_server/src/default_responses/mod.rs +11 -0
- data/crates/itsi_server/src/lib.rs +58 -26
- data/crates/itsi_server/src/prelude.rs +2 -0
- data/crates/itsi_server/src/ruby_types/README.md +21 -0
- data/crates/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +8 -6
- data/crates/itsi_server/src/ruby_types/itsi_grpc_call.rs +344 -0
- data/crates/itsi_server/src/ruby_types/{itsi_grpc_stream → itsi_grpc_response_stream}/mod.rs +121 -73
- data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +103 -40
- data/crates/itsi_server/src/ruby_types/itsi_http_response.rs +8 -5
- data/crates/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +4 -4
- data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +37 -17
- data/crates/itsi_server/src/ruby_types/itsi_server.rs +4 -3
- data/crates/itsi_server/src/ruby_types/mod.rs +6 -13
- data/crates/itsi_server/src/server/{bind.rs → binds/bind.rs} +23 -4
- data/crates/itsi_server/src/server/{listener.rs → binds/listener.rs} +24 -10
- data/crates/itsi_server/src/server/binds/mod.rs +4 -0
- data/crates/itsi_server/src/server/{tls.rs → binds/tls.rs} +9 -4
- data/crates/itsi_server/src/server/http_message_types.rs +97 -0
- data/crates/itsi_server/src/server/io_stream.rs +2 -1
- data/crates/itsi_server/src/server/middleware_stack/middleware.rs +28 -16
- data/crates/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +17 -8
- data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +47 -18
- data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +13 -9
- data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +50 -29
- data/crates/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +5 -2
- data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +37 -48
- data/crates/itsi_server/src/server/middleware_stack/middlewares/cors.rs +25 -20
- data/crates/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +14 -7
- data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +190 -0
- data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +125 -95
- data/crates/itsi_server/src/server/middleware_stack/middlewares/etag.rs +9 -5
- data/crates/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +1 -4
- data/crates/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +25 -19
- data/crates/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +4 -4
- data/crates/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
- data/crates/itsi_server/src/server/middleware_stack/middlewares/mod.rs +9 -4
- data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +260 -62
- data/crates/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +29 -22
- data/crates/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +6 -6
- data/crates/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +6 -5
- data/crates/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +4 -2
- data/crates/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +51 -18
- data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +31 -13
- data/crates/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +55 -0
- data/crates/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +13 -8
- data/crates/itsi_server/src/server/middleware_stack/mod.rs +101 -69
- data/crates/itsi_server/src/server/mod.rs +3 -9
- data/crates/itsi_server/src/server/process_worker.rs +21 -3
- data/crates/itsi_server/src/server/request_job.rs +2 -2
- data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +8 -3
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +26 -26
- data/crates/itsi_server/src/server/signal.rs +24 -41
- data/crates/itsi_server/src/server/size_limited_incoming.rs +101 -0
- data/crates/itsi_server/src/server/thread_worker.rs +59 -28
- data/crates/itsi_server/src/services/itsi_http_service.rs +239 -0
- data/crates/itsi_server/src/services/mime_types.rs +1416 -0
- data/crates/itsi_server/src/services/mod.rs +6 -0
- data/crates/itsi_server/src/services/password_hasher.rs +83 -0
- data/crates/itsi_server/src/{server → services}/rate_limiter.rs +35 -31
- data/crates/itsi_server/src/{server → services}/static_file_server.rs +521 -181
- data/crates/itsi_tracing/src/lib.rs +145 -55
- data/{Itsi.rb → foo/Itsi.rb} +6 -9
- data/gems/scheduler/Cargo.lock +7 -0
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/test/helpers/test_helper.rb +0 -1
- data/gems/scheduler/test/test_address_resolve.rb +0 -1
- data/gems/scheduler/test/test_network_io.rb +1 -1
- data/gems/scheduler/test/test_process_wait.rb +0 -1
- data/gems/server/Cargo.lock +124 -109
- data/gems/server/exe/itsi +65 -19
- data/gems/server/itsi-server.gemspec +4 -3
- data/gems/server/lib/itsi/http_request/response_status_shortcodes.rb +74 -0
- data/gems/server/lib/itsi/http_request.rb +116 -17
- data/gems/server/lib/itsi/http_response.rb +2 -0
- data/gems/server/lib/itsi/passfile.rb +109 -0
- data/gems/server/lib/itsi/server/config/dsl.rb +160 -101
- data/gems/server/lib/itsi/server/config.rb +58 -23
- data/gems/server/lib/itsi/server/default_app/default_app.rb +25 -29
- data/gems/server/lib/itsi/server/default_app/index.html +113 -89
- data/gems/server/lib/itsi/server/{Itsi.rb → default_config/Itsi-rackup.rb} +1 -1
- data/gems/server/lib/itsi/server/default_config/Itsi.rb +107 -0
- data/gems/server/lib/itsi/server/grpc/grpc_call.rb +246 -0
- data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +100 -0
- data/gems/server/lib/itsi/server/grpc/reflection/v1/reflection_pb.rb +26 -0
- data/gems/server/lib/itsi/server/grpc/reflection/v1/reflection_services_pb.rb +122 -0
- data/gems/server/lib/itsi/server/route_tester.rb +107 -0
- data/gems/server/lib/itsi/server/typed_handlers/param_parser.rb +200 -0
- data/gems/server/lib/itsi/server/typed_handlers/source_parser.rb +55 -0
- data/gems/server/lib/itsi/server/typed_handlers.rb +17 -0
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +82 -12
- data/gems/server/lib/ruby_lsp/itsi/addon.rb +111 -0
- data/gems/server/lib/shell_completions/completions.rb +26 -0
- data/gems/server/test/helpers/test_helper.rb +2 -1
- data/lib/itsi/version.rb +1 -1
- data/sandbox/README.md +5 -0
- data/sandbox/itsi_file/Gemfile +4 -2
- data/sandbox/itsi_file/Gemfile.lock +48 -6
- data/sandbox/itsi_file/Itsi.rb +326 -129
- data/sandbox/itsi_file/call.json +1 -0
- data/sandbox/itsi_file/echo_client/Gemfile +10 -0
- data/sandbox/itsi_file/echo_client/Gemfile.lock +27 -0
- data/sandbox/itsi_file/echo_client/README.md +95 -0
- data/sandbox/itsi_file/echo_client/echo_client.rb +164 -0
- data/sandbox/itsi_file/echo_client/gen_proto.sh +17 -0
- data/sandbox/itsi_file/echo_client/lib/echo_pb.rb +16 -0
- data/sandbox/itsi_file/echo_client/lib/echo_services_pb.rb +29 -0
- data/sandbox/itsi_file/echo_client/run_client.rb +64 -0
- data/sandbox/itsi_file/echo_client/test_compressions.sh +20 -0
- data/sandbox/itsi_file/echo_service_nonitsi/Gemfile +10 -0
- data/sandbox/itsi_file/echo_service_nonitsi/Gemfile.lock +79 -0
- data/sandbox/itsi_file/echo_service_nonitsi/echo.proto +26 -0
- data/sandbox/itsi_file/echo_service_nonitsi/echo_pb.rb +16 -0
- data/sandbox/itsi_file/echo_service_nonitsi/echo_services_pb.rb +29 -0
- data/sandbox/itsi_file/echo_service_nonitsi/server.rb +52 -0
- data/sandbox/itsi_sandbox_async/config.ru +0 -1
- data/sandbox/itsi_sandbox_rack/Gemfile.lock +2 -2
- data/sandbox/itsi_sandbox_rails/Gemfile +2 -2
- data/sandbox/itsi_sandbox_rails/Gemfile.lock +76 -2
- data/sandbox/itsi_sandbox_rails/app/controllers/home_controller.rb +15 -0
- data/sandbox/itsi_sandbox_rails/config/environments/development.rb +1 -0
- data/sandbox/itsi_sandbox_rails/config/environments/production.rb +1 -0
- data/sandbox/itsi_sandbox_rails/config/routes.rb +2 -0
- data/sandbox/itsi_sinatra/app.rb +0 -1
- data/sandbox/static_files/.env +1 -0
- data/sandbox/static_files/404.html +25 -0
- data/sandbox/static_files/_DSC0102.NEF.jpg +0 -0
- data/sandbox/static_files/about.html +68 -0
- data/sandbox/static_files/tiny.html +1 -0
- data/sandbox/static_files/writebook.zip +0 -0
- data/tasks.txt +28 -33
- metadata +87 -26
- data/crates/itsi_error/src/from.rs +0 -68
- data/crates/itsi_server/src/ruby_types/itsi_grpc_request.rs +0 -147
- data/crates/itsi_server/src/ruby_types/itsi_grpc_response.rs +0 -19
- data/crates/itsi_server/src/server/itsi_service.rs +0 -172
- data/crates/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +0 -72
- data/crates/itsi_server/src/server/types.rs +0 -43
- data/gems/server/lib/itsi/server/grpc_interface.rb +0 -213
- data/sandbox/itsi_file/public/assets/index.html +0 -1
- /data/crates/itsi_server/src/server/{bind_protocol.rs → binds/bind_protocol.rs} +0 -0
- /data/crates/itsi_server/src/server/{tls → binds/tls}/locked_dir_cache.rs +0 -0
- /data/crates/itsi_server/src/{server → services}/cache_store.rs +0 -0
@@ -1,19 +1,17 @@
|
|
1
1
|
use super::MiddlewareLayer;
|
2
|
-
use crate::ruby_types::
|
3
|
-
use crate::
|
4
|
-
use crate::{
|
5
|
-
|
6
|
-
|
7
|
-
itsi_service::RequestContext,
|
8
|
-
types::{HttpRequest, HttpResponse},
|
9
|
-
},
|
10
|
-
};
|
2
|
+
use crate::ruby_types::itsi_grpc_call::ItsiGrpcCall;
|
3
|
+
use crate::ruby_types::itsi_http_request::ItsiHttpRequest;
|
4
|
+
use crate::server::http_message_types::{HttpRequest, HttpResponse};
|
5
|
+
use crate::services::itsi_http_service::HttpRequestContext;
|
6
|
+
use crate::services::static_file_server::ROOT_STATIC_FILE_SERVER;
|
11
7
|
use async_trait::async_trait;
|
12
8
|
use derive_more::Debug;
|
13
9
|
use either::Either;
|
14
10
|
use itsi_rb_helpers::{HeapVal, HeapValue};
|
15
11
|
use magnus::{block::Proc, error::Result, value::ReprValue, Symbol};
|
12
|
+
use regex::Regex;
|
16
13
|
use std::str::FromStr;
|
14
|
+
use std::sync::atomic::Ordering;
|
17
15
|
use std::sync::Arc;
|
18
16
|
|
19
17
|
#[derive(Debug)]
|
@@ -21,6 +19,8 @@ pub struct RubyApp {
|
|
21
19
|
app: Arc<HeapValue<Proc>>,
|
22
20
|
request_type: RequestType,
|
23
21
|
sendfile: bool,
|
22
|
+
nonblocking: bool,
|
23
|
+
base_path: Regex,
|
24
24
|
}
|
25
25
|
|
26
26
|
#[derive(Debug)]
|
@@ -47,6 +47,14 @@ impl RubyApp {
|
|
47
47
|
let sendfile = params
|
48
48
|
.funcall::<_, _, bool>(Symbol::new("[]"), ("sendfile",))
|
49
49
|
.unwrap_or(true);
|
50
|
+
let nonblocking = params
|
51
|
+
.funcall::<_, _, bool>(Symbol::new("[]"), ("nonblocking",))
|
52
|
+
.unwrap_or(false);
|
53
|
+
let base_path_src = params
|
54
|
+
.funcall::<_, _, String>(Symbol::new("[]"), ("base_path",))
|
55
|
+
.unwrap_or("".to_owned());
|
56
|
+
let base_path = Regex::new(&base_path_src).unwrap();
|
57
|
+
|
50
58
|
let request_type: RequestType = params
|
51
59
|
.funcall::<_, _, String>(Symbol::new("[]"), ("request_type",))
|
52
60
|
.unwrap_or("http".to_string())
|
@@ -56,7 +64,9 @@ impl RubyApp {
|
|
56
64
|
Ok(RubyApp {
|
57
65
|
app: Arc::new(app.into()),
|
58
66
|
sendfile,
|
67
|
+
nonblocking,
|
59
68
|
request_type,
|
69
|
+
base_path,
|
60
70
|
})
|
61
71
|
}
|
62
72
|
}
|
@@ -66,25 +76,48 @@ impl MiddlewareLayer for RubyApp {
|
|
66
76
|
async fn before(
|
67
77
|
&self,
|
68
78
|
req: HttpRequest,
|
69
|
-
context: &mut
|
79
|
+
context: &mut HttpRequestContext,
|
70
80
|
) -> Result<Either<HttpRequest, HttpResponse>> {
|
81
|
+
context.is_ruby_request.store(true, Ordering::SeqCst);
|
71
82
|
match self.request_type {
|
72
|
-
RequestType::Http =>
|
73
|
-
.
|
74
|
-
|
75
|
-
|
76
|
-
|
83
|
+
RequestType::Http => {
|
84
|
+
let uri = req.uri().path();
|
85
|
+
let script_name = self
|
86
|
+
.base_path
|
87
|
+
.captures(uri)
|
88
|
+
.and_then(|caps| caps.name("base_path"))
|
89
|
+
.map(|m| m.as_str())
|
90
|
+
.unwrap_or("/")
|
91
|
+
.to_owned();
|
92
|
+
ItsiHttpRequest::process_request(
|
93
|
+
self.app.clone(),
|
94
|
+
req,
|
95
|
+
context,
|
96
|
+
script_name,
|
97
|
+
self.nonblocking,
|
98
|
+
)
|
77
99
|
.await
|
78
100
|
.map_err(|e| e.into())
|
79
|
-
.map(Either::Right)
|
101
|
+
.map(Either::Right)
|
102
|
+
}
|
103
|
+
RequestType::Grpc => {
|
104
|
+
ItsiGrpcCall::process_request(self.app.clone(), req, context, self.nonblocking)
|
105
|
+
.await
|
106
|
+
.map_err(|e| e.into())
|
107
|
+
.map(Either::Right)
|
108
|
+
}
|
80
109
|
}
|
81
110
|
}
|
82
111
|
|
83
|
-
async fn after(&self, resp: HttpResponse,
|
112
|
+
async fn after(&self, resp: HttpResponse, context: &mut HttpRequestContext) -> HttpResponse {
|
84
113
|
if self.sendfile {
|
85
114
|
if let Some(sendfile_header) = resp.headers().get("X-Sendfile") {
|
86
115
|
return ROOT_STATIC_FILE_SERVER
|
87
|
-
.
|
116
|
+
.serve_single_abs(
|
117
|
+
sendfile_header.to_str().unwrap(),
|
118
|
+
context.accept.clone(),
|
119
|
+
&[],
|
120
|
+
)
|
88
121
|
.await;
|
89
122
|
}
|
90
123
|
}
|
@@ -1,8 +1,12 @@
|
|
1
1
|
use super::{FromValue, MiddlewareLayer};
|
2
|
-
use crate::
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
use crate::{
|
3
|
+
server::http_message_types::{HttpRequest, HttpResponse},
|
4
|
+
services::{
|
5
|
+
itsi_http_service::HttpRequestContext,
|
6
|
+
static_file_server::{
|
7
|
+
NotFoundBehavior, ServeRange, StaticFileServer, StaticFileServerConfig,
|
8
|
+
},
|
9
|
+
},
|
6
10
|
};
|
7
11
|
use async_trait::async_trait;
|
8
12
|
use either::Either;
|
@@ -12,6 +16,7 @@ use http::{
|
|
12
16
|
};
|
13
17
|
use itsi_error::ItsiError;
|
14
18
|
use magnus::error::Result;
|
19
|
+
use regex::Regex;
|
15
20
|
use serde::Deserialize;
|
16
21
|
use std::{collections::HashMap, path::PathBuf, sync::OnceLock, time::Duration};
|
17
22
|
|
@@ -25,7 +30,12 @@ pub struct StaticAssets {
|
|
25
30
|
pub max_files_in_memory: u64,
|
26
31
|
pub file_check_interval: u64,
|
27
32
|
pub headers: Option<HashMap<String, String>>,
|
33
|
+
pub allowed_extensions: Vec<String>,
|
28
34
|
pub relative_path: bool,
|
35
|
+
pub serve_hidden_files: bool,
|
36
|
+
pub base_path: String,
|
37
|
+
#[serde(skip)]
|
38
|
+
pub base_path_regex: OnceLock<Regex>,
|
29
39
|
#[serde(skip)]
|
30
40
|
file_server: OnceLock<StaticFileServer>,
|
31
41
|
}
|
@@ -46,6 +56,9 @@ impl MiddlewareLayer for StaticAssets {
|
|
46
56
|
"Root directory exists but is not a directory".to_string(),
|
47
57
|
))
|
48
58
|
}?;
|
59
|
+
self.base_path_regex
|
60
|
+
.set(Regex::new(&self.base_path).map_err(ItsiError::new)?)
|
61
|
+
.map_err(ItsiError::new)?;
|
49
62
|
self.file_server
|
50
63
|
.set(StaticFileServer::new(StaticFileServerConfig {
|
51
64
|
root_dir: self.root_dir.clone(),
|
@@ -55,15 +68,17 @@ impl MiddlewareLayer for StaticAssets {
|
|
55
68
|
try_html_extension: self.try_html_extension,
|
56
69
|
max_file_size: self.max_file_size_in_memory,
|
57
70
|
recheck_interval: Duration::from_secs(self.file_check_interval),
|
71
|
+
serve_hidden_files: self.serve_hidden_files,
|
72
|
+
allowed_extensions: self.allowed_extensions.clone(),
|
58
73
|
}))
|
59
|
-
.map_err(ItsiError::
|
74
|
+
.map_err(ItsiError::new)?;
|
60
75
|
Ok(())
|
61
76
|
}
|
62
77
|
|
63
78
|
async fn before(
|
64
79
|
&self,
|
65
80
|
req: HttpRequest,
|
66
|
-
context: &mut
|
81
|
+
context: &mut HttpRequestContext,
|
67
82
|
) -> Result<Either<HttpRequest, HttpResponse>> {
|
68
83
|
// Only handle GET and HEAD requests
|
69
84
|
if req.method() != Method::GET && req.method() != Method::HEAD {
|
@@ -73,13 +88,16 @@ impl MiddlewareLayer for StaticAssets {
|
|
73
88
|
let rel_path = if !self.relative_path {
|
74
89
|
abs_path
|
75
90
|
} else {
|
76
|
-
|
77
|
-
.
|
78
|
-
.
|
79
|
-
.
|
80
|
-
.
|
91
|
+
let base_path = self
|
92
|
+
.base_path_regex
|
93
|
+
.get()
|
94
|
+
.unwrap()
|
95
|
+
.captures(abs_path)
|
96
|
+
.and_then(|caps| caps.name("base_path"))
|
81
97
|
.map(|m| m.as_str())
|
82
|
-
|
98
|
+
.unwrap_or("/");
|
99
|
+
|
100
|
+
match abs_path.strip_prefix(base_path) {
|
83
101
|
Some(suffix) => suffix,
|
84
102
|
None => return Ok(Either::Left(req)),
|
85
103
|
}
|
@@ -106,9 +124,9 @@ impl MiddlewareLayer for StaticAssets {
|
|
106
124
|
serve_range,
|
107
125
|
if_modified_since,
|
108
126
|
is_head_request,
|
127
|
+
&context.supported_encoding_set,
|
109
128
|
)
|
110
129
|
.await;
|
111
|
-
|
112
130
|
if response.is_none() {
|
113
131
|
Ok(Either::Left(req))
|
114
132
|
} else {
|
@@ -0,0 +1,55 @@
|
|
1
|
+
use std::sync::OnceLock;
|
2
|
+
|
3
|
+
use super::{FromValue, MiddlewareLayer};
|
4
|
+
use crate::server::http_message_types::{HttpRequest, HttpResponse};
|
5
|
+
use crate::services::itsi_http_service::HttpRequestContext;
|
6
|
+
use async_trait::async_trait;
|
7
|
+
use bytes::Bytes;
|
8
|
+
use derive_more::Debug;
|
9
|
+
use either::Either;
|
10
|
+
use http::{HeaderMap, HeaderName, HeaderValue, Response, StatusCode};
|
11
|
+
use http_body_util::combinators::BoxBody;
|
12
|
+
use http_body_util::Full;
|
13
|
+
use itsi_error::ItsiError;
|
14
|
+
use magnus::error::Result;
|
15
|
+
use serde::Deserialize;
|
16
|
+
|
17
|
+
#[derive(Debug, Deserialize)]
|
18
|
+
pub struct StaticResponse {
|
19
|
+
code: u16,
|
20
|
+
headers: Vec<(String, String)>,
|
21
|
+
body: Vec<u8>,
|
22
|
+
#[serde(skip)]
|
23
|
+
header_map: OnceLock<HeaderMap>,
|
24
|
+
}
|
25
|
+
|
26
|
+
#[async_trait]
|
27
|
+
impl MiddlewareLayer for StaticResponse {
|
28
|
+
async fn initialize(&self) -> Result<()> {
|
29
|
+
let mut header_map = HeaderMap::new();
|
30
|
+
for (key, value) in self.headers.iter() {
|
31
|
+
if let (Ok(hn), Ok(hv)) = (key.parse::<HeaderName>(), value.parse::<HeaderValue>()) {
|
32
|
+
header_map.insert(hn, hv);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
self.header_map
|
36
|
+
.set(header_map)
|
37
|
+
.map_err(|_| ItsiError::new("Failed to set headers"))?;
|
38
|
+
Ok(())
|
39
|
+
}
|
40
|
+
|
41
|
+
async fn before(
|
42
|
+
&self,
|
43
|
+
_req: HttpRequest,
|
44
|
+
_context: &mut HttpRequestContext,
|
45
|
+
) -> Result<Either<HttpRequest, HttpResponse>> {
|
46
|
+
let mut resp = Response::new(BoxBody::new(Full::new(Bytes::from(self.body.clone()))));
|
47
|
+
let status = StatusCode::from_u16(self.code).unwrap_or(StatusCode::OK);
|
48
|
+
*resp.status_mut() = status;
|
49
|
+
*resp.headers_mut() = self.header_map.get().unwrap().clone();
|
50
|
+
|
51
|
+
Ok(Either::Right(resp))
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
impl FromValue for StaticResponse {}
|
@@ -1,10 +1,11 @@
|
|
1
|
-
use crate::server::{
|
2
|
-
itsi_service::RequestContext,
|
3
|
-
types::{HttpRequest, HttpResponse},
|
4
|
-
};
|
5
1
|
use serde::Deserialize;
|
6
2
|
use std::sync::OnceLock;
|
7
3
|
|
4
|
+
use crate::{
|
5
|
+
server::http_message_types::{HttpRequest, HttpResponse},
|
6
|
+
services::itsi_http_service::HttpRequestContext,
|
7
|
+
};
|
8
|
+
|
8
9
|
#[derive(Debug, Clone, Deserialize)]
|
9
10
|
#[serde(transparent)]
|
10
11
|
pub struct StringRewrite {
|
@@ -49,7 +50,7 @@ pub fn parse_template(template: &str) -> Vec<Segment> {
|
|
49
50
|
}
|
50
51
|
|
51
52
|
impl StringRewrite {
|
52
|
-
pub fn rewrite_request(&self, req: &HttpRequest, context: &
|
53
|
+
pub fn rewrite_request(&self, req: &HttpRequest, context: &HttpRequestContext) -> String {
|
53
54
|
let segments = self
|
54
55
|
.segments
|
55
56
|
.get_or_init(|| parse_template(&self.template_string));
|
@@ -65,9 +66,11 @@ impl StringRewrite {
|
|
65
66
|
Segment::Literal(text) => result.push_str(text),
|
66
67
|
Segment::Placeholder(placeholder) => {
|
67
68
|
let replacement = match placeholder.as_str() {
|
68
|
-
"request_id" => context.
|
69
|
+
"request_id" => context.short_request_id(),
|
70
|
+
"request_id_full" => context.request_id(),
|
69
71
|
"method" => req.method().as_str().to_string(),
|
70
72
|
"path" => req.uri().path().to_string(),
|
73
|
+
"addr" => context.addr.to_owned(),
|
71
74
|
"host" => req.uri().host().unwrap_or("localhost").to_string(),
|
72
75
|
"path_and_query" => req
|
73
76
|
.uri()
|
@@ -116,7 +119,7 @@ impl StringRewrite {
|
|
116
119
|
result
|
117
120
|
}
|
118
121
|
|
119
|
-
pub fn rewrite_response(&self, resp: &HttpResponse, context: &
|
122
|
+
pub fn rewrite_response(&self, resp: &HttpResponse, context: &HttpRequestContext) -> String {
|
120
123
|
let segments = self
|
121
124
|
.segments
|
122
125
|
.get_or_init(|| parse_template(&self.template_string));
|
@@ -127,8 +130,10 @@ impl StringRewrite {
|
|
127
130
|
Segment::Literal(text) => result.push_str(text),
|
128
131
|
Segment::Placeholder(placeholder) => {
|
129
132
|
let replacement = match placeholder.as_str() {
|
130
|
-
"request_id" => context.
|
133
|
+
"request_id" => context.short_request_id(),
|
134
|
+
"request_id_full" => context.request_id(),
|
131
135
|
"status" => resp.status().as_str().to_string(),
|
136
|
+
"addr" => context.addr.to_owned(),
|
132
137
|
"response_time" => {
|
133
138
|
if let Some(response_time) = context.get_response_time() {
|
134
139
|
if let Some(microseconds) = response_time.num_microseconds() {
|
@@ -1,6 +1,5 @@
|
|
1
1
|
mod middleware;
|
2
2
|
mod middlewares;
|
3
|
-
use super::types::HttpRequest;
|
4
3
|
use http::header::{ACCEPT, CONTENT_TYPE, HOST};
|
5
4
|
use itsi_rb_helpers::HeapVal;
|
6
5
|
use magnus::{error::Result, value::ReprValue, RArray, RHash, Ruby, TryConvert, Value};
|
@@ -8,14 +7,15 @@ pub use middleware::Middleware;
|
|
8
7
|
pub use middlewares::*;
|
9
8
|
use regex::{Regex, RegexSet};
|
10
9
|
use std::{collections::HashMap, sync::Arc};
|
11
|
-
use tracing::
|
10
|
+
use tracing::debug;
|
11
|
+
|
12
|
+
use super::http_message_types::HttpRequest;
|
12
13
|
|
13
14
|
#[derive(Debug)]
|
14
15
|
pub struct MiddlewareSet {
|
15
16
|
pub route_set: RegexSet,
|
16
17
|
pub patterns: Vec<Arc<Regex>>,
|
17
18
|
pub stacks: HashMap<usize, MiddlewareStack>,
|
18
|
-
pub default_stack: Vec<Middleware>,
|
19
19
|
}
|
20
20
|
|
21
21
|
#[derive(Debug)]
|
@@ -43,7 +43,7 @@ impl StringMatch {
|
|
43
43
|
let src_str = value.funcall::<_, _, String>("source", ())?;
|
44
44
|
let regex = Regex::new(&src_str).map_err(|e| {
|
45
45
|
magnus::Error::new(
|
46
|
-
magnus::exception::
|
46
|
+
magnus::exception::standard_error(),
|
47
47
|
format!("Invalid regexp: {}", e),
|
48
48
|
)
|
49
49
|
})?;
|
@@ -86,13 +86,15 @@ impl MiddlewareStack {
|
|
86
86
|
|
87
87
|
if let (Some(ports), Some(port)) = (&self.ports, request.uri().port()) {
|
88
88
|
if !ports.iter().any(|d| d.matches(port.as_str())) {
|
89
|
-
info!("No match between port {} and {:?}", port, ports);
|
90
89
|
return false;
|
91
90
|
}
|
92
91
|
}
|
93
92
|
|
94
93
|
if let Some(extensions) = &self.extensions {
|
95
|
-
let
|
94
|
+
let path = request.uri().path();
|
95
|
+
let segment = path.split('/').next_back().unwrap_or("");
|
96
|
+
let extension = segment.split('.').next_back().unwrap_or("");
|
97
|
+
let extension = if segment != extension { extension } else { "" };
|
96
98
|
if !extensions.iter().any(|e| e.matches(extension)) {
|
97
99
|
return false;
|
98
100
|
}
|
@@ -125,13 +127,13 @@ impl MiddlewareStack {
|
|
125
127
|
}
|
126
128
|
|
127
129
|
impl MiddlewareSet {
|
128
|
-
pub fn new(routes_raw: Option<HeapVal
|
130
|
+
pub fn new(routes_raw: Option<HeapVal>) -> Result<Self> {
|
129
131
|
if let Some(routes_raw) = routes_raw {
|
130
132
|
let mut stacks = HashMap::new();
|
131
133
|
let mut routes = vec![];
|
132
134
|
for (index, route) in RArray::from_value(*routes_raw)
|
133
135
|
.ok_or(magnus::Error::new(
|
134
|
-
magnus::exception::
|
136
|
+
magnus::exception::standard_error(),
|
135
137
|
format!("Routes must be an array. Got {:?}", routes_raw),
|
136
138
|
))?
|
137
139
|
.into_iter()
|
@@ -141,17 +143,17 @@ impl MiddlewareSet {
|
|
141
143
|
let route_raw = route_hash
|
142
144
|
.get("route")
|
143
145
|
.ok_or(magnus::Error::new(
|
144
|
-
magnus::exception::
|
146
|
+
magnus::exception::standard_error(),
|
145
147
|
"Route is missing :route key",
|
146
148
|
))?
|
147
149
|
.funcall::<_, _, String>("source", ())?;
|
148
150
|
let middleware =
|
149
151
|
RArray::from_value(route_hash.get("middleware").ok_or(magnus::Error::new(
|
150
|
-
magnus::exception::
|
152
|
+
magnus::exception::standard_error(),
|
151
153
|
"Route is missing middleware key",
|
152
154
|
))?)
|
153
155
|
.ok_or(magnus::Error::new(
|
154
|
-
magnus::exception::
|
156
|
+
magnus::exception::standard_error(),
|
155
157
|
format!("middleware must be an array. Got {:?}", routes_raw),
|
156
158
|
))?;
|
157
159
|
|
@@ -159,9 +161,6 @@ impl MiddlewareSet {
|
|
159
161
|
.into_iter()
|
160
162
|
.map(MiddlewareSet::parse_middleware)
|
161
163
|
.collect::<Result<Vec<_>>>()?;
|
162
|
-
layers.push(Middleware::RubyApp(RubyApp::from_value(
|
163
|
-
default_app.clone(),
|
164
|
-
)?));
|
165
164
|
routes.push(route_raw);
|
166
165
|
layers.sort();
|
167
166
|
stacks.insert(
|
@@ -178,11 +177,10 @@ impl MiddlewareSet {
|
|
178
177
|
},
|
179
178
|
);
|
180
179
|
}
|
181
|
-
info!("Routes are {:?}", routes);
|
182
180
|
Ok(Self {
|
183
181
|
route_set: RegexSet::new(&routes).map_err(|e| {
|
184
182
|
magnus::Error::new(
|
185
|
-
magnus::exception::
|
183
|
+
magnus::exception::standard_error(),
|
186
184
|
format!("Failed to create route set: {}", e),
|
187
185
|
)
|
188
186
|
})?,
|
@@ -192,7 +190,7 @@ impl MiddlewareSet {
|
|
192
190
|
.collect::<std::result::Result<Vec<Regex>, regex::Error>>()
|
193
191
|
.map_err(|e| {
|
194
192
|
magnus::Error::new(
|
195
|
-
magnus::exception::
|
193
|
+
magnus::exception::standard_error(),
|
196
194
|
format!("Failed to create route set: {}", e),
|
197
195
|
)
|
198
196
|
})?
|
@@ -200,97 +198,131 @@ impl MiddlewareSet {
|
|
200
198
|
.map(Arc::new)
|
201
199
|
.collect(),
|
202
200
|
stacks,
|
203
|
-
default_stack: vec![Middleware::RubyApp(RubyApp::from_value(default_app)?)],
|
204
201
|
})
|
205
202
|
} else {
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
default_stack: vec![Middleware::RubyApp(RubyApp::from_value(default_app)?)],
|
211
|
-
})
|
203
|
+
Err(magnus::Error::new(
|
204
|
+
magnus::exception::standard_error(),
|
205
|
+
"Failed to create middleware stack",
|
206
|
+
))
|
212
207
|
}
|
213
208
|
}
|
214
209
|
|
215
|
-
pub fn stack_for(
|
210
|
+
pub fn stack_for(
|
211
|
+
&self,
|
212
|
+
request: &HttpRequest,
|
213
|
+
) -> Result<(&Vec<Middleware>, Option<Arc<Regex>>)> {
|
216
214
|
let binding = self.route_set.matches(request.uri().path());
|
217
215
|
let matches = binding.iter();
|
216
|
+
|
217
|
+
debug!(
|
218
|
+
"Matching request URI {:?} against self.route_set: {:?}",
|
219
|
+
request.uri().path(),
|
220
|
+
self.route_set
|
221
|
+
);
|
218
222
|
for index in matches {
|
219
223
|
let matching_pattern = self.patterns.get(index).cloned();
|
220
224
|
if let Some(stack) = self.stacks.get(&index) {
|
221
225
|
if stack.matches(request) {
|
222
|
-
return (&stack.layers, matching_pattern);
|
226
|
+
return Ok((&stack.layers, matching_pattern));
|
223
227
|
}
|
224
228
|
}
|
225
229
|
}
|
226
|
-
(
|
230
|
+
debug!(
|
231
|
+
"Failed to match request URI {:?} to self.route_set: {:?}",
|
232
|
+
request.uri().path(),
|
233
|
+
self.route_set
|
234
|
+
);
|
235
|
+
Err(magnus::Error::new(
|
236
|
+
magnus::exception::standard_error(),
|
237
|
+
format!(
|
238
|
+
"No matching middleware stack found for request: {:?}",
|
239
|
+
request
|
240
|
+
),
|
241
|
+
))
|
227
242
|
}
|
228
243
|
|
229
244
|
pub fn parse_middleware(middleware: Value) -> Result<Middleware> {
|
230
245
|
let middleware_hash = RHash::from_value(middleware).ok_or(magnus::Error::new(
|
231
|
-
magnus::exception::
|
246
|
+
magnus::exception::standard_error(),
|
232
247
|
format!("Filter must be a hash. Got {:?}", middleware),
|
233
248
|
))?;
|
234
249
|
let middleware_type: String = middleware_hash
|
235
250
|
.get("type")
|
236
251
|
.ok_or(magnus::Error::new(
|
237
|
-
magnus::exception::
|
252
|
+
magnus::exception::standard_error(),
|
238
253
|
format!("Filter must have a :type key. Got {:?}", middleware_hash),
|
239
254
|
))?
|
240
255
|
.to_string();
|
256
|
+
let mw_type = middleware_type.clone();
|
241
257
|
|
242
258
|
let parameters: Value = middleware_hash.get("parameters").ok_or(magnus::Error::new(
|
243
|
-
magnus::exception::
|
259
|
+
magnus::exception::standard_error(),
|
244
260
|
format!(
|
245
261
|
"Filter must have a :parameters key. Got {:?}",
|
246
262
|
middleware_hash
|
247
263
|
),
|
248
264
|
))?;
|
249
265
|
|
250
|
-
let result =
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
266
|
+
let result = (move || -> Result<Middleware> {
|
267
|
+
match mw_type.as_str() {
|
268
|
+
"allow_list" => Ok(Middleware::AllowList(AllowList::from_value(parameters)?)),
|
269
|
+
"auth_basic" => Ok(Middleware::AuthBasic(AuthBasic::from_value(parameters)?)),
|
270
|
+
"auth_jwt" => Ok(Middleware::AuthJwt(Box::new(AuthJwt::from_value(
|
271
|
+
parameters,
|
272
|
+
)?))),
|
273
|
+
"auth_api_key" => Ok(Middleware::AuthAPIKey(AuthAPIKey::from_value(parameters)?)),
|
274
|
+
"cache_control" => Ok(Middleware::CacheControl(CacheControl::from_value(
|
275
|
+
parameters,
|
276
|
+
)?)),
|
277
|
+
"deny_list" => Ok(Middleware::DenyList(DenyList::from_value(parameters)?)),
|
278
|
+
"etag" => Ok(Middleware::ETag(ETag::from_value(parameters)?)),
|
279
|
+
"intrusion_protection" => Ok({
|
280
|
+
Middleware::IntrusionProtection(IntrusionProtection::from_value(parameters)?)
|
281
|
+
}),
|
282
|
+
"max_body" => Ok(Middleware::MaxBody(MaxBody::from_value(parameters)?)),
|
283
|
+
"rate_limit" => Ok(Middleware::RateLimit(RateLimit::from_value(parameters)?)),
|
284
|
+
"cors" => Ok(Middleware::Cors(Box::new(Cors::from_value(parameters)?))),
|
285
|
+
"request_headers" => Ok(Middleware::RequestHeaders(RequestHeaders::from_value(
|
286
|
+
parameters,
|
287
|
+
)?)),
|
288
|
+
"response_headers" => Ok(Middleware::ResponseHeaders(ResponseHeaders::from_value(
|
289
|
+
parameters,
|
290
|
+
)?)),
|
291
|
+
"static_assets" => Ok(Middleware::StaticAssets(StaticAssets::from_value(
|
292
|
+
parameters,
|
293
|
+
)?)),
|
294
|
+
"static_response" => Ok(Middleware::StaticResponse(StaticResponse::from_value(
|
295
|
+
parameters,
|
296
|
+
)?)),
|
297
|
+
"compression" => Ok(Middleware::Compression(Compression::from_value(
|
298
|
+
parameters,
|
299
|
+
)?)),
|
300
|
+
"log_requests" => Ok(Middleware::LogRequests(LogRequests::from_value(
|
301
|
+
parameters,
|
302
|
+
)?)),
|
303
|
+
"redirect" => Ok(Middleware::Redirect(Redirect::from_value(parameters)?)),
|
304
|
+
"app" => Ok(Middleware::RubyApp(RubyApp::from_value(parameters.into())?)),
|
305
|
+
"proxy" => Ok(Middleware::Proxy(Proxy::from_value(parameters)?)),
|
306
|
+
_ => Err(magnus::Error::new(
|
307
|
+
magnus::exception::standard_error(),
|
308
|
+
format!("Unknown filter type: {}", mw_type),
|
309
|
+
)),
|
260
310
|
}
|
261
|
-
|
262
|
-
"cors" => Middleware::Cors(Box::new(Cors::from_value(parameters)?)),
|
263
|
-
"request_headers" => {
|
264
|
-
Middleware::RequestHeaders(RequestHeaders::from_value(parameters)?)
|
265
|
-
}
|
266
|
-
"response_headers" => {
|
267
|
-
Middleware::ResponseHeaders(ResponseHeaders::from_value(parameters)?)
|
268
|
-
}
|
269
|
-
"static_assets" => Middleware::StaticAssets(StaticAssets::from_value(parameters)?),
|
270
|
-
"compression" => Middleware::Compression(Compression::from_value(parameters)?),
|
271
|
-
"log_requests" => Middleware::LogRequests(LogRequests::from_value(parameters)?),
|
272
|
-
"redirect" => Middleware::Redirect(Redirect::from_value(parameters)?),
|
273
|
-
"app" => Middleware::RubyApp(RubyApp::from_value(parameters.into())?),
|
274
|
-
"proxy" => Middleware::Proxy(Proxy::from_value(parameters)?),
|
275
|
-
_ => {
|
276
|
-
return Err(magnus::Error::new(
|
277
|
-
magnus::exception::exception(),
|
278
|
-
format!("Unknown filter type: {}", middleware_type),
|
279
|
-
))
|
280
|
-
}
|
281
|
-
};
|
311
|
+
})();
|
282
312
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
313
|
+
match result {
|
314
|
+
Ok(result) => Ok(result),
|
315
|
+
Err(err) => Err(magnus::Error::new(
|
316
|
+
magnus::exception::standard_error(),
|
317
|
+
format!(
|
318
|
+
"Failed to instantiate middleware of type {}, due to {}",
|
319
|
+
middleware_type, err
|
320
|
+
),
|
321
|
+
)),
|
322
|
+
}
|
288
323
|
}
|
289
324
|
|
290
325
|
pub async fn initialize_layers(&self) -> Result<()> {
|
291
|
-
for middleware in &self.default_stack {
|
292
|
-
middleware.initialize().await?;
|
293
|
-
}
|
294
326
|
for stack in self.stacks.values() {
|
295
327
|
for middleware in &stack.layers {
|
296
328
|
middleware.initialize().await?;
|
@@ -1,18 +1,12 @@
|
|
1
|
-
pub mod
|
2
|
-
pub mod bind_protocol;
|
1
|
+
pub mod binds;
|
3
2
|
pub mod byte_frame;
|
4
|
-
pub mod
|
3
|
+
pub mod http_message_types;
|
5
4
|
pub mod io_stream;
|
6
|
-
pub mod itsi_service;
|
7
5
|
pub mod lifecycle_event;
|
8
|
-
pub mod listener;
|
9
6
|
pub mod middleware_stack;
|
10
7
|
pub mod process_worker;
|
11
|
-
pub mod rate_limiter;
|
12
8
|
pub mod request_job;
|
13
9
|
pub mod serve_strategy;
|
14
10
|
pub mod signal;
|
15
|
-
pub mod
|
11
|
+
pub mod size_limited_incoming;
|
16
12
|
pub mod thread_worker;
|
17
|
-
pub mod tls;
|
18
|
-
pub mod types;
|