Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+206
View File
@@ -0,0 +1,206 @@
use super::*;
use http_body_util::{BodyExt, Full};
use hyper_util::rt::{TokioExecutor, TokioIo};
use tokio::sync::Notify;
fn limits() -> Limits {
Limits {
http2: Some(http::Http2Limits::default()),
..Limits::default()
}
}
#[test]
fn idle_http2_connection_releases_capacity_after_response() {
runtime()
.run(async |scope| {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let port = listener.port().unwrap();
let incoming = listener.clone();
let limits = Limits {
connections: 1,
timeout_ms: 50,
..limits()
};
let serving = limits.clone();
let server = scope
.spawn(move |child| async move {
http::serve::<_, _, false>(&incoming, &serving, &child, route).await
})
.unwrap();
let stream = tokio::net::TcpStream::connect(("127.0.0.1", port as u16))
.await
.unwrap();
let (mut sender, connection) =
hyper::client::conn::http2::Builder::new(TokioExecutor::new())
.handshake::<_, Full<bytes::Bytes>>(TokioIo::new(stream))
.await
.unwrap();
let driver = scope.spawn_leaf(move || connection).unwrap();
let response = sender
.send_request(
hyper::Request::builder()
.uri("http://fixture.test/echo")
.body(Full::new(bytes::Bytes::from_static(b"first")))
.unwrap(),
)
.await
.unwrap();
assert_eq!(
response.into_body().collect().await.unwrap().to_bytes(),
b"first"[..]
);
tokio::time::timeout(Duration::from_secs(2), driver.wait())
.await
.unwrap()
.unwrap()
.unwrap();
assert_eq!(
http::send("127.0.0.1", port, request("/echo", b"second"), &limits)
.await
.unwrap()
.body
.as_ref(),
b"second"
);
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn http2_single_request_preserves_body_headers_and_validates_protocol_options() {
runtime()
.run(async |scope| {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let port = listener.port().unwrap();
let incoming = listener.clone();
let server = scope
.spawn(move |child| async move {
http::serve::<_, _, false>(&incoming, &limits(), &child, route).await
})
.unwrap();
let response = http::send("127.0.0.1", port, request("/echo", b"http2"), &limits())
.await
.unwrap();
assert_eq!(response.body.as_ref(), b"http2");
let headers = response
.headers
.iter()
.filter(|header| header.name == "x-data")
.collect::<Vec<_>>();
assert_eq!(headers.len(), 2);
assert_eq!(headers[0].value.as_ref(), [0x80]);
for header in ["Connection", "Keep-Alive", "Proxy-Connection", "TE"] {
let mut message = request("/", b"");
message.headers.push(Header {
name: header.into(),
value: b"invalid".to_vec().into(),
});
assert!(
http::send("127.0.0.1", port, message, &limits())
.await
.unwrap_err()
.contains("connection header")
);
}
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn http2_multiplexes_independent_streams_normalizes_authority_and_drains_goaway() {
for threads in [1, 2] {
let runtime = Runtime::new(dever_runtime::task::Limits {
threads,
workers: 2,
tasks: 32,
})
.unwrap();
runtime.run(async |scope| {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let port = listener.port().unwrap();
let incoming = listener.clone();
let started = Arc::new(Notify::new());
let ready = started.clone();
let release = Arc::new(Notify::new());
let released = release.clone();
let server = scope.spawn(move |child| async move {
http::serve::<_, _, false>(&incoming, &limits(), &child, move |request, _| {
let ready = ready.clone();
let released = released.clone();
async move {
assert!(request.headers.iter().any(|header| header.name == "host" && header.value.as_ref() == b"fixture.test"));
if request.target == "/slow" {
ready.notify_one();
released.notified().await;
}
Ok(Response { status: 200, headers: vec![], body: request.target.into_bytes().into() })
}
}).await
}).unwrap();
let stream = tokio::net::TcpStream::connect(("127.0.0.1", port as u16)).await.unwrap();
let (mut sender, connection) = hyper::client::conn::http2::Builder::new(TokioExecutor::new())
.handshake::<_, Full<bytes::Bytes>>(TokioIo::new(stream)).await.unwrap();
let driver = scope.spawn_leaf(move || connection).unwrap();
let message = |path: &str| hyper::Request::builder().uri(format!("http://fixture.test{path}"))
.body(Full::new(bytes::Bytes::new())).unwrap();
let slow = sender.send_request(message("/slow"));
tokio::pin!(slow);
tokio::select! {
result = &mut slow => panic!("slow stream finished before release: {result:?}"),
() = started.notified() => (),
() = tokio::time::sleep(Duration::from_secs(3)) => panic!("slow stream did not start"),
}
let fast = tokio::time::timeout(Duration::from_secs(3), sender.send_request(message("/fast"))).await.unwrap().unwrap();
assert_eq!(fast.into_body().collect().await.unwrap().to_bytes(), b"/fast"[..]);
listener.close().unwrap();
release.notify_one();
let slow = slow.await.unwrap();
assert_eq!(slow.into_body().collect().await.unwrap().to_bytes(), b"/slow"[..]);
drop(sender);
tokio::time::timeout(Duration::from_secs(3), server.wait()).await.unwrap().unwrap().unwrap();
driver.stop().await.unwrap();
}).unwrap();
}
}
#[test]
fn http2_limits_reject_invalid_windows_and_total_stream_capacity() {
runtime()
.run(async |_| {
for http2 in [
http::Http2Limits {
streams: 2049,
..http::Http2Limits::default()
},
http::Http2Limits {
streams: 0,
..http::Http2Limits::default()
},
http::Http2Limits {
stream_window_bytes: 0,
..http::Http2Limits::default()
},
http::Http2Limits {
connection_window_bytes: 65534,
..http::Http2Limits::default()
},
] {
let limits = Limits {
http2: Some(http2),
..Limits::default()
};
assert!(
http::send("127.0.0.1", 1, request("/", b""), &limits)
.await
.unwrap_err()
.contains("HTTP/2 limits")
);
}
})
.unwrap();
}