spikard 0.8.3 → 0.10.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.
- checksums.yaml +4 -4
- data/README.md +19 -10
- data/ext/spikard_rb/Cargo.lock +234 -162
- data/ext/spikard_rb/Cargo.toml +2 -2
- data/ext/spikard_rb/extconf.rb +4 -3
- data/lib/spikard/config.rb +88 -12
- data/lib/spikard/testing.rb +3 -1
- data/lib/spikard/version.rb +1 -1
- data/lib/spikard.rb +11 -0
- data/vendor/crates/spikard-bindings-shared/Cargo.toml +3 -6
- data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +8 -8
- data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +2 -2
- data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +4 -4
- data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +10 -4
- data/vendor/crates/spikard-bindings-shared/src/error_response.rs +3 -3
- data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +10 -5
- data/vendor/crates/spikard-bindings-shared/src/json_conversion.rs +829 -0
- data/vendor/crates/spikard-bindings-shared/src/lazy_cache.rs +587 -0
- data/vendor/crates/spikard-bindings-shared/src/lib.rs +7 -0
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +11 -11
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +9 -37
- data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +436 -3
- data/vendor/crates/spikard-bindings-shared/src/response_interpreter.rs +944 -0
- data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +4 -4
- data/vendor/crates/spikard-bindings-shared/tests/config_extractor_behavior.rs +3 -2
- data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +13 -13
- data/vendor/crates/spikard-bindings-shared/tests/{comprehensive_coverage.rs → full_coverage.rs} +10 -5
- data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +14 -14
- data/vendor/crates/spikard-bindings-shared/tests/integration_tests.rs +669 -0
- data/vendor/crates/spikard-core/Cargo.toml +3 -3
- data/vendor/crates/spikard-core/src/di/container.rs +1 -1
- data/vendor/crates/spikard-core/src/di/factory.rs +2 -2
- data/vendor/crates/spikard-core/src/di/resolved.rs +2 -2
- data/vendor/crates/spikard-core/src/di/value.rs +1 -1
- data/vendor/crates/spikard-core/src/http.rs +75 -0
- data/vendor/crates/spikard-core/src/lifecycle.rs +43 -43
- data/vendor/crates/spikard-core/src/parameters.rs +14 -19
- data/vendor/crates/spikard-core/src/problem.rs +1 -1
- data/vendor/crates/spikard-core/src/request_data.rs +7 -16
- data/vendor/crates/spikard-core/src/router.rs +6 -0
- data/vendor/crates/spikard-core/src/schema_registry.rs +2 -3
- data/vendor/crates/spikard-core/src/type_hints.rs +3 -2
- data/vendor/crates/spikard-core/src/validation/error_mapper.rs +1 -1
- data/vendor/crates/spikard-core/src/validation/mod.rs +1 -1
- data/vendor/crates/spikard-core/tests/di_dependency_defaults.rs +1 -1
- data/vendor/crates/spikard-core/tests/error_mapper.rs +2 -2
- data/vendor/crates/spikard-core/tests/parameters_edge_cases.rs +1 -1
- data/vendor/crates/spikard-core/tests/parameters_full.rs +1 -1
- data/vendor/crates/spikard-core/tests/parameters_schema_and_formats.rs +1 -1
- data/vendor/crates/spikard-core/tests/validation_coverage.rs +4 -4
- data/vendor/crates/spikard-http/Cargo.toml +4 -2
- data/vendor/crates/spikard-http/src/cors.rs +32 -11
- data/vendor/crates/spikard-http/src/di_handler.rs +12 -8
- data/vendor/crates/spikard-http/src/grpc/framing.rs +469 -0
- data/vendor/crates/spikard-http/src/grpc/handler.rs +887 -25
- data/vendor/crates/spikard-http/src/grpc/mod.rs +114 -22
- data/vendor/crates/spikard-http/src/grpc/service.rs +232 -2
- data/vendor/crates/spikard-http/src/grpc/streaming.rs +80 -2
- data/vendor/crates/spikard-http/src/handler_trait.rs +204 -27
- data/vendor/crates/spikard-http/src/handler_trait_tests.rs +15 -15
- data/vendor/crates/spikard-http/src/jsonrpc/http_handler.rs +2 -2
- data/vendor/crates/spikard-http/src/jsonrpc/router.rs +2 -2
- data/vendor/crates/spikard-http/src/lib.rs +1 -1
- data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +2 -2
- data/vendor/crates/spikard-http/src/lifecycle.rs +4 -4
- data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +2 -0
- data/vendor/crates/spikard-http/src/server/fast_router.rs +186 -0
- data/vendor/crates/spikard-http/src/server/grpc_routing.rs +324 -23
- data/vendor/crates/spikard-http/src/server/handler.rs +33 -22
- data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +21 -2
- data/vendor/crates/spikard-http/src/server/mod.rs +125 -20
- data/vendor/crates/spikard-http/src/server/request_extraction.rs +126 -44
- data/vendor/crates/spikard-http/src/server/routing_factory.rs +80 -69
- data/vendor/crates/spikard-http/tests/common/handlers.rs +2 -2
- data/vendor/crates/spikard-http/tests/common/test_builders.rs +12 -12
- data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +2 -2
- data/vendor/crates/spikard-http/tests/di_integration.rs +6 -6
- data/vendor/crates/spikard-http/tests/grpc_bidirectional_streaming.rs +430 -0
- data/vendor/crates/spikard-http/tests/grpc_client_streaming.rs +738 -0
- data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +13 -9
- data/vendor/crates/spikard-http/tests/grpc_server_streaming.rs +974 -0
- data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +2 -2
- data/vendor/crates/spikard-http/tests/request_extraction_full.rs +4 -4
- data/vendor/crates/spikard-http/tests/server_config_builder.rs +2 -2
- data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +1 -0
- data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +140 -0
- data/vendor/crates/spikard-rb/Cargo.toml +3 -1
- data/vendor/crates/spikard-rb/src/conversion.rs +138 -4
- data/vendor/crates/spikard-rb/src/grpc/handler.rs +706 -229
- data/vendor/crates/spikard-rb/src/grpc/mod.rs +6 -2
- data/vendor/crates/spikard-rb/src/gvl.rs +2 -2
- data/vendor/crates/spikard-rb/src/handler.rs +169 -91
- data/vendor/crates/spikard-rb/src/lib.rs +444 -62
- data/vendor/crates/spikard-rb/src/lifecycle.rs +29 -1
- data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +108 -43
- data/vendor/crates/spikard-rb/src/request.rs +117 -20
- data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +52 -25
- data/vendor/crates/spikard-rb/src/server.rs +23 -14
- data/vendor/crates/spikard-rb/src/testing/client.rs +5 -4
- data/vendor/crates/spikard-rb/src/testing/sse.rs +1 -36
- data/vendor/crates/spikard-rb/src/testing/websocket.rs +3 -38
- data/vendor/crates/spikard-rb/src/websocket.rs +32 -23
- data/vendor/crates/spikard-rb-macros/Cargo.toml +1 -1
- metadata +14 -4
- data/vendor/bundle/ruby/3.4.0/gems/diff-lcs-1.6.2/mise.toml +0 -5
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.10.0/build/buildkitd.toml +0 -2
|
@@ -179,16 +179,15 @@ mod tests {
|
|
|
179
179
|
}
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
-
let
|
|
182
|
+
let validators: Vec<_> = (0..10)
|
|
183
183
|
.map(|_| {
|
|
184
184
|
let registry = StdArc::clone(®istry);
|
|
185
185
|
let schema = schema.clone();
|
|
186
186
|
thread::spawn(move || registry.get_or_compile(&schema).unwrap())
|
|
187
187
|
})
|
|
188
|
+
.map(|h| h.join().unwrap())
|
|
188
189
|
.collect();
|
|
189
190
|
|
|
190
|
-
let validators: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
|
|
191
|
-
|
|
192
191
|
for i in 1..validators.len() {
|
|
193
192
|
assert!(Arc::ptr_eq(&validators[0], &validators[i]));
|
|
194
193
|
}
|
|
@@ -206,7 +206,7 @@ pub fn auto_generate_parameter_schema(route_path: &str) -> Option<Value> {
|
|
|
206
206
|
/// "required": ["count"]
|
|
207
207
|
/// });
|
|
208
208
|
///
|
|
209
|
-
/// let merged = merge_parameter_schemas(auto_schema, explicit_schema);
|
|
209
|
+
/// let merged = merge_parameter_schemas(&auto_schema, &explicit_schema);
|
|
210
210
|
/// // Result: auto-generated id + explicit count with constraints
|
|
211
211
|
/// ```
|
|
212
212
|
#[must_use]
|
|
@@ -235,6 +235,7 @@ pub fn merge_parameter_schemas(auto_schema: &Value, explicit_schema: &Value) ->
|
|
|
235
235
|
result
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
#[allow(clippy::literal_string_with_formatting_args)]
|
|
238
239
|
#[cfg(test)]
|
|
239
240
|
mod tests {
|
|
240
241
|
use super::*;
|
|
@@ -302,7 +303,7 @@ mod tests {
|
|
|
302
303
|
"required": ["count"]
|
|
303
304
|
});
|
|
304
305
|
|
|
305
|
-
let merged = merge_parameter_schemas(auto_schema, explicit_schema);
|
|
306
|
+
let merged = merge_parameter_schemas(&auto_schema, &explicit_schema);
|
|
306
307
|
assert!(merged["properties"]["id"].is_object());
|
|
307
308
|
assert!(merged["properties"]["count"].is_object());
|
|
308
309
|
assert_eq!(merged["properties"]["count"]["minimum"], 1);
|
|
@@ -675,7 +675,7 @@ mod tests {
|
|
|
675
675
|
let condition = ErrorCondition::EmailFormat;
|
|
676
676
|
let (error_type, msg, ctx) = ErrorMapper::map_error(&condition, &schema, "", "");
|
|
677
677
|
assert_eq!(error_type, "string_pattern_mismatch");
|
|
678
|
-
assert!(msg.contains(
|
|
678
|
+
assert!(msg.contains('@'));
|
|
679
679
|
assert!(ctx.is_some());
|
|
680
680
|
}
|
|
681
681
|
|
|
@@ -460,7 +460,7 @@ mod tests {
|
|
|
460
460
|
});
|
|
461
461
|
|
|
462
462
|
let result = validator.validate(&data);
|
|
463
|
-
eprintln!("Validation result: {:?}"
|
|
463
|
+
eprintln!("Validation result: {result:?}");
|
|
464
464
|
|
|
465
465
|
assert!(result.is_err(), "Should have validation errors");
|
|
466
466
|
let err = result.unwrap_err();
|
|
@@ -466,7 +466,7 @@ fn test_error_context_includes_constraints() {
|
|
|
466
466
|
let err = result.unwrap_err();
|
|
467
467
|
assert!(err.errors[0].ctx.is_some());
|
|
468
468
|
let ctx = err.errors[0].ctx.as_ref().unwrap();
|
|
469
|
-
assert_eq!(ctx.get("min_length").and_then(
|
|
469
|
+
assert_eq!(ctx.get("min_length").and_then(serde_json::Value::as_u64), Some(5));
|
|
470
470
|
}
|
|
471
471
|
|
|
472
472
|
#[test]
|
|
@@ -631,7 +631,7 @@ fn test_error_messages_are_user_friendly() {
|
|
|
631
631
|
let msg = &err.errors[0].msg;
|
|
632
632
|
assert!(msg.contains("18") || msg.contains("minimum"));
|
|
633
633
|
assert!(!msg.contains("exclusiveMinimum"));
|
|
634
|
-
assert!(!msg.contains(
|
|
634
|
+
assert!(!msg.contains('$'));
|
|
635
635
|
}
|
|
636
636
|
|
|
637
637
|
#[test]
|
|
@@ -39,7 +39,7 @@ fn test_query_boolean_empty_string_coerces_to_false() {
|
|
|
39
39
|
let validator = ParameterValidator::new(schema).expect("validator");
|
|
40
40
|
|
|
41
41
|
let mut raw_query_params = HashMap::new();
|
|
42
|
-
raw_query_params.insert("flag".to_string(), vec![
|
|
42
|
+
raw_query_params.insert("flag".to_string(), vec![String::new()]);
|
|
43
43
|
|
|
44
44
|
let extracted = validator.validate_and_extract(
|
|
45
45
|
&json!({}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//! Comprehensive parameter validation tests
|
|
2
2
|
//!
|
|
3
3
|
//! These tests cover header, cookie, and query parameter validation scenarios
|
|
4
|
-
//! using the ParameterValidator from spikard-core.
|
|
4
|
+
//! using the `ParameterValidator` from spikard-core.
|
|
5
5
|
|
|
6
6
|
use serde_json::json;
|
|
7
7
|
use spikard_core::parameters::ParameterValidator;
|
|
@@ -203,7 +203,7 @@ fn boolean_empty_string_is_coerced_to_false() {
|
|
|
203
203
|
|
|
204
204
|
let validator = ParameterValidator::new(schema).expect("validator");
|
|
205
205
|
let mut raw_query = HashMap::new();
|
|
206
|
-
raw_query.insert("flag".to_string(), vec![
|
|
206
|
+
raw_query.insert("flag".to_string(), vec![String::new()]);
|
|
207
207
|
|
|
208
208
|
let extracted = validator
|
|
209
209
|
.validate_and_extract(
|
|
@@ -32,10 +32,10 @@ fn validator_preprocesses_binary_file_objects_recursively() {
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
let data = json!({
|
|
35
|
-
"file": file_object
|
|
36
|
-
"files": [file_object
|
|
35
|
+
"file": &file_object,
|
|
36
|
+
"files": [&file_object],
|
|
37
37
|
"nested": {
|
|
38
|
-
"inner": file_object,
|
|
38
|
+
"inner": &file_object,
|
|
39
39
|
"other": 1
|
|
40
40
|
}
|
|
41
41
|
});
|
|
@@ -242,7 +242,7 @@ fn error_mapper_uses_schema_constraints_when_present() {
|
|
|
242
242
|
|
|
243
243
|
let (ty, _msg, ctx) = ErrorMapper::map_error(&ErrorCondition::EmailFormat, &schema, "/properties/value", "generic");
|
|
244
244
|
assert_eq!(ty, "string_pattern_mismatch");
|
|
245
|
-
assert!(ctx.as_ref().unwrap()["pattern"].as_str().unwrap().contains(
|
|
245
|
+
assert!(ctx.as_ref().unwrap()["pattern"].as_str().unwrap().contains('@'));
|
|
246
246
|
|
|
247
247
|
let (ty, _msg, ctx) = ErrorMapper::map_error(&ErrorCondition::UuidFormat, &schema, "/properties/value", "generic");
|
|
248
248
|
assert_eq!(ty, "uuid_parsing");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "spikard-http"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.10.2"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
authors = ["Na'aman Hirschfeld <nhirschfeld@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -16,6 +16,7 @@ readme = "README.md"
|
|
|
16
16
|
axum = { version = "0.8", features = ["multipart", "ws"] }
|
|
17
17
|
tokio = { version = "1", features = ["full"] }
|
|
18
18
|
tokio-util = "0.7"
|
|
19
|
+
tokio-stream = "0.1"
|
|
19
20
|
tower = "0.5"
|
|
20
21
|
tower-http = { version = "0.6.8", features = ["fs", "trace", "compression-gzip", "compression-br", "compression-deflate", "cors", "request-id", "limit", "timeout", "set-header", "sensitive-headers"] }
|
|
21
22
|
tower_governor = "0.8"
|
|
@@ -40,7 +41,8 @@ urlencoding = "2.1"
|
|
|
40
41
|
url = "2.5"
|
|
41
42
|
mime = "0.3"
|
|
42
43
|
jiff = "0.2"
|
|
43
|
-
uuid = "1.
|
|
44
|
+
uuid = "1.20"
|
|
45
|
+
ahash = "0.8"
|
|
44
46
|
bytes = "1.11"
|
|
45
47
|
http-body-util = "0.1"
|
|
46
48
|
http-body = "1.0"
|
|
@@ -56,15 +56,16 @@ fn is_method_allowed(method: &str, allowed_methods: &[String]) -> bool {
|
|
|
56
56
|
///
|
|
57
57
|
/// # Returns
|
|
58
58
|
/// `true` if all requested headers are allowed, `false` if any header is not allowed
|
|
59
|
-
fn are_headers_allowed(requested:
|
|
59
|
+
fn are_headers_allowed(requested: impl IntoIterator<Item = impl AsRef<str>>, allowed: &[String]) -> bool {
|
|
60
60
|
if allowed.iter().any(|h| h == "*") {
|
|
61
61
|
return true;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
requested.
|
|
64
|
+
requested.into_iter().all(|req_header| {
|
|
65
|
+
let req_str = req_header.as_ref();
|
|
65
66
|
allowed
|
|
66
67
|
.iter()
|
|
67
|
-
.any(|allowed_header| allowed_header.eq_ignore_ascii_case(
|
|
68
|
+
.any(|allowed_header| allowed_header.eq_ignore_ascii_case(req_str))
|
|
68
69
|
})
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -133,9 +134,8 @@ pub fn handle_preflight(headers: &HeaderMap, cors_config: &CorsConfig) -> Result
|
|
|
133
134
|
.and_then(|v| v.to_str().ok());
|
|
134
135
|
|
|
135
136
|
if let Some(req_headers) = requested_headers_str {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if !are_headers_allowed(&requested_headers, &cors_config.allowed_headers) {
|
|
137
|
+
// Pass iterator directly without collecting into Vec
|
|
138
|
+
if !are_headers_allowed(req_headers.split(',').map(|h| h.trim()), &cors_config.allowed_headers) {
|
|
139
139
|
return Err(Box::new((StatusCode::FORBIDDEN).into_response()));
|
|
140
140
|
}
|
|
141
141
|
}
|
|
@@ -162,16 +162,16 @@ pub fn handle_preflight(headers: &HeaderMap, cors_config: &CorsConfig) -> Result
|
|
|
162
162
|
HeaderValue::from_str(origin).unwrap_or_else(|_| HeaderValue::from_static("*")),
|
|
163
163
|
);
|
|
164
164
|
|
|
165
|
-
let methods = cors_config.
|
|
165
|
+
let methods = cors_config.allowed_methods_joined();
|
|
166
166
|
headers_mut.insert(
|
|
167
167
|
"access-control-allow-methods",
|
|
168
|
-
HeaderValue::from_str(
|
|
168
|
+
HeaderValue::from_str(methods).unwrap_or_else(|_| HeaderValue::from_static("*")),
|
|
169
169
|
);
|
|
170
170
|
|
|
171
|
-
let allowed_headers = cors_config.
|
|
171
|
+
let allowed_headers = cors_config.allowed_headers_joined();
|
|
172
172
|
headers_mut.insert(
|
|
173
173
|
"access-control-allow-headers",
|
|
174
|
-
HeaderValue::from_str(
|
|
174
|
+
HeaderValue::from_str(allowed_headers).unwrap_or_else(|_| HeaderValue::from_static("*")),
|
|
175
175
|
);
|
|
176
176
|
|
|
177
177
|
if let Some(max_age) = cors_config.max_age
|
|
@@ -291,6 +291,7 @@ mod tests {
|
|
|
291
291
|
expose_headers: Some(vec!["x-custom-header".to_string()]),
|
|
292
292
|
max_age: Some(3600),
|
|
293
293
|
allow_credentials: Some(true),
|
|
294
|
+
..Default::default()
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
|
|
@@ -500,7 +501,8 @@ mod tests {
|
|
|
500
501
|
allowed_headers: vec![],
|
|
501
502
|
expose_headers: None,
|
|
502
503
|
max_age: None,
|
|
503
|
-
allow_credentials: Some(true), // SECURITY BUG: This should not be allowed with wildcard
|
|
504
|
+
allow_credentials: Some(true), // SECURITY BUG: This should not be allowed with wildcard,
|
|
505
|
+
..Default::default()
|
|
504
506
|
};
|
|
505
507
|
|
|
506
508
|
let mut headers = HeaderMap::new();
|
|
@@ -538,6 +540,7 @@ mod tests {
|
|
|
538
540
|
expose_headers: None,
|
|
539
541
|
max_age: None,
|
|
540
542
|
allow_credentials: None,
|
|
543
|
+
..Default::default()
|
|
541
544
|
};
|
|
542
545
|
|
|
543
546
|
assert!(!is_origin_allowed("https://api.example.com", &config.allowed_origins));
|
|
@@ -561,6 +564,7 @@ mod tests {
|
|
|
561
564
|
expose_headers: None,
|
|
562
565
|
max_age: None,
|
|
563
566
|
allow_credentials: None,
|
|
567
|
+
..Default::default()
|
|
564
568
|
};
|
|
565
569
|
|
|
566
570
|
assert!(!is_origin_allowed("http://localhost:3001", &config.allowed_origins));
|
|
@@ -581,6 +585,7 @@ mod tests {
|
|
|
581
585
|
expose_headers: None,
|
|
582
586
|
max_age: None,
|
|
583
587
|
allow_credentials: None,
|
|
588
|
+
..Default::default()
|
|
584
589
|
};
|
|
585
590
|
|
|
586
591
|
assert!(!is_origin_allowed("http://example.com", &config.allowed_origins));
|
|
@@ -601,6 +606,7 @@ mod tests {
|
|
|
601
606
|
expose_headers: None,
|
|
602
607
|
max_age: None,
|
|
603
608
|
allow_credentials: None,
|
|
609
|
+
..Default::default()
|
|
604
610
|
};
|
|
605
611
|
|
|
606
612
|
assert!(!is_origin_allowed("https://example.com", &config.allowed_origins));
|
|
@@ -620,6 +626,7 @@ mod tests {
|
|
|
620
626
|
expose_headers: None,
|
|
621
627
|
max_age: None,
|
|
622
628
|
allow_credentials: None,
|
|
629
|
+
..Default::default()
|
|
623
630
|
};
|
|
624
631
|
|
|
625
632
|
assert!(!is_origin_allowed("https://example.com/", &config.allowed_origins));
|
|
@@ -640,6 +647,7 @@ mod tests {
|
|
|
640
647
|
expose_headers: None,
|
|
641
648
|
max_age: None,
|
|
642
649
|
allow_credentials: None,
|
|
650
|
+
..Default::default()
|
|
643
651
|
};
|
|
644
652
|
|
|
645
653
|
// SECURITY NOTE: "null" origin is allowed by wildcard in current implementation
|
|
@@ -652,6 +660,7 @@ mod tests {
|
|
|
652
660
|
expose_headers: None,
|
|
653
661
|
max_age: None,
|
|
654
662
|
allow_credentials: None,
|
|
663
|
+
..Default::default()
|
|
655
664
|
};
|
|
656
665
|
assert!(is_origin_allowed("null", &with_explicit_null.allowed_origins));
|
|
657
666
|
}
|
|
@@ -666,6 +675,7 @@ mod tests {
|
|
|
666
675
|
expose_headers: None,
|
|
667
676
|
max_age: None,
|
|
668
677
|
allow_credentials: None,
|
|
678
|
+
..Default::default()
|
|
669
679
|
};
|
|
670
680
|
assert!(!is_origin_allowed("", &config_with_wildcard.allowed_origins));
|
|
671
681
|
|
|
@@ -676,6 +686,7 @@ mod tests {
|
|
|
676
686
|
expose_headers: None,
|
|
677
687
|
max_age: None,
|
|
678
688
|
allow_credentials: None,
|
|
689
|
+
..Default::default()
|
|
679
690
|
};
|
|
680
691
|
assert!(!is_origin_allowed("", &config_with_explicit.allowed_origins));
|
|
681
692
|
}
|
|
@@ -705,6 +716,7 @@ mod tests {
|
|
|
705
716
|
expose_headers: None,
|
|
706
717
|
max_age: None,
|
|
707
718
|
allow_credentials: None,
|
|
719
|
+
..Default::default()
|
|
708
720
|
};
|
|
709
721
|
|
|
710
722
|
assert!(is_origin_allowed("https://trusted1.com", &config.allowed_origins));
|
|
@@ -728,6 +740,7 @@ mod tests {
|
|
|
728
740
|
expose_headers: None,
|
|
729
741
|
max_age: None,
|
|
730
742
|
allow_credentials: None,
|
|
743
|
+
..Default::default()
|
|
731
744
|
};
|
|
732
745
|
|
|
733
746
|
assert!(is_origin_allowed("https://example.com", &config.allowed_origins));
|
|
@@ -747,6 +760,7 @@ mod tests {
|
|
|
747
760
|
expose_headers: None,
|
|
748
761
|
max_age: Some(3600),
|
|
749
762
|
allow_credentials: Some(false),
|
|
763
|
+
..Default::default()
|
|
750
764
|
};
|
|
751
765
|
|
|
752
766
|
let mut headers = HeaderMap::new();
|
|
@@ -798,6 +812,7 @@ mod tests {
|
|
|
798
812
|
expose_headers: None,
|
|
799
813
|
max_age: None,
|
|
800
814
|
allow_credentials: None,
|
|
815
|
+
..Default::default()
|
|
801
816
|
};
|
|
802
817
|
|
|
803
818
|
let test_cases = vec![
|
|
@@ -836,6 +851,7 @@ mod tests {
|
|
|
836
851
|
expose_headers: None,
|
|
837
852
|
max_age: None,
|
|
838
853
|
allow_credentials: None,
|
|
854
|
+
..Default::default()
|
|
839
855
|
};
|
|
840
856
|
|
|
841
857
|
let test_cases = vec![
|
|
@@ -883,6 +899,7 @@ mod tests {
|
|
|
883
899
|
expose_headers: Some(vec!["x-custom".to_string()]),
|
|
884
900
|
max_age: None,
|
|
885
901
|
allow_credentials: Some(true),
|
|
902
|
+
..Default::default()
|
|
886
903
|
};
|
|
887
904
|
|
|
888
905
|
let mut response = Response::new(Body::empty());
|
|
@@ -908,6 +925,7 @@ mod tests {
|
|
|
908
925
|
expose_headers: None,
|
|
909
926
|
max_age: None,
|
|
910
927
|
allow_credentials: None,
|
|
928
|
+
..Default::default()
|
|
911
929
|
};
|
|
912
930
|
|
|
913
931
|
let mut headers = HeaderMap::new();
|
|
@@ -943,6 +961,7 @@ mod tests {
|
|
|
943
961
|
expose_headers: None,
|
|
944
962
|
max_age: None,
|
|
945
963
|
allow_credentials: None,
|
|
964
|
+
..Default::default()
|
|
946
965
|
};
|
|
947
966
|
|
|
948
967
|
let test_cases = vec!["GET", "get", "Get", "POST", "post"];
|
|
@@ -971,6 +990,7 @@ mod tests {
|
|
|
971
990
|
expose_headers: None,
|
|
972
991
|
max_age: Some(7200),
|
|
973
992
|
allow_credentials: None,
|
|
993
|
+
..Default::default()
|
|
974
994
|
};
|
|
975
995
|
|
|
976
996
|
let mut headers = HeaderMap::new();
|
|
@@ -995,6 +1015,7 @@ mod tests {
|
|
|
995
1015
|
expose_headers: None,
|
|
996
1016
|
max_age: None,
|
|
997
1017
|
allow_credentials: None,
|
|
1018
|
+
..Default::default()
|
|
998
1019
|
};
|
|
999
1020
|
|
|
1000
1021
|
assert!(!is_origin_allowed("https://api.example.com", &config.allowed_origins));
|
|
@@ -153,10 +153,14 @@ impl Handler for DependencyInjectingHandler {
|
|
|
153
153
|
|
|
154
154
|
let core_request_data = spikard_core::RequestData {
|
|
155
155
|
path_params: Arc::clone(&request_data.path_params),
|
|
156
|
-
query_params: request_data.query_params
|
|
157
|
-
|
|
156
|
+
query_params: Arc::try_unwrap(Arc::clone(&request_data.query_params))
|
|
157
|
+
.unwrap_or_else(|arc| (*arc).clone()),
|
|
158
|
+
validated_params: request_data
|
|
159
|
+
.validated_params
|
|
160
|
+
.as_ref()
|
|
161
|
+
.map(|arc| Arc::try_unwrap(Arc::clone(arc)).unwrap_or_else(|a| (*a).clone())),
|
|
158
162
|
raw_query_params: Arc::clone(&request_data.raw_query_params),
|
|
159
|
-
body: request_data.body.clone(),
|
|
163
|
+
body: Arc::try_unwrap(Arc::clone(&request_data.body)).unwrap_or_else(|arc| (*arc).clone()),
|
|
160
164
|
raw_body: request_data.raw_body.clone(),
|
|
161
165
|
headers: Arc::clone(&request_data.headers),
|
|
162
166
|
cookies: Arc::clone(&request_data.cookies),
|
|
@@ -346,10 +350,10 @@ mod tests {
|
|
|
346
350
|
fn create_request_data() -> RequestData {
|
|
347
351
|
RequestData {
|
|
348
352
|
path_params: Arc::new(HashMap::new()),
|
|
349
|
-
query_params: serde_json::Value::Null,
|
|
353
|
+
query_params: Arc::new(serde_json::Value::Null),
|
|
350
354
|
validated_params: None,
|
|
351
355
|
raw_query_params: Arc::new(HashMap::new()),
|
|
352
|
-
body: serde_json::Value::Null,
|
|
356
|
+
body: Arc::new(serde_json::Value::Null),
|
|
353
357
|
raw_body: None,
|
|
354
358
|
headers: Arc::new(HashMap::new()),
|
|
355
359
|
cookies: Arc::new(HashMap::new()),
|
|
@@ -1163,7 +1167,7 @@ mod tests {
|
|
|
1163
1167
|
.unwrap();
|
|
1164
1168
|
let mut request_data = create_request_data();
|
|
1165
1169
|
request_data.method = "POST".to_string();
|
|
1166
|
-
request_data.body = serde_json::json!({"key": "value"});
|
|
1170
|
+
request_data.body = Arc::new(serde_json::json!({"key": "value"}));
|
|
1167
1171
|
|
|
1168
1172
|
let result = di_handler.call(request, request_data).await;
|
|
1169
1173
|
|
|
@@ -1602,10 +1606,10 @@ mod tests {
|
|
|
1602
1606
|
|
|
1603
1607
|
let request_data = RequestData {
|
|
1604
1608
|
path_params: Arc::new(path_params.clone()),
|
|
1605
|
-
query_params: serde_json::json!({"filter": "active", "sort": "name"}),
|
|
1609
|
+
query_params: Arc::new(serde_json::json!({"filter": "active", "sort": "name"})),
|
|
1606
1610
|
validated_params: None,
|
|
1607
1611
|
raw_query_params: Arc::new(raw_query_params.clone()),
|
|
1608
|
-
body: serde_json::json!({"name": "John", "email": "john@example.com"}),
|
|
1612
|
+
body: Arc::new(serde_json::json!({"name": "John", "email": "john@example.com"})),
|
|
1609
1613
|
raw_body: Some(bytes::Bytes::from(r#"{"name":"John","email":"john@example.com"}"#)),
|
|
1610
1614
|
headers: Arc::new(headers.clone()),
|
|
1611
1615
|
cookies: Arc::new(cookies.clone()),
|