v0.34.12
This commit is contained in:
1 parent
37c197c095
commit
5ac3617947
48 files changed
+1049
-255
No files matched your search
Vendored
+84
@@ -62,6 +62,90 @@ mod cold_tier_tests {
|
||||
panic!("cold tier did not persist in time");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn upstream_cache_headers_survive_cold_demotion_and_hit_annotation() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let settings = cold_settings();
|
||||
let shutdown = CancellationToken::new();
|
||||
let cache = Cache::start(
|
||||
11,
|
||||
Some(dir.path().to_path_buf()),
|
||||
settings.clone(),
|
||||
shutdown.clone(),
|
||||
);
|
||||
let ticket = cache
|
||||
.ticket_scoped(
|
||||
11,
|
||||
"cold-test",
|
||||
"http",
|
||||
"cold.example",
|
||||
"origin@127.0.0.1:8080",
|
||||
"/origin-cache",
|
||||
&HeaderMap::new(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut response = Response::new(Body::from("cached-origin-body"));
|
||||
response.headers_mut().insert(
|
||||
"cache-control",
|
||||
HeaderValue::from_static("public, max-age=120"),
|
||||
);
|
||||
response.headers_mut().append(
|
||||
"cache-status",
|
||||
HeaderValue::from_static("OriginCache; hit; ttl=300"),
|
||||
);
|
||||
response
|
||||
.headers_mut()
|
||||
.insert("x-cache", HeaderValue::from_static("Origin-HIT"));
|
||||
let plan = plan(
|
||||
StatusCode::OK,
|
||||
response.headers(),
|
||||
&settings,
|
||||
Duration::ZERO,
|
||||
)
|
||||
.unwrap();
|
||||
cache
|
||||
.wrap(response, ticket.clone(), plan, &settings)
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap();
|
||||
fill(&cache, &settings, "/second", "other-cached-body").await;
|
||||
wait_for_cold(&cache, 1).await;
|
||||
let mut hit = cache
|
||||
.get_tiered(&ticket, &settings)
|
||||
.await
|
||||
.expect("COLD hit");
|
||||
assert_eq!(hit.response.headers()["x-cache"], "Origin-HIT");
|
||||
assert_eq!(
|
||||
hit.response.headers()["cache-status"],
|
||||
"OriginCache; hit; ttl=300"
|
||||
);
|
||||
let display = Settings {
|
||||
expose_status: true,
|
||||
expose_cache_status: true,
|
||||
..settings.clone()
|
||||
};
|
||||
display.annotate_reason_transformed(
|
||||
hit.response.headers_mut(),
|
||||
"HIT",
|
||||
hit.hits,
|
||||
hit.remaining_ttl,
|
||||
"cache_hit",
|
||||
false,
|
||||
);
|
||||
assert_eq!(hit.response.headers()["x-cache"], "HIT");
|
||||
assert_eq!(hit.response.headers()["x-upstream-cache"], "Origin-HIT");
|
||||
assert_eq!(
|
||||
hit.response
|
||||
.headers()
|
||||
.get_all("cache-status")
|
||||
.iter()
|
||||
.count(),
|
||||
2
|
||||
);
|
||||
shutdown.cancel();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn selective_purge_removes_matching_cold_object() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
||||
Vendored
+200
-31
@@ -103,7 +103,10 @@ mod dashboard_tests {
|
||||
}
|
||||
#[test]
|
||||
fn bypass_reasons_are_fixed_and_private_inputs_are_not_echoed() {
|
||||
let settings = Settings { varnish_compatible: false, ..Settings::default() };
|
||||
let settings = Settings {
|
||||
varnish_compatible: false,
|
||||
..Settings::default()
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("cookie", HeaderValue::from_static("session=very-private"));
|
||||
assert_eq!(
|
||||
@@ -125,13 +128,15 @@ mod dashboard_tests {
|
||||
expose_cache_status: true,
|
||||
..settings
|
||||
};
|
||||
diagnostics.annotate_reason(&mut headers, "BYPASS", 0, 0, "request_cookie");
|
||||
assert_eq!(headers["x-cache-reason"], "request_cookie");
|
||||
assert_eq!(
|
||||
headers["cache-status"],
|
||||
"RustProxy; fwd=bypass; detail=\"request_cookie\""
|
||||
diagnostics.annotate_reason_transformed(
|
||||
&mut headers,
|
||||
"BYPASS",
|
||||
0,
|
||||
0,
|
||||
"request_cookie",
|
||||
true,
|
||||
);
|
||||
diagnostics.annotate_transformed(&mut headers, true);
|
||||
assert_eq!(headers["x-cache-reason"], "request_cookie");
|
||||
assert_eq!(
|
||||
headers["cache-status"],
|
||||
"RustProxy; fwd=bypass; detail=\"request_cookie\"; transformed"
|
||||
@@ -149,29 +154,39 @@ mod dashboard_tests {
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-cache", HeaderValue::from_static("untrusted-upstream"));
|
||||
headers.insert(
|
||||
"cache-status",
|
||||
HeaderValue::from_static("untrusted-upstream"),
|
||||
headers.insert("cache-status", HeaderValue::from_static("Origin; hit"));
|
||||
global.annotate_reason_transformed(
|
||||
&mut headers,
|
||||
"MISS",
|
||||
0,
|
||||
0,
|
||||
"response_headers_policy",
|
||||
true,
|
||||
);
|
||||
global.annotate_reason(&mut headers, "MISS", 0, 0, "response_headers_policy");
|
||||
global.annotate_transformed(&mut headers, true);
|
||||
for name in [
|
||||
"x-cache",
|
||||
"x-cache-hits",
|
||||
"cache-status",
|
||||
"x-cache-reason",
|
||||
"x-cache-resident-age",
|
||||
] {
|
||||
assert!(!headers.contains_key(name), "{name} leaked on MISS");
|
||||
}
|
||||
assert_eq!(headers["x-cache"], "untrusted-upstream");
|
||||
assert_eq!(headers["cache-status"], "Origin; hit");
|
||||
assert!(!headers.contains_key("x-cache-hits"));
|
||||
assert!(!headers.contains_key("x-cache-reason"));
|
||||
assert_eq!(headers["via"], "RustProxy");
|
||||
global.annotate_reason(&mut headers, "BYPASS", 0, 0, "request_cookie");
|
||||
assert!(!headers.contains_key("cache-status"));
|
||||
assert_eq!(headers.get_all("cache-status").iter().count(), 1);
|
||||
global.annotate_reason(&mut headers, "HIT", 4, 60, "cache_hit");
|
||||
global.annotate_resident_age(&mut headers, 10);
|
||||
assert_eq!(headers["x-cache"], "HIT");
|
||||
assert_eq!(headers["x-cache-hits"], "4");
|
||||
assert!(headers["cache-status"].to_str().unwrap().contains("; hit;"));
|
||||
let chain: Vec<_> = headers
|
||||
.get_all("cache-status")
|
||||
.iter()
|
||||
.map(|value| value.to_str().unwrap())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
chain,
|
||||
vec![
|
||||
"Origin; hit",
|
||||
"RustProxy; hit; ttl=60; detail=\"cache_hit\""
|
||||
]
|
||||
);
|
||||
assert_eq!(headers["x-upstream-cache"], "untrusted-upstream");
|
||||
assert_eq!(headers["x-cache-resident-age"], "10");
|
||||
let mut override_on = Overrides {
|
||||
expose_only_hits: Some(false),
|
||||
@@ -210,7 +225,7 @@ mod dashboard_tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disabled_cache_strips_diagnostics_without_exposing_bypass_headers() {
|
||||
fn disabled_cache_forwards_origin_diagnostics_unchanged() {
|
||||
let settings = Settings {
|
||||
enabled: false,
|
||||
expose_status: true,
|
||||
@@ -228,16 +243,170 @@ mod dashboard_tests {
|
||||
headers.insert("x-cache-resident-age", HeaderValue::from_static("5"));
|
||||
settings.annotate_reason(&mut headers, "BYPASS", 0, 0, "cache_disabled");
|
||||
settings.annotate_resident_age(&mut headers, 9);
|
||||
for name in [
|
||||
"x-cache",
|
||||
"x-cache-hits",
|
||||
"x-cache-status",
|
||||
assert_eq!(headers["x-cache"], "origin");
|
||||
assert_eq!(headers["x-cache-hits"], "99");
|
||||
assert_eq!(headers["x-cache-status"], "origin");
|
||||
assert_eq!(headers["cache-status"], "Origin; hit");
|
||||
assert_eq!(headers["x-cache-reason"], "origin");
|
||||
assert_eq!(headers["x-cache-resident-age"], "5");
|
||||
assert!(!headers.contains_key("x-upstream-cache"));
|
||||
assert!(!headers.contains_key("via"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_and_legacy_header_collisions_keep_origin_values() {
|
||||
let settings = Settings {
|
||||
expose_status: true,
|
||||
expose_hits: true,
|
||||
expose_cache_status: true,
|
||||
expose_resident_age: true,
|
||||
..Settings::default()
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.append(
|
||||
"cache-status",
|
||||
"x-cache-reason",
|
||||
"x-cache-resident-age",
|
||||
HeaderValue::from_static("Inner; hit; ttl=100"),
|
||||
);
|
||||
headers.append("cache-status", HeaderValue::from_static("Shield; fwd=miss"));
|
||||
for (name, value) in [
|
||||
("x-cache", "BACKEND-MISS"),
|
||||
("x-cache-hits", "21"),
|
||||
("x-cache-reason", "origin_reason"),
|
||||
("x-cache-resident-age", "17"),
|
||||
("x-cache-status", "backend-status"),
|
||||
] {
|
||||
assert!(!headers.contains_key(name), "{name}");
|
||||
headers.insert(name, value.parse().unwrap());
|
||||
}
|
||||
settings.annotate_reason_transformed(&mut headers, "HIT", 3, 482, "cache_hit", true);
|
||||
settings.annotate_resident_age(&mut headers, 9);
|
||||
let cache_chain: Vec<_> = headers
|
||||
.get_all("cache-status")
|
||||
.iter()
|
||||
.map(|value| value.to_str().unwrap())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
cache_chain,
|
||||
vec![
|
||||
"Inner; hit; ttl=100",
|
||||
"Shield; fwd=miss",
|
||||
"RustProxy; hit; ttl=482; detail=\"cache_hit\"; transformed",
|
||||
]
|
||||
);
|
||||
assert_eq!(headers["x-cache"], "HIT");
|
||||
assert_eq!(headers["x-upstream-cache"], "BACKEND-MISS");
|
||||
assert_eq!(headers["x-cache-hits"], "3");
|
||||
assert_eq!(headers["x-upstream-cache-hits"], "21");
|
||||
assert_eq!(headers["x-cache-reason"], "cache_hit");
|
||||
assert_eq!(headers["x-upstream-cache-reason"], "origin_reason");
|
||||
assert_eq!(headers["x-cache-resident-age"], "9");
|
||||
assert_eq!(headers["x-upstream-cache-resident-age"], "17");
|
||||
assert_eq!(headers["x-cache-status"], "backend-status");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagnostic_options_do_not_hide_backend_cache_status() {
|
||||
for (enabled, status, only_hits) in [
|
||||
(true, false, false),
|
||||
(true, true, true),
|
||||
(false, true, false),
|
||||
] {
|
||||
let settings = Settings {
|
||||
enabled,
|
||||
expose_status: status,
|
||||
expose_cache_status: status,
|
||||
expose_only_hits: only_hits,
|
||||
..Settings::default()
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-cache", HeaderValue::from_static("Backend; MISS"));
|
||||
headers.insert("cache-status", HeaderValue::from_static("Origin; fwd=miss"));
|
||||
settings.annotate_reason_transformed(&mut headers, "MISS", 0, 0, "cache_miss", true);
|
||||
assert_eq!(headers["x-cache"], "Backend; MISS");
|
||||
assert_eq!(headers["cache-status"], "Origin; fwd=miss");
|
||||
assert_eq!(headers.get_all("cache-status").iter().count(), 1);
|
||||
assert!(!headers.contains_key("x-upstream-cache"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_upstream_legacy_values_are_not_collapsed_or_discarded() {
|
||||
let settings = Settings {
|
||||
expose_status: true,
|
||||
expose_cache_status: true,
|
||||
..Settings::default()
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.append("x-cache", HeaderValue::from_static("inner-HIT"));
|
||||
headers.append("x-cache", HeaderValue::from_static("outer-MISS"));
|
||||
headers.append("cache-status", HeaderValue::from_static("Inner; hit"));
|
||||
headers.append("cache-status", HeaderValue::from_static("Outer; fwd=miss"));
|
||||
settings.annotate_reason(&mut headers, "MISS", 0, 0, "cache_miss");
|
||||
let upstream_values: Vec<_> = headers
|
||||
.get_all("x-upstream-cache")
|
||||
.iter()
|
||||
.map(|value| value.to_str().unwrap())
|
||||
.collect();
|
||||
assert_eq!(upstream_values, vec!["inner-HIT", "outer-MISS"]);
|
||||
assert_eq!(headers["x-cache"], "MISS");
|
||||
let status_chain: Vec<_> = headers
|
||||
.get_all("cache-status")
|
||||
.iter()
|
||||
.map(|value| value.to_str().unwrap())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
status_chain,
|
||||
vec![
|
||||
"Inner; hit",
|
||||
"Outer; fwd=miss",
|
||||
"RustProxy; fwd=miss; detail=\"cache_miss\"",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_no_cache_miss_does_not_claim_a_fresh_candidate() {
|
||||
let settings = Settings {
|
||||
expose_cache_status: true,
|
||||
..Settings::default()
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
settings.annotate_reason(&mut headers, "MISS", 0, 0, "request_no_cache");
|
||||
assert_eq!(
|
||||
headers["cache-status"],
|
||||
"RustProxy; fwd=miss; detail=\"request_no_cache\"",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revalidated_304_is_forwarded_under_rfc_9211_even_if_legacy_x_cache_is_hit() {
|
||||
let settings = Settings {
|
||||
expose_status: true,
|
||||
expose_cache_status: true,
|
||||
..Settings::default()
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("cache-status", HeaderValue::from_static("Origin; hit"));
|
||||
settings.annotate_reason_transformed(
|
||||
&mut headers,
|
||||
"HIT",
|
||||
2,
|
||||
50,
|
||||
"cache_revalidated",
|
||||
false,
|
||||
);
|
||||
assert_eq!(headers["x-cache"], "HIT");
|
||||
let chain: Vec<_> = headers
|
||||
.get_all("cache-status")
|
||||
.iter()
|
||||
.map(|v| v.to_str().unwrap())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
chain,
|
||||
vec![
|
||||
"Origin; hit",
|
||||
"RustProxy; fwd=stale; fwd-status=304; detail=\"cache_revalidated\"",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Vendored
+1
-8
@@ -97,14 +97,7 @@ impl Candidate {
|
||||
self.headers.insert("date", value);
|
||||
}
|
||||
}
|
||||
for name in [
|
||||
"x-request-id",
|
||||
"x-cache",
|
||||
"x-cache-hits",
|
||||
"x-cache-status",
|
||||
"x-cache-reason",
|
||||
"cache-status",
|
||||
] {
|
||||
for name in ["x-request-id"] {
|
||||
self.headers.remove(name);
|
||||
}
|
||||
let vary = std::mem::take(&mut self.plan.vary);
|
||||
|
||||
Vendored
+1
-8
@@ -27,14 +27,7 @@ impl Cache {
|
||||
headers.insert("date", value);
|
||||
}
|
||||
}
|
||||
for name in [
|
||||
"x-request-id",
|
||||
"x-cache",
|
||||
"x-cache-hits",
|
||||
"x-cache-status",
|
||||
"x-cache-reason",
|
||||
"cache-status",
|
||||
] {
|
||||
for name in ["x-request-id"] {
|
||||
headers.remove(name);
|
||||
}
|
||||
let hits = cached.hits.saturating_add(1);
|
||||
|
||||
Vendored
+87
-74
@@ -171,16 +171,41 @@ impl Settings {
|
||||
.any(|item| item.eq_ignore_ascii_case(extension))
|
||||
}
|
||||
pub fn annotate(&self, headers: &mut HeaderMap, outcome: &'static str, hits: u64, ttl: u64) {
|
||||
for name in [
|
||||
"x-cache",
|
||||
"x-cache-hits",
|
||||
"x-cache-status",
|
||||
"cache-status",
|
||||
"x-cache-reason",
|
||||
"x-cache-resident-age",
|
||||
] {
|
||||
headers.remove(name);
|
||||
}
|
||||
self.annotate_internal(headers, outcome, hits, ttl, None, false);
|
||||
}
|
||||
|
||||
pub fn annotate_reason(
|
||||
&self,
|
||||
headers: &mut HeaderMap,
|
||||
outcome: &'static str,
|
||||
hits: u64,
|
||||
ttl: u64,
|
||||
reason: &'static str,
|
||||
) {
|
||||
self.annotate_internal(headers, outcome, hits, ttl, Some(reason), false);
|
||||
}
|
||||
|
||||
pub fn annotate_reason_transformed(
|
||||
&self,
|
||||
headers: &mut HeaderMap,
|
||||
outcome: &'static str,
|
||||
hits: u64,
|
||||
ttl: u64,
|
||||
reason: &'static str,
|
||||
transformed: bool,
|
||||
) {
|
||||
self.annotate_internal(headers, outcome, hits, ttl, Some(reason), transformed);
|
||||
}
|
||||
|
||||
fn annotate_internal(
|
||||
&self,
|
||||
headers: &mut HeaderMap,
|
||||
outcome: &'static str,
|
||||
hits: u64,
|
||||
ttl: u64,
|
||||
reason: Option<&'static str>,
|
||||
transformed: bool,
|
||||
) {
|
||||
if !self.enabled {
|
||||
return;
|
||||
}
|
||||
@@ -196,89 +221,77 @@ impl Settings {
|
||||
return;
|
||||
}
|
||||
if self.expose_status {
|
||||
headers.insert("x-cache", HeaderValue::from_static(outcome));
|
||||
insert_cache_diagnostic(
|
||||
headers,
|
||||
"x-cache",
|
||||
"x-upstream-cache",
|
||||
HeaderValue::from_static(outcome),
|
||||
);
|
||||
if let Some(reason) = reason {
|
||||
insert_cache_diagnostic(
|
||||
headers,
|
||||
"x-cache-reason",
|
||||
"x-upstream-cache-reason",
|
||||
HeaderValue::from_static(reason),
|
||||
);
|
||||
}
|
||||
}
|
||||
if self.expose_hits {
|
||||
headers.insert(
|
||||
insert_cache_diagnostic(
|
||||
headers,
|
||||
"x-cache-hits",
|
||||
"x-upstream-cache-hits",
|
||||
HeaderValue::from_str(&hits.to_string()).expect("integer header"),
|
||||
);
|
||||
}
|
||||
if self.expose_cache_status {
|
||||
let value = match outcome {
|
||||
"HIT" => format!("RustProxy; hit; ttl={ttl}"),
|
||||
"MISS" => "RustProxy; fwd=uri-miss".into(),
|
||||
_ => "RustProxy; fwd=bypass".into(),
|
||||
let mut value = if matches!(
|
||||
reason,
|
||||
Some("cache_revalidated" | "cache_revalidated_uncached")
|
||||
) {
|
||||
"RustProxy; fwd=stale; fwd-status=304".to_owned()
|
||||
} else {
|
||||
match outcome {
|
||||
"MISS" => "RustProxy; fwd=miss".to_owned(),
|
||||
_ => "RustProxy; fwd=bypass".to_owned(),
|
||||
}
|
||||
};
|
||||
headers.insert(
|
||||
if let Some(reason) = reason {
|
||||
value.push_str(&format!("; detail=\"{reason}\""));
|
||||
}
|
||||
if transformed {
|
||||
value.push_str("; transformed");
|
||||
}
|
||||
headers.append(
|
||||
"cache-status",
|
||||
HeaderValue::from_str(&value).expect("generated cache status"),
|
||||
HeaderValue::from_str(&value).expect("generated Cache-Status member"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn annotate_resident_age(&self, headers: &mut HeaderMap, resident_age: u64) {
|
||||
headers.remove("x-cache-resident-age");
|
||||
if !self.enabled {
|
||||
return;
|
||||
}
|
||||
if self.expose_resident_age {
|
||||
headers.insert(
|
||||
if self.enabled && self.expose_resident_age {
|
||||
insert_cache_diagnostic(
|
||||
headers,
|
||||
"x-cache-resident-age",
|
||||
"x-upstream-cache-resident-age",
|
||||
HeaderValue::from_str(&resident_age.to_string()).expect("integer header"),
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn annotate_transformed(&self, headers: &mut HeaderMap, transformed: bool) {
|
||||
if !self.enabled
|
||||
|| !self.expose_cache_status
|
||||
|| !transformed
|
||||
|| (self.expose_only_hits
|
||||
&& !headers
|
||||
.get("cache-status")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.is_some_and(|value| value.starts_with("RustProxy; hit")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
let base = headers
|
||||
.get("cache-status")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.unwrap_or("RustProxy");
|
||||
if base
|
||||
.split(';')
|
||||
.any(|part| part.trim().eq_ignore_ascii_case("transformed"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if let Ok(value) = format!("{base}; transformed").parse() {
|
||||
headers.insert("cache-status", value);
|
||||
}
|
||||
}
|
||||
pub fn annotate_reason(
|
||||
&self,
|
||||
headers: &mut HeaderMap,
|
||||
outcome: &'static str,
|
||||
hits: u64,
|
||||
ttl: u64,
|
||||
reason: &'static str,
|
||||
) {
|
||||
self.annotate(headers, outcome, hits, ttl);
|
||||
if !self.enabled || (self.expose_only_hits && outcome != "HIT") {
|
||||
return;
|
||||
}
|
||||
if self.expose_status {
|
||||
headers.insert("x-cache-reason", HeaderValue::from_static(reason));
|
||||
}
|
||||
if self.expose_cache_status {
|
||||
let base = headers
|
||||
.get("cache-status")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("RustProxy");
|
||||
if let Ok(value) = format!("{base}; detail=\"{reason}\"").parse() {
|
||||
headers.insert("cache-status", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_cache_diagnostic(
|
||||
headers: &mut HeaderMap,
|
||||
name: &'static str,
|
||||
upstream_name: &'static str,
|
||||
value: HeaderValue,
|
||||
) {
|
||||
let upstream_values: Vec<HeaderValue> = headers.get_all(name).iter().cloned().collect();
|
||||
for upstream_value in upstream_values {
|
||||
headers.append(upstream_name, upstream_value);
|
||||
}
|
||||
headers.insert(name, value);
|
||||
}
|
||||
fn valid_relative_cache_path(value: &str) -> bool {
|
||||
if value.is_empty() || value.len() > 256 {
|
||||
|
||||
Reference in new issue
Block a user