itsi-server 0.2.2 → 0.2.4
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 +28 -29
- data/ext/itsi_scheduler/Cargo.toml +1 -1
- data/ext/itsi_server/Cargo.toml +1 -1
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +26 -3
- data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +28 -11
- data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +1 -1
- data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +1 -2
- data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +14 -2
- data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +86 -41
- data/ext/itsi_server/src/services/itsi_http_service.rs +46 -35
- data/ext/itsi_server/src/services/static_file_server.rs +31 -3
- data/lib/itsi/http_request.rb +31 -34
- data/lib/itsi/http_response.rb +10 -8
- data/lib/itsi/passfile.rb +6 -6
- data/lib/itsi/server/config/config_helpers.rb +33 -33
- data/lib/itsi/server/config/dsl.rb +16 -21
- data/lib/itsi/server/config/known_paths.rb +11 -7
- data/lib/itsi/server/config/middleware/endpoint/endpoint.rb +0 -4
- data/lib/itsi/server/config/middleware/error_response.md +13 -0
- data/lib/itsi/server/config/middleware/location.rb +25 -21
- data/lib/itsi/server/config/middleware/proxy.rb +15 -14
- data/lib/itsi/server/config/middleware/rackup_file.rb +7 -10
- data/lib/itsi/server/config/middleware/static_assets.md +40 -0
- data/lib/itsi/server/config/middleware/static_assets.rb +8 -4
- data/lib/itsi/server/config/middleware/string_rewrite.md +14 -0
- data/lib/itsi/server/config/option.rb +0 -1
- data/lib/itsi/server/config/options/include.rb +1 -1
- data/lib/itsi/server/config/options/nodelay.md +2 -2
- data/lib/itsi/server/config/options/reuse_address.md +1 -1
- data/lib/itsi/server/config/typed_struct.rb +32 -35
- data/lib/itsi/server/config.rb +107 -92
- data/lib/itsi/server/default_app/default_app.rb +1 -1
- data/lib/itsi/server/grpc/grpc_call.rb +4 -5
- data/lib/itsi/server/grpc/grpc_interface.rb +6 -7
- data/lib/itsi/server/rack/handler/itsi.rb +0 -1
- data/lib/itsi/server/rack_interface.rb +1 -2
- data/lib/itsi/server/route_tester.rb +26 -24
- data/lib/itsi/server/typed_handlers/param_parser.rb +25 -0
- data/lib/itsi/server/typed_handlers/source_parser.rb +9 -7
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +22 -22
- data/lib/itsi/standard_headers.rb +80 -80
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09f3db1a1f234effca0f069532c406269afd5d97fd94f2bd84e0897794111c94'
|
4
|
+
data.tar.gz: 9b58b1c27aa1be97ffbcda9f58d7d10773c34ec17ed2f8266f7b4515a1916866
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4b0adee39683e6aa6d8fde02c7caeb448924090c85db779cd210ddc01a6b1ded6944604e16b17a9e18cf8833a8565c6473d3761c0015ab13af56570f0b769850
|
7
|
+
data.tar.gz: 2733bfcbb217c88fb2c49f577eb764952551c2c0f6f0be6435505eb55334276367d3ef854bb3aed054f33c192c4c7b5ee02a2ca90d0abf2249b991b1d9a531fd
|
data/Cargo.lock
CHANGED
@@ -117,9 +117,9 @@ dependencies = [
|
|
117
117
|
|
118
118
|
[[package]]
|
119
119
|
name = "anyhow"
|
120
|
-
version = "1.0.
|
120
|
+
version = "1.0.98"
|
121
121
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
122
|
-
checksum = "
|
122
|
+
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
123
123
|
|
124
124
|
[[package]]
|
125
125
|
name = "arc-swap"
|
@@ -253,9 +253,9 @@ dependencies = [
|
|
253
253
|
|
254
254
|
[[package]]
|
255
255
|
name = "aws-lc-sys"
|
256
|
-
version = "0.28.
|
256
|
+
version = "0.28.2"
|
257
257
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
258
|
-
checksum = "
|
258
|
+
checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1"
|
259
259
|
dependencies = [
|
260
260
|
"bindgen",
|
261
261
|
"cc",
|
@@ -467,9 +467,9 @@ dependencies = [
|
|
467
467
|
|
468
468
|
[[package]]
|
469
469
|
name = "brotli-decompressor"
|
470
|
-
version = "4.0.
|
470
|
+
version = "4.0.3"
|
471
471
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
472
|
-
checksum = "
|
472
|
+
checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd"
|
473
473
|
dependencies = [
|
474
474
|
"alloc-no-stdlib",
|
475
475
|
"alloc-stdlib",
|
@@ -587,9 +587,9 @@ dependencies = [
|
|
587
587
|
|
588
588
|
[[package]]
|
589
589
|
name = "clap"
|
590
|
-
version = "4.5.
|
590
|
+
version = "4.5.37"
|
591
591
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
592
|
-
checksum = "
|
592
|
+
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
593
593
|
dependencies = [
|
594
594
|
"clap_builder",
|
595
595
|
"clap_derive",
|
@@ -597,9 +597,9 @@ dependencies = [
|
|
597
597
|
|
598
598
|
[[package]]
|
599
599
|
name = "clap_builder"
|
600
|
-
version = "4.5.
|
600
|
+
version = "4.5.37"
|
601
601
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
602
|
-
checksum = "
|
602
|
+
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
603
603
|
dependencies = [
|
604
604
|
"anstream",
|
605
605
|
"anstyle",
|
@@ -764,9 +764,9 @@ dependencies = [
|
|
764
764
|
|
765
765
|
[[package]]
|
766
766
|
name = "data-encoding"
|
767
|
-
version = "2.
|
767
|
+
version = "2.9.0"
|
768
768
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
769
|
-
checksum = "
|
769
|
+
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
770
770
|
|
771
771
|
[[package]]
|
772
772
|
name = "der-parser"
|
@@ -1176,9 +1176,9 @@ dependencies = [
|
|
1176
1176
|
|
1177
1177
|
[[package]]
|
1178
1178
|
name = "h2"
|
1179
|
-
version = "0.4.
|
1179
|
+
version = "0.4.9"
|
1180
1180
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1181
|
-
checksum = "
|
1181
|
+
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
|
1182
1182
|
dependencies = [
|
1183
1183
|
"atomic-waker",
|
1184
1184
|
"bytes",
|
@@ -1363,7 +1363,7 @@ dependencies = [
|
|
1363
1363
|
"bytes",
|
1364
1364
|
"futures-channel",
|
1365
1365
|
"futures-util",
|
1366
|
-
"h2 0.4.
|
1366
|
+
"h2 0.4.9",
|
1367
1367
|
"http 1.3.1",
|
1368
1368
|
"http-body 1.0.1",
|
1369
1369
|
"httparse",
|
@@ -1644,7 +1644,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|
1644
1644
|
|
1645
1645
|
[[package]]
|
1646
1646
|
name = "itsi-server"
|
1647
|
-
version = "0.2.
|
1647
|
+
version = "0.2.4"
|
1648
1648
|
dependencies = [
|
1649
1649
|
"argon2",
|
1650
1650
|
"async-channel",
|
@@ -1681,7 +1681,7 @@ dependencies = [
|
|
1681
1681
|
"parking_lot",
|
1682
1682
|
"percent-encoding",
|
1683
1683
|
"pin-project",
|
1684
|
-
"rand 0.9.
|
1684
|
+
"rand 0.9.1",
|
1685
1685
|
"rcgen",
|
1686
1686
|
"redis",
|
1687
1687
|
"regex",
|
@@ -1713,7 +1713,7 @@ dependencies = [
|
|
1713
1713
|
"axum-server",
|
1714
1714
|
"base64 0.22.1",
|
1715
1715
|
"chrono",
|
1716
|
-
"clap 4.5.
|
1716
|
+
"clap 4.5.37",
|
1717
1717
|
"futures",
|
1718
1718
|
"log",
|
1719
1719
|
"num-bigint",
|
@@ -1841,9 +1841,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
|
1841
1841
|
|
1842
1842
|
[[package]]
|
1843
1843
|
name = "libc"
|
1844
|
-
version = "0.2.
|
1844
|
+
version = "0.2.172"
|
1845
1845
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1846
|
-
checksum = "
|
1846
|
+
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
1847
1847
|
|
1848
1848
|
[[package]]
|
1849
1849
|
name = "libloading"
|
@@ -1852,7 +1852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1852
1852
|
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
1853
1853
|
dependencies = [
|
1854
1854
|
"cfg-if",
|
1855
|
-
"windows-targets 0.
|
1855
|
+
"windows-targets 0.48.5",
|
1856
1856
|
]
|
1857
1857
|
|
1858
1858
|
[[package]]
|
@@ -2366,9 +2366,9 @@ dependencies = [
|
|
2366
2366
|
|
2367
2367
|
[[package]]
|
2368
2368
|
name = "proc-macro2"
|
2369
|
-
version = "1.0.
|
2369
|
+
version = "1.0.95"
|
2370
2370
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
2371
|
-
checksum = "
|
2371
|
+
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
2372
2372
|
dependencies = [
|
2373
2373
|
"unicode-ident",
|
2374
2374
|
]
|
@@ -2401,7 +2401,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
|
|
2401
2401
|
dependencies = [
|
2402
2402
|
"bytes",
|
2403
2403
|
"getrandom 0.3.2",
|
2404
|
-
"rand 0.9.
|
2404
|
+
"rand 0.9.1",
|
2405
2405
|
"ring",
|
2406
2406
|
"rustc-hash 2.1.1",
|
2407
2407
|
"rustls",
|
@@ -2466,13 +2466,12 @@ dependencies = [
|
|
2466
2466
|
|
2467
2467
|
[[package]]
|
2468
2468
|
name = "rand"
|
2469
|
-
version = "0.9.
|
2469
|
+
version = "0.9.1"
|
2470
2470
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
2471
|
-
checksum = "
|
2471
|
+
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
2472
2472
|
dependencies = [
|
2473
2473
|
"rand_chacha 0.9.0",
|
2474
2474
|
"rand_core 0.9.3",
|
2475
|
-
"zerocopy",
|
2476
2475
|
]
|
2477
2476
|
|
2478
2477
|
[[package]]
|
@@ -3062,9 +3061,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
3062
3061
|
|
3063
3062
|
[[package]]
|
3064
3063
|
name = "signal-hook-registry"
|
3065
|
-
version = "1.4.
|
3064
|
+
version = "1.4.5"
|
3066
3065
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
3067
|
-
checksum = "
|
3066
|
+
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
3068
3067
|
dependencies = [
|
3069
3068
|
"libc",
|
3070
3069
|
]
|
data/ext/itsi_server/Cargo.toml
CHANGED
@@ -14,7 +14,7 @@ use magnus::{
|
|
14
14
|
block::Proc,
|
15
15
|
error::Result,
|
16
16
|
value::{LazyId, ReprValue},
|
17
|
-
RArray, RHash, Ruby, Symbol, Value,
|
17
|
+
RArray, RHash, Ruby, Symbol, TryConvert, Value,
|
18
18
|
};
|
19
19
|
use nix::{
|
20
20
|
fcntl::{fcntl, FcntlArg, FdFlag},
|
@@ -124,9 +124,17 @@ impl ServerParams {
|
|
124
124
|
debug!("Loading Itsi Scheduler");
|
125
125
|
ruby.require("itsi/scheduler")?;
|
126
126
|
}
|
127
|
-
let
|
127
|
+
let result_pair = self
|
128
128
|
.middleware_loader
|
129
|
-
.call::<
|
129
|
+
.call::<(), RArray>(())
|
130
|
+
.inspect_err(|e| {
|
131
|
+
eprintln!("Error loading middleware: {:?}", e);
|
132
|
+
if let Some(err_value) = e.value() {
|
133
|
+
print_rb_backtrace(err_value);
|
134
|
+
}
|
135
|
+
})?;
|
136
|
+
let routes_raw = result_pair
|
137
|
+
.entry::<Option<Value>>(0)
|
130
138
|
.inspect_err(|e| {
|
131
139
|
eprintln!("Error loading middleware: {:?}", e);
|
132
140
|
if let Some(err_value) = e.value() {
|
@@ -134,6 +142,21 @@ impl ServerParams {
|
|
134
142
|
}
|
135
143
|
})?
|
136
144
|
.map(|mw| mw.into());
|
145
|
+
let error_lines = result_pair.entry::<Option<RArray>>(1).inspect_err(|e| {
|
146
|
+
eprintln!("Error loading middleware: {:?}", e);
|
147
|
+
if let Some(err_value) = e.value() {
|
148
|
+
print_rb_backtrace(err_value);
|
149
|
+
}
|
150
|
+
})?;
|
151
|
+
if error_lines.is_some_and(|r| !r.is_empty()) {
|
152
|
+
let errors: Vec<String> =
|
153
|
+
Vec::<String>::try_convert(error_lines.unwrap().as_value())?;
|
154
|
+
ItsiServerConfig::print_config_errors(errors);
|
155
|
+
return Err(magnus::Error::new(
|
156
|
+
magnus::exception::runtime_error(),
|
157
|
+
"Failed to set middleware",
|
158
|
+
));
|
159
|
+
}
|
137
160
|
let middleware = MiddlewareSet::new(routes_raw)?;
|
138
161
|
self.middleware.set(middleware).map_err(|_| {
|
139
162
|
magnus::Error::new(
|
@@ -1,5 +1,6 @@
|
|
1
1
|
use crate::{
|
2
|
-
server::http_message_types::
|
2
|
+
server::http_message_types::{HttpRequest, HttpResponse},
|
3
|
+
services::itsi_http_service::HttpRequestContext,
|
3
4
|
};
|
4
5
|
|
5
6
|
use super::{
|
@@ -13,6 +14,7 @@ use async_compression::{
|
|
13
14
|
};
|
14
15
|
use async_trait::async_trait;
|
15
16
|
use bytes::{Bytes, BytesMut};
|
17
|
+
use either::Either;
|
16
18
|
use futures::TryStreamExt;
|
17
19
|
use http::{
|
18
20
|
header::{GetAll, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TYPE},
|
@@ -20,6 +22,7 @@ use http::{
|
|
20
22
|
};
|
21
23
|
use http_body_util::{combinators::BoxBody, BodyExt, Full, StreamBody};
|
22
24
|
use hyper::body::{Body, Frame};
|
25
|
+
use magnus::error::Result;
|
23
26
|
use serde::{Deserialize, Serialize};
|
24
27
|
use std::convert::Infallible;
|
25
28
|
use tokio::io::{AsyncRead, AsyncReadExt, BufReader};
|
@@ -151,6 +154,15 @@ fn update_content_encoding(parts: &mut http::response::Parts, new_encoding: Head
|
|
151
154
|
|
152
155
|
#[async_trait]
|
153
156
|
impl MiddlewareLayer for Compression {
|
157
|
+
async fn before(
|
158
|
+
&self,
|
159
|
+
req: HttpRequest,
|
160
|
+
context: &mut HttpRequestContext,
|
161
|
+
) -> Result<Either<HttpRequest, HttpResponse>> {
|
162
|
+
context.set_supported_encoding_set(&req);
|
163
|
+
Ok(Either::Left(req))
|
164
|
+
}
|
165
|
+
|
154
166
|
/// We'll apply compression on the response, where appropriate.
|
155
167
|
/// This is if:
|
156
168
|
/// * The response body is larger than the minimum size.
|
@@ -207,16 +219,21 @@ impl MiddlewareLayer for Compression {
|
|
207
219
|
}
|
208
220
|
}
|
209
221
|
|
210
|
-
let compression_method =
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
222
|
+
let compression_method =
|
223
|
+
if let Some(supported_encoding_set) = context.supported_encoding_set() {
|
224
|
+
match find_first_supported(
|
225
|
+
supported_encoding_set,
|
226
|
+
self.algorithms.iter().map(|algo| algo.as_str()),
|
227
|
+
) {
|
228
|
+
Some("gzip") => CompressionAlgorithm::Gzip,
|
229
|
+
Some("br") => CompressionAlgorithm::Brotli,
|
230
|
+
Some("deflate") => CompressionAlgorithm::Deflate,
|
231
|
+
Some("zstd") => CompressionAlgorithm::Zstd,
|
232
|
+
_ => CompressionAlgorithm::Identity,
|
233
|
+
}
|
234
|
+
} else {
|
235
|
+
CompressionAlgorithm::Identity
|
236
|
+
};
|
220
237
|
|
221
238
|
debug!(
|
222
239
|
target: "middleware::compress",
|
@@ -62,7 +62,7 @@ impl MiddlewareLayer for LogRequests {
|
|
62
62
|
req: HttpRequest,
|
63
63
|
context: &mut HttpRequestContext,
|
64
64
|
) -> Result<Either<HttpRequest, HttpResponse>> {
|
65
|
-
context.
|
65
|
+
context.init_logging_params();
|
66
66
|
if let Some(LogConfig { level, format }) = self.before.as_ref() {
|
67
67
|
level.log(format.rewrite_request(&req, context));
|
68
68
|
}
|
@@ -33,7 +33,7 @@ use reqwest::{
|
|
33
33
|
Body, Client, Url,
|
34
34
|
};
|
35
35
|
use serde::Deserialize;
|
36
|
-
use tracing::
|
36
|
+
use tracing::debug;
|
37
37
|
|
38
38
|
#[derive(Debug, Clone, Deserialize)]
|
39
39
|
pub struct Proxy {
|
@@ -324,7 +324,6 @@ impl MiddlewareLayer for Proxy {
|
|
324
324
|
.unwrap_or("")
|
325
325
|
});
|
326
326
|
|
327
|
-
info!("Extracted host str is {}", host_str);
|
328
327
|
let req_info = RequestInfo {
|
329
328
|
method: req.method().clone(),
|
330
329
|
headers: req_headers.clone(),
|
@@ -12,10 +12,11 @@ use async_trait::async_trait;
|
|
12
12
|
use either::Either;
|
13
13
|
use http::{
|
14
14
|
header::{IF_MODIFIED_SINCE, RANGE},
|
15
|
-
HeaderMap, Method,
|
15
|
+
HeaderMap, HeaderValue, Method,
|
16
16
|
};
|
17
17
|
use itsi_error::ItsiError;
|
18
18
|
use magnus::error::Result;
|
19
|
+
use moka::sync::Cache;
|
19
20
|
use regex::Regex;
|
20
21
|
use serde::Deserialize;
|
21
22
|
use std::{collections::HashMap, path::PathBuf, sync::OnceLock, time::Duration};
|
@@ -75,6 +76,10 @@ impl MiddlewareLayer for StaticAssets {
|
|
75
76
|
recheck_interval: Duration::from_secs(self.file_check_interval),
|
76
77
|
serve_hidden_files: self.serve_hidden_files,
|
77
78
|
allowed_extensions: self.allowed_extensions.clone(),
|
79
|
+
miss_cache: Cache::builder()
|
80
|
+
.max_capacity(self.max_files_in_memory)
|
81
|
+
.time_to_live(Duration::from_secs(self.file_check_interval))
|
82
|
+
.build(),
|
78
83
|
})?)
|
79
84
|
.map_err(ItsiError::new)?;
|
80
85
|
Ok(())
|
@@ -90,6 +95,8 @@ impl MiddlewareLayer for StaticAssets {
|
|
90
95
|
debug!(target: "middleware::static_assets", "Refusing to handle non-GET/HEAD request");
|
91
96
|
return Ok(Either::Left(req));
|
92
97
|
}
|
98
|
+
|
99
|
+
context.set_supported_encoding_set(&req);
|
93
100
|
let abs_path = req.uri().path();
|
94
101
|
let rel_path = if !self.relative_path {
|
95
102
|
abs_path.trim_start_matches("/")
|
@@ -123,6 +130,10 @@ impl MiddlewareLayer for StaticAssets {
|
|
123
130
|
|
124
131
|
// Let the file server handle everything
|
125
132
|
let file_server = self.file_server.get().unwrap();
|
133
|
+
let encodings: &[HeaderValue] = context
|
134
|
+
.supported_encoding_set()
|
135
|
+
.map(Vec::as_slice)
|
136
|
+
.unwrap_or(&[] as &[HeaderValue]);
|
126
137
|
let response = file_server
|
127
138
|
.serve(
|
128
139
|
&req,
|
@@ -131,9 +142,10 @@ impl MiddlewareLayer for StaticAssets {
|
|
131
142
|
serve_range,
|
132
143
|
if_modified_since,
|
133
144
|
is_head_request,
|
134
|
-
|
145
|
+
encodings,
|
135
146
|
)
|
136
147
|
.await;
|
148
|
+
|
137
149
|
if response.is_none() {
|
138
150
|
Ok(Either::Left(req))
|
139
151
|
} else {
|
@@ -50,6 +50,35 @@ pub fn parse_template(template: &str) -> Vec<Segment> {
|
|
50
50
|
}
|
51
51
|
|
52
52
|
impl StringRewrite {
|
53
|
+
/// Apply a single modifier of the form `op:arg` (or for replace `op:from,to`)
|
54
|
+
#[inline]
|
55
|
+
fn apply_modifier(s: &mut String, mod_str: &str) {
|
56
|
+
if let Some((op, arg)) = mod_str.split_once(':') {
|
57
|
+
match op {
|
58
|
+
"strip_prefix" => {
|
59
|
+
if s.starts_with(arg) {
|
60
|
+
let _ = s.drain(..arg.len());
|
61
|
+
}
|
62
|
+
}
|
63
|
+
"strip_suffix" => {
|
64
|
+
if s.ends_with(arg) {
|
65
|
+
let len = s.len();
|
66
|
+
let start = len.saturating_sub(arg.len());
|
67
|
+
let _ = s.drain(start..);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
"replace" => {
|
71
|
+
if let Some((from, to)) = arg.split_once(',') {
|
72
|
+
if s.contains(from) {
|
73
|
+
*s = s.replace(from, to);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
_ => {}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
53
82
|
pub fn rewrite_request(&self, req: &HttpRequest, context: &HttpRequestContext) -> String {
|
54
83
|
let segments = self
|
55
84
|
.segments
|
@@ -63,9 +92,17 @@ impl StringRewrite {
|
|
63
92
|
|
64
93
|
for segment in segments {
|
65
94
|
match segment {
|
66
|
-
Segment::Literal(text) =>
|
67
|
-
|
68
|
-
|
95
|
+
Segment::Literal(text) => {
|
96
|
+
result.push_str(text);
|
97
|
+
}
|
98
|
+
Segment::Placeholder(raw) => {
|
99
|
+
// split into key and optional modifier
|
100
|
+
let mut parts = raw.split('|');
|
101
|
+
let key = parts.next().unwrap();
|
102
|
+
let modifiers = parts; // zero o
|
103
|
+
|
104
|
+
// 1) lookup the raw replacement
|
105
|
+
let mut replacement = match key {
|
69
106
|
"request_id" => context.short_request_id(),
|
70
107
|
"request_id_full" => context.request_id(),
|
71
108
|
"method" => req.method().as_str().to_string(),
|
@@ -76,13 +113,13 @@ impl StringRewrite {
|
|
76
113
|
.uri()
|
77
114
|
.path_and_query()
|
78
115
|
.map(|pq| pq.to_string())
|
79
|
-
.
|
116
|
+
.unwrap_or_default(),
|
80
117
|
"query" => {
|
81
|
-
let
|
82
|
-
if
|
83
|
-
|
118
|
+
let q = req.uri().query().unwrap_or("");
|
119
|
+
if q.is_empty() {
|
120
|
+
"".to_string()
|
84
121
|
} else {
|
85
|
-
format!("?{}",
|
122
|
+
format!("?{}", q)
|
86
123
|
}
|
87
124
|
}
|
88
125
|
"port" => req
|
@@ -91,31 +128,34 @@ impl StringRewrite {
|
|
91
128
|
.map(|p| p.to_string())
|
92
129
|
.unwrap_or_else(|| "80".to_string()),
|
93
130
|
"start_time" => {
|
94
|
-
if let Some(
|
95
|
-
|
131
|
+
if let Some(ts) = context.start_time() {
|
132
|
+
ts.format("%Y-%m-%d:%H:%M:%S:%3f").to_string()
|
96
133
|
} else {
|
97
134
|
"N/A".to_string()
|
98
135
|
}
|
99
136
|
}
|
100
137
|
other => {
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
}
|
114
|
-
} else {
|
138
|
+
// headers first
|
139
|
+
if let Some(hv) = req.headers().get(other) {
|
140
|
+
hv.to_str().unwrap_or("").to_string()
|
141
|
+
}
|
142
|
+
// then any regex‐capture
|
143
|
+
else if let Some(caps) = &captures {
|
144
|
+
caps.name(other)
|
145
|
+
.map(|m| m.as_str().to_string())
|
146
|
+
.unwrap_or_else(|| format!("{{{}}}", other))
|
147
|
+
}
|
148
|
+
// fallback: leave placeholder intact
|
149
|
+
else {
|
115
150
|
format!("{{{}}}", other)
|
116
151
|
}
|
117
152
|
}
|
118
153
|
};
|
154
|
+
|
155
|
+
for m in modifiers {
|
156
|
+
Self::apply_modifier(&mut replacement, m);
|
157
|
+
}
|
158
|
+
|
119
159
|
result.push_str(&replacement);
|
120
160
|
}
|
121
161
|
}
|
@@ -132,37 +172,42 @@ impl StringRewrite {
|
|
132
172
|
let mut result = String::with_capacity(self.template_string.len());
|
133
173
|
for segment in segments {
|
134
174
|
match segment {
|
135
|
-
Segment::Literal(text) =>
|
136
|
-
|
137
|
-
|
175
|
+
Segment::Literal(text) => {
|
176
|
+
result.push_str(text);
|
177
|
+
}
|
178
|
+
Segment::Placeholder(raw) => {
|
179
|
+
let mut parts = raw.split('|');
|
180
|
+
let key = parts.next().unwrap();
|
181
|
+
let modifiers = parts; // zero o
|
182
|
+
|
183
|
+
let mut replacement = match key {
|
138
184
|
"request_id" => context.short_request_id(),
|
139
185
|
"request_id_full" => context.request_id(),
|
140
186
|
"status" => resp.status().as_str().to_string(),
|
141
187
|
"addr" => context.addr.to_owned(),
|
142
188
|
"response_time" => {
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
}
|
147
|
-
format!("{}ms", response_time.num_milliseconds())
|
148
|
-
}
|
189
|
+
let dur = context.get_response_time();
|
190
|
+
let micros = dur.as_micros();
|
191
|
+
if micros < 1_000 {
|
192
|
+
format!("{}µs", micros)
|
149
193
|
} else {
|
150
|
-
|
194
|
+
let ms = dur.as_secs_f64() * 1_000.0;
|
195
|
+
format!("{:.3}ms", ms)
|
151
196
|
}
|
152
197
|
}
|
153
198
|
other => {
|
154
|
-
|
155
|
-
|
156
|
-
if let Ok(s) = val.to_str() {
|
157
|
-
s.to_string()
|
158
|
-
} else {
|
159
|
-
"".to_string()
|
160
|
-
}
|
199
|
+
if let Some(hv) = resp.headers().get(other) {
|
200
|
+
hv.to_str().unwrap_or("").to_string()
|
161
201
|
} else {
|
162
202
|
format!("{{{}}}", other)
|
163
203
|
}
|
164
204
|
}
|
165
205
|
};
|
206
|
+
|
207
|
+
for m in modifiers {
|
208
|
+
Self::apply_modifier(&mut replacement, m);
|
209
|
+
}
|
210
|
+
|
166
211
|
result.push_str(&replacement);
|
167
212
|
}
|
168
213
|
}
|