This commit is contained in:
Mateusz Gruszczyński committed 2026-10-05 09:30:21 +02:00
1 parent 1dea7f55c8
commit c819f0a335
151 files changed
+7610 -4264

No files matched your search

+2 -3
View File
@@ -14,8 +14,8 @@ use http_body_util::BodyExt;
use hyper::upgrade::{OnUpgrade, Upgraded};
use hyper_util::rt::TokioIo;
use rand::{rngs::OsRng, RngCore};
use sha1::{Digest, Sha1};
use serde::Serialize;
use sha1::{Digest, Sha1};
use std::{
collections::BTreeMap,
future::Future,
@@ -283,8 +283,7 @@ pub fn validate_response(
"invalid upstream upgrade"
);
ensure!(
!headers.contains_key("transfer-encoding")
&& !headers.contains_key("content-length"),
!headers.contains_key("transfer-encoding") && !headers.contains_key("content-length"),
"101 response must not include body framing"
);
for value in headers.get_all("connection").iter() {
+35 -12
View File
@@ -20,9 +20,11 @@ mod tests {
.header("sec-websocket-version", "13")
.body(Body::empty())
.unwrap();
request
.extensions_mut()
.insert("websocket".parse::<h3::ext::Protocol>().unwrap_or_else(|_| panic!("static websocket H3 protocol token must parse")));
request.extensions_mut().insert(
"websocket"
.parse::<h3::ext::Protocol>()
.unwrap_or_else(|_| panic!("static websocket H3 protocol token must parse")),
);
request
}
fn h2_request() -> Request {
@@ -80,22 +82,41 @@ mod tests {
#[test]
fn rfc8441_h2_response_rejects_h1_framing_fields() {
let handshake = validate_h2(&h2_request()).unwrap();
for (name, value) in [("connection", "close"), ("upgrade", "websocket"), ("content-length", "0")] {
for (name, value) in [
("connection", "close"),
("upgrade", "websocket"),
("content-length", "0"),
] {
let mut headers = HeaderMap::new();
headers.insert(
http::header::HeaderName::from_bytes(name.as_bytes()).unwrap(),
value.parse().unwrap(),
);
assert!(validate_h2_response(&headers, &handshake).is_err(), "accepted forbidden response {name}");
assert!(
validate_h2_response(&headers, &handshake).is_err(),
"accepted forbidden response {name}"
);
}
}
#[test]
fn tunnel_transport_metrics_classify_frontend_protocol() {
assert!(matches!(TunnelTransport::from_protocol("h1-upgrade"), TunnelTransport::H1));
assert!(matches!(TunnelTransport::from_protocol("h2-rfc8441-h1-upstream"), TunnelTransport::H2));
assert!(matches!(TunnelTransport::from_protocol("h2-rfc8441-h2-upstream"), TunnelTransport::H2));
assert!(matches!(TunnelTransport::from_protocol("h3-rfc9220"), TunnelTransport::H3));
assert!(matches!(
TunnelTransport::from_protocol("h1-upgrade"),
TunnelTransport::H1
));
assert!(matches!(
TunnelTransport::from_protocol("h2-rfc8441-h1-upstream"),
TunnelTransport::H2
));
assert!(matches!(
TunnelTransport::from_protocol("h2-rfc8441-h2-upstream"),
TunnelTransport::H2
));
assert!(matches!(
TunnelTransport::from_protocol("h3-rfc9220"),
TunnelTransport::H3
));
}
#[test]
@@ -139,9 +160,11 @@ mod tests {
assert!(validate_h3(&request).is_err(), "accepted forbidden {name}");
}
let mut request = h3_request();
request
.extensions_mut()
.insert("connect-udp".parse::<h3::ext::Protocol>().unwrap_or_else(|_| panic!("static connect-udp H3 protocol token must parse")));
request.extensions_mut().insert(
"connect-udp"
.parse::<h3::ext::Protocol>()
.unwrap_or_else(|_| panic!("static connect-udp H3 protocol token must parse")),
);
assert!(validate_h3(&request).is_err());
}
#[test]
+5 -1
View File
@@ -406,7 +406,11 @@ pub fn spawn_h2_idle_watch(state: Arc<App>, session: &Arc<TunnelSession>) {
return;
};
if session.inactive_for() >= idle {
session._active.exchange.failed.store(true, Ordering::Relaxed);
session
._active
.exchange
.failed
.store(true, Ordering::Relaxed);
session
._active
.exchange
+27 -11
View File
@@ -9,7 +9,9 @@ pub fn bridge_h2_to_h1(
lease: Option<Arc<ConnectionLease>>,
stop: Option<ServerShutdown>,
) -> Body {
let stop = stop.map(|value| value.0).unwrap_or_else(|| state.shutdown.clone());
let stop = stop
.map(|value| value.0)
.unwrap_or_else(|| state.shutdown.clone());
let session = state.tunnel_registry.register(
exchange.clone(),
upstream_label,
@@ -70,7 +72,10 @@ pub fn bridge_h2_to_h1(
Ok(read) => {
buffer.truncate(read);
record_session_bytes(&exchange, &session, read as u64, false);
Some((Ok::<Bytes, io::Error>(Bytes::from(buffer)), (upstream_read, session, exchange)))
Some((
Ok::<Bytes, io::Error>(Bytes::from(buffer)),
(upstream_read, session, exchange),
))
}
Err(error) => Some((Err(error), (upstream_read, session, exchange))),
}
@@ -212,21 +217,32 @@ pub async fn run_h3(
let stop = stop.map(|v| v.0).unwrap_or_else(|| state.shutdown.clone());
let started = Instant::now();
let activity = Arc::new(AtomicU64::new(0));
let session = state.tunnel_registry.register(
exchange.clone(),
upstream_label,
"h3-rfc9220",
stop,
lease,
);
let session =
state
.tunnel_registry
.register(exchange.clone(), upstream_label, "h3-rfc9220", stop, lease);
let stop = session.cancellation();
let upstream = TokioIo::new(upstream);
let (mut upstream_read, mut upstream_write) = tokio::io::split(upstream);
let result = {
let copying = async {
tokio::try_join!(
h3_client_to_backend(receive, &mut upstream_write, &exchange, &activity, started, &session),
backend_to_h3(&mut upstream_read, send, &exchange, &activity, started, &session)
h3_client_to_backend(
receive,
&mut upstream_write,
&exchange,
&activity,
started,
&session
),
backend_to_h3(
&mut upstream_read,
send,
&exchange,
&activity,
started,
&session
)
)?;
Ok::<(), io::Error>(())
};