v0.34.0_2
This commit is contained in:
1 parent
0a8bc6f040
commit
434e789144
38 files changed
+899
-238
No files matched your search
+14
-1
@@ -301,6 +301,7 @@ pub struct MeteredBody {
|
||||
/// For a response, backend selection is final before this wrapper is made.
|
||||
/// Keep the selected lease alive and avoid locking Exchange.backend per frame.
|
||||
response_backend: Option<Arc<crate::balance::Lease>>,
|
||||
request_backend: Option<Arc<crate::balance::Lease>>,
|
||||
direction: Direction,
|
||||
timer: Option<Pin<Box<Sleep>>>,
|
||||
deadline: tokio::time::Instant,
|
||||
@@ -317,6 +318,7 @@ impl MeteredBody {
|
||||
inner,
|
||||
exchange: Some(exchange),
|
||||
response_backend,
|
||||
request_backend: None,
|
||||
direction,
|
||||
// Ready/empty bodies never need a timer allocation. Keep the
|
||||
// original deadline so a delayed first poll still times out.
|
||||
@@ -330,6 +332,7 @@ impl MeteredBody {
|
||||
self.finished = true;
|
||||
self.timer.take();
|
||||
self.response_backend.take();
|
||||
self.request_backend.take();
|
||||
self.exchange.take();
|
||||
}
|
||||
}
|
||||
@@ -350,7 +353,17 @@ impl HttpBody for MeteredBody {
|
||||
if let (Some(data), Some(exchange)) = (frame.data_ref(), this.exchange.as_ref()) {
|
||||
let count = data.len() as u64;
|
||||
match this.direction {
|
||||
Direction::Request => exchange.backend_bytes(count, true),
|
||||
Direction::Request => {
|
||||
// Backend is selected before a non-empty upstream
|
||||
// upload is polled. No retry of a streamed write.
|
||||
if this.request_backend.is_none() {
|
||||
this.request_backend = exchange.backend
|
||||
.lock().unwrap_or_else(|e| e.into_inner()).clone();
|
||||
}
|
||||
if let Some(lease) = &this.request_backend {
|
||||
lease.bytes(count, true);
|
||||
}
|
||||
}
|
||||
Direction::Response => {
|
||||
if let Some(lease) = &this.response_backend {
|
||||
lease.bytes(count, false);
|
||||
|
||||
@@ -125,7 +125,9 @@ let cache_settings = route
|
||||
let transform_settings = route.frontend.as_ref().map(|f| &f.transform);
|
||||
let scope = route.frontend.as_ref().map(|f| f.id.as_str()).unwrap_or("");
|
||||
if let Some(exchange) = request.extensions().get::<Arc<Exchange>>() {
|
||||
*exchange.route.lock().unwrap_or_else(|e| e.into_inner()) = Some(route.id.clone());
|
||||
// The resolved route was already recorded before local response, LB and
|
||||
// ACL decisions. Do not acquire the route mutex and copy its ID twice on
|
||||
// every reverse-proxy request.
|
||||
exchange.set_cache_enabled(cache_settings.active_for(route.cache));
|
||||
exchange.set_transform_enabled(transform_settings.is_some_and(|settings| settings.enabled));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
fn request_id_hash(sequence: u64) -> String {
|
||||
use rand::TryRng;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
let mut nonce = [0u8; 32];
|
||||
let mut rng = rand::rngs::SysRng;
|
||||
rng.try_fill_bytes(&mut nonce)
|
||||
.expect("secure operating system random source unavailable");
|
||||
// Thread-local CSPRNG is seeded (and periodically reseeded) from the OS.
|
||||
// Do not make a getrandom syscall on the hot path for every HTTP request.
|
||||
// Keep hashing the per-process sequence as before for the same ID format.
|
||||
let nonce: [u8; 32] = rand::random();
|
||||
let mut hash = Sha256::new();
|
||||
hash.update(sequence.to_be_bytes());
|
||||
hash.update(nonce);
|
||||
|
||||
@@ -25,7 +25,7 @@ mod request_id_tests {
|
||||
.all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)));
|
||||
assert!(
|
||||
seen.insert(id),
|
||||
"fixed sequence must still use independent OS entropy"
|
||||
"fixed sequence must still use independent CSPRNG output"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,4 +595,23 @@ mod graceful_h2_tests {
|
||||
assert!(!is_graceful_h2_goaway(&protocol));
|
||||
assert!(!is_graceful_h2_goaway(&local));
|
||||
}
|
||||
#[test]
|
||||
fn compiled_request_origin_matches_previous_uri_parser() {
|
||||
let mut config = crate::config::demo_config("127.0.0.1:9000".parse().unwrap());
|
||||
config.vhosts[0].backends[0].url = "https://127.0.0.1:9000".into();
|
||||
config.vhosts[0].backends[0].tls_server_name = Some("backend.example".into());
|
||||
let snapshot = crate::config::compile(config, 1, &validation_policy()).unwrap();
|
||||
let backend = &snapshot.pools[0].members[0].backend;
|
||||
assert_eq!(backend.request_origin_uri.to_string(), backend.request_origin);
|
||||
for suffix in [
|
||||
"/", "/static/main.css", "/path?x=1&y=2", "/p%2Fq?url=https%3A//example.org/a",
|
||||
"/?q=%C5%82&empty=", "/double//slash", "/item#frag",
|
||||
] {
|
||||
let previous = format!("{}{suffix}", backend.request_origin).parse::<Uri>().ok();
|
||||
assert_eq!(upstream_uri(backend, suffix), previous, "{suffix}");
|
||||
}
|
||||
assert!(upstream_uri(backend, "http://attacker.invalid/").is_none());
|
||||
assert_eq!(backend.origin, "https://127.0.0.1:9000");
|
||||
}
|
||||
|
||||
}
|
||||
+7
-1
@@ -152,7 +152,13 @@ fn retry_safe_method(method: &Method) -> bool {
|
||||
}
|
||||
|
||||
fn upstream_uri(backend: &crate::config::CompiledBackend, suffix: &str) -> Option<Uri> {
|
||||
format!("{}{suffix}", backend.request_origin).parse().ok()
|
||||
// Reuse the validated scheme and authority from the immutable compiled
|
||||
// backend. Only the untrusted path/query is parsed per request. This also
|
||||
// prevents accidental authority changes from rewritten paths.
|
||||
let path: http::uri::PathAndQuery = suffix.parse().ok()?;
|
||||
let mut parts = backend.request_origin_uri.clone().into_parts();
|
||||
parts.path_and_query = Some(path);
|
||||
Uri::from_parts(parts).ok()
|
||||
}
|
||||
|
||||
fn legacy_websocket_origin(
|
||||
|
||||
@@ -56,14 +56,11 @@ async fn observe(State(state): State<Arc<App>>, mut request: Request, next: Next
|
||||
|| (request.version() == Version::HTTP_3
|
||||
&& request.method() == Method::CONNECT
|
||||
&& websocket::h3_protocol(&request) == Some("websocket"));
|
||||
if request.version() == Version::HTTP_11 {
|
||||
state.metrics.http1_requests.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
if request.version() == Version::HTTP_2 {
|
||||
state.metrics.http2_requests.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
if request.version() == Version::HTTP_3 {
|
||||
state.metrics.http3_requests.fetch_add(1, Ordering::Relaxed);
|
||||
match request.version() {
|
||||
Version::HTTP_11 => { state.metrics.http1_requests.fetch_add(1, Ordering::Relaxed); }
|
||||
Version::HTTP_2 => { state.metrics.http2_requests.fetch_add(1, Ordering::Relaxed); }
|
||||
Version::HTTP_3 => { state.metrics.http3_requests.fetch_add(1, Ordering::Relaxed); }
|
||||
_ => {}
|
||||
}
|
||||
let id = state.metrics.requests.fetch_add(1, Ordering::Relaxed) + 1;
|
||||
let request_id = request_id_hash(id);
|
||||
|
||||
Reference in new issue
Block a user