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

@@ -0,0 +1,239 @@
use super::*;
#[test]
fn headers_return_before_body_and_download_ignores_buffered_total_limit() {
let (opened, permit) = std::sync::mpsc::channel();
let (port, server) = peer(move |listener| {
let mut socket = accept(&listener);
headers(&mut socket);
socket
.get_mut()
.write_all(
b"HTTP/1.1 200 OK\r\nContent-Length: 100003\r\nX-Test: one\r\nX-Test: two\r\n\r\n",
)
.unwrap();
permit.recv_timeout(Duration::from_secs(2)).unwrap();
socket.get_mut().write_all(&vec![b'x'; 100003]).unwrap();
assert_eq!(socket.read(&mut [0]).unwrap(), 0);
});
runtime()
.run(async |scope| {
let client = client(
scope,
port,
&Limits {
body_bytes: 1,
..Default::default()
},
&PoolLimits {
chunk_bytes: 7,
..Default::default()
},
);
let response = client.open(message(), event).await.unwrap();
assert_eq!(
response
.headers
.iter()
.filter(|header| header.name == "x-test")
.count(),
2
);
opened.send(()).unwrap();
let body = collect(&response.body, 7).await;
assert_eq!(body, vec![b'x'; 100003]);
assert!(response.body.next().await.unwrap().is_none());
client.close().await.unwrap();
})
.unwrap();
server.join().unwrap();
}
#[test]
fn unread_body_holds_slot_and_idle_clock_starts_after_eof() {
let (port, server) = peer(|listener| {
let mut socket = accept(&listener);
for _ in 0..2 {
headers(&mut socket);
socket
.get_mut()
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nabcdef")
.unwrap();
}
let start = std::time::Instant::now();
assert_eq!(socket.read(&mut [0]).unwrap(), 0);
assert!(start.elapsed() < Duration::from_secs(1));
});
runtime()
.run(async |scope| {
let client = client(
scope,
port,
&Limits {
connections: 1,
timeout_ms: 80,
..Default::default()
},
&PoolLimits {
idle_ms: 20,
chunk_bytes: 2,
..Default::default()
},
);
let response = client.open(message(), event).await.unwrap();
assert!(
client
.open(message(), event)
.await
.unwrap_err()
.contains("timed out")
);
assert_eq!(collect(&response.body, 2).await, b"abcdef");
assert_eq!(
client.request(message()).await.unwrap().body.as_ref(),
b"abcdef"
);
tokio::time::sleep(Duration::from_millis(80)).await;
client.close().await.unwrap();
})
.unwrap();
server.join().unwrap();
}
#[test]
fn early_close_of_buffered_frame_closes_http1_and_next_request_reconnects() {
let (port, server) = peer(|listener| {
let mut first = accept(&listener);
headers(&mut first);
first
.get_mut()
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nabcdef")
.unwrap();
assert_eq!(first.read(&mut [0]).unwrap(), 0);
let mut second = accept(&listener);
headers(&mut second);
second
.get_mut()
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok")
.unwrap();
});
runtime()
.run(async |scope| {
let client = client(
scope,
port,
&Limits {
connections: 1,
..Default::default()
},
&PoolLimits::default(),
);
let response = client.open(message(), event).await.unwrap();
let alias = response.body.clone();
tokio::time::sleep(Duration::from_millis(30)).await;
response.body.close();
assert!(alias.next().await.unwrap().is_none());
assert_eq!(
client.request(message()).await.unwrap().body.as_ref(),
b"ok"
);
client.close().await.unwrap();
})
.unwrap();
server.join().unwrap();
}
#[test]
fn read_timeout_and_protocol_failure_emit_one_error_then_eof() {
for body in [
b"".as_slice(),
b"3\r\nabc\r\n0\r\nX-End: bad\r\n\r\n",
b"invalid\r\n",
] {
let (port, server) = peer(move |listener| {
let mut socket = accept(&listener);
headers(&mut socket);
socket
.get_mut()
.write_all(b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n")
.unwrap();
socket.get_mut().write_all(body).unwrap();
let _ = socket.read(&mut [0]);
});
runtime()
.run(async |scope| {
let client = client(
scope,
port,
&Limits::default(),
&PoolLimits {
read_ms: 40,
..Default::default()
},
);
let response = client.open(message(), event).await.unwrap();
let mut failures = 0;
while let Some(item) = response.body.next().await.unwrap() {
failures += usize::from(item.is_err());
}
assert_eq!(failures, 1);
assert!(response.body.next().await.unwrap().is_none());
client.close().await.unwrap();
})
.unwrap();
server.join().unwrap();
}
}
#[test]
fn cancelling_consumer_closes_body_and_pool_close_wakes_body_and_slot_waiters() {
for cancel in [true, false] {
let (port, server) = peer(|listener| {
let mut socket = accept(&listener);
headers(&mut socket);
socket
.get_mut()
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 20\r\n\r\n")
.unwrap();
assert_eq!(socket.read(&mut [0]).unwrap(), 0);
});
runtime()
.run(async |scope| {
let client = client(
scope,
port,
&Limits {
connections: 1,
..Default::default()
},
&PoolLimits::default(),
);
let response = client.open(message(), event).await.unwrap();
let alias = response.body.clone();
if cancel {
let consume = async {
let _owner = alias.close_on_drop();
alias.next().await
};
assert!(
tokio::time::timeout(Duration::from_millis(20), consume)
.await
.is_err()
);
assert!(response.body.next().await.unwrap().is_none());
} else {
let (item, waiting, ()) =
tokio::join!(response.body.next(), client.open(message(), event), async {
tokio::time::sleep(Duration::from_millis(20)).await;
client.close().await.unwrap();
});
assert!(item.unwrap().unwrap().unwrap_err().contains("closed"));
assert!(waiting.unwrap_err().contains("closed"));
assert!(response.body.next().await.unwrap().is_none());
}
client.close().await.unwrap();
})
.unwrap();
server.join().unwrap();
}
}
+95
View File
@@ -0,0 +1,95 @@
//! 独立回环协议对端验证客户端按需拉取与连接所有权。
use super::*;
use bytes::Bytes;
use dever_runtime::{
async_stream::AsyncStream,
http::{HttpClient, PoolLimits, StreamRequest},
io::Error,
tls,
};
use std::net::TcpListener;
mod download;
mod protocol;
mod upload;
type Event = Result<Bytes, Error>;
fn event(value: Event) -> Event {
value
}
fn message() -> Request {
Request {
method: "POST".into(),
target: "/stream".into(),
headers: vec![],
body: bytes::Bytes::new(),
}
}
fn client(scope: &Scope<'_>, port: u16, limits: &Limits, pool: &PoolLimits) -> HttpClient {
http::client(
&format!("http://127.0.0.1:{port}"),
&tls::system(),
limits,
pool,
scope,
)
.unwrap()
}
fn peer(work: impl FnOnce(TcpListener) + Send + 'static) -> (u16, std::thread::JoinHandle<()>) {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.local_addr().unwrap().port();
(port, std::thread::spawn(move || work(listener)))
}
fn accept(listener: &TcpListener) -> BufReader<TcpStream> {
listener.set_nonblocking(true).unwrap();
let start = std::time::Instant::now();
let socket = loop {
match listener.accept() {
Ok((socket, _)) => break socket,
Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
assert!(
start.elapsed() < Duration::from_secs(5),
"peer accept timed out"
);
std::thread::sleep(Duration::from_millis(2));
}
Err(error) => panic!("{error}"),
}
};
socket
.set_read_timeout(Some(Duration::from_secs(5)))
.unwrap();
socket
.set_write_timeout(Some(Duration::from_secs(5)))
.unwrap();
BufReader::new(socket)
}
fn headers(socket: &mut BufReader<TcpStream>) -> String {
let mut headers = String::new();
loop {
let mut line = String::new();
assert!(socket.read_line(&mut line).unwrap() > 0);
if line == "\r\n" {
return headers;
}
headers.push_str(&line);
}
}
async fn collect(body: &AsyncStream<Event>, chunk: usize) -> Vec<u8> {
let _guard = body.close_on_drop();
let mut bytes = vec![];
while let Some(event) = body.next().await.unwrap() {
let part = event.unwrap();
assert!(!part.is_empty() && part.len() <= chunk);
bytes.extend_from_slice(&part);
}
bytes
}
@@ -0,0 +1,173 @@
use super::*;
#[test]
fn http1_http2_tls_streams_roundtrip_and_buffered_limits_remain_enforced() {
for http2 in [false, true] {
for secure in [false, true] {
runtime()
.run(async |scope| {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let port = listener.port().unwrap();
let limits = Limits {
connections: 1,
http2: http2.then(Default::default),
..Default::default()
};
let incoming = listener.clone();
let protocol = limits.clone();
let server = scope
.spawn(move |child| async move {
if secure {
let tls = tls::server(
super::super::tls::CERTIFICATE,
super::super::tls::KEY,
)
.unwrap();
http::serve_tls::<_, _, false>(
&incoming, &protocol, &child, &tls, route,
)
.await
} else {
http::serve::<_, _, false>(&incoming, &protocol, &child, route)
.await
}
})
.unwrap();
let origin = format!(
"{}://localhost:{port}",
if secure { "https" } else { "http" }
);
let trust = tls::client(super::super::tls::ROOT).unwrap();
let client = http::client(
&origin,
&trust,
&Limits {
body_bytes: 1,
..limits
},
&PoolLimits {
chunk_bytes: 3,
..Default::default()
},
scope,
)
.unwrap();
let body = AsyncStream::values([
Ok(Bytes::from_static(b"abc")),
Ok(Bytes::from_static(b"def")),
]);
let request = StreamRequest {
method: "POST".into(),
target: "/echo".into(),
headers: vec![],
body,
};
let response = client.open_stream(request, event, event).await.unwrap();
assert_eq!(collect(&response.body, 3).await, b"abcdef");
let mut request = message();
request.body = b"ab".to_vec().into();
assert!(
client
.open(request.clone(), event)
.await
.unwrap_err()
.contains("byte limit")
);
assert!(
client
.request(request)
.await
.unwrap_err()
.contains("byte limit")
);
client.close().await.unwrap();
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
}
}
#[test]
fn http2_closing_one_response_preserves_neighbor_and_releases_only_its_slot() {
runtime()
.run(async |scope| {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let port = listener.port().unwrap();
let limits = Limits {
connections: 1,
http2: Some(http::Http2Limits {
streams: 2,
..Default::default()
}),
..Default::default()
};
let incoming = listener.clone();
let protocol = limits.clone();
let server = scope
.spawn(move |child| async move {
http::serve_live(
&incoming,
&protocol,
&http::LiveLimits::default(),
&child,
|request: Request, reply: http::HttpReply, _| async move {
if request.target == "/ordinary" {
return reply
.respond(Response {
status: 200,
headers: vec![],
body: b"ok".to_vec().into(),
})
.await
.map_err(|error| error.to_string());
}
reply
.start(200, vec![])
.await
.map_err(|error| error.to_string())?;
reply
.write(b"first")
.await
.map_err(|error| error.to_string())?;
tokio::time::sleep(Duration::from_millis(150)).await;
reply
.write(b"last")
.await
.map_err(|error| error.to_string())
},
)
.await
})
.unwrap();
let client = client(
scope,
port as u16,
&limits,
&PoolLimits {
idle_ms: 20,
..Default::default()
},
);
let first = client.open(message(), event).await.unwrap();
let second = client.open(message(), event).await.unwrap();
assert_eq!(
first.body.next().await.unwrap().unwrap().unwrap(),
b"first"[..]
);
assert_eq!(
second.body.next().await.unwrap().unwrap().unwrap(),
b"first"[..]
);
first.body.close();
let mut ordinary = message();
ordinary.target = "/ordinary".into();
assert_eq!(client.request(ordinary).await.unwrap().body.as_ref(), b"ok");
assert_eq!(collect(&second.body, 65536).await, b"last");
client.close().await.unwrap();
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
+163
View File
@@ -0,0 +1,163 @@
use super::*;
fn chunk(socket: &mut BufReader<TcpStream>) -> Vec<u8> {
let mut line = String::new();
assert!(socket.read_line(&mut line).unwrap() > 0);
let count = usize::from_str_radix(line.trim(), 16).unwrap();
let mut body = vec![0; count];
socket.read_exact(&mut body).unwrap();
let mut ending = [0; 2];
socket.read_exact(&mut ending).unwrap();
assert_eq!(&ending, b"\r\n");
body
}
fn request(body: AsyncStream<Event>) -> StreamRequest<Event> {
StreamRequest {
method: "POST".into(),
target: "/stream".into(),
headers: vec![],
body,
}
}
#[test]
fn duplex_upload_sends_first_chunk_before_source_eof_and_returns_response_early() {
let (port, server) = peer(|listener| {
let mut socket = accept(&listener);
let head = headers(&mut socket).to_ascii_lowercase();
assert!(head.contains("transfer-encoding: chunked"));
assert!(!head.contains("content-length:"));
assert_eq!(chunk(&mut socket), b"abc");
socket
.get_mut()
.write_all(b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nabc\r\n")
.unwrap();
assert_eq!(chunk(&mut socket), b"def");
assert!(chunk(&mut socket).is_empty());
socket
.get_mut()
.write_all(b"3\r\ndef\r\n0\r\n\r\n")
.unwrap();
assert_eq!(socket.read(&mut [0]).unwrap(), 0);
});
runtime()
.run(async |scope| {
let client = client(
scope,
port,
&Limits {
body_bytes: 1,
..Default::default()
},
&PoolLimits {
chunk_bytes: 3,
..Default::default()
},
);
let proceed = Arc::new(tokio::sync::Notify::new());
let source = proceed.clone();
let body = AsyncStream::new(futures_util::stream::unfold(0, move |index| {
let source = source.clone();
async move {
match index {
0 => Some((Ok(Ok(Bytes::from_static(b"abc"))), 1)),
1 => {
source.notified().await;
Some((Ok(Ok(Bytes::from_static(b"def"))), 2))
}
_ => None,
}
}
}));
let alias = body.clone();
let response = client
.open_stream(request(body), event, event)
.await
.unwrap();
assert_eq!(
response.body.next().await.unwrap().unwrap().unwrap(),
b"abc"[..]
);
proceed.notify_one();
assert_eq!(collect(&response.body, 3).await, b"def");
assert!(alias.next().await.unwrap().is_none());
client.close().await.unwrap();
})
.unwrap();
server.join().unwrap();
}
#[test]
fn upload_rejects_oversized_chunks_source_errors_and_stalled_reads() {
for mode in 0..3 {
let (port, server) = peer(|listener| {
let mut socket = accept(&listener);
let mut request = Vec::new();
socket.read_to_end(&mut request).unwrap();
assert!(!request.windows(8).any(|window| window == b"oversize"));
});
runtime()
.run(async |scope| {
let client = client(
scope,
port,
&Limits {
timeout_ms: 1000,
..Default::default()
},
&PoolLimits {
chunk_bytes: 3,
read_ms: 40,
..Default::default()
},
);
let body = match mode {
0 => AsyncStream::values([Ok(Bytes::from_static(b"oversize"))]),
1 => AsyncStream::values([Err("source failed".into())]),
_ => AsyncStream::new(futures_util::stream::pending()),
};
let alias = body.clone();
assert!(
client
.open_stream(request(body), event, event)
.await
.is_err()
);
assert!(alias.next().await.unwrap().is_none());
client.close().await.unwrap();
})
.unwrap();
server.join().unwrap();
}
}
#[test]
fn cancelling_request_before_headers_closes_upload_source_and_socket() {
let (port, server) = peer(|listener| {
let mut socket = accept(&listener);
let mut bytes = vec![];
socket.read_to_end(&mut bytes).unwrap();
});
runtime()
.run(async |scope| {
let client = client(scope, port, &Limits::default(), &PoolLimits::default());
let body = AsyncStream::<Event>::new(futures_util::stream::pending());
assert!(
tokio::time::timeout(
Duration::from_millis(40),
client.open_stream(request(body.clone()), event, event)
)
.await
.is_err()
);
tokio::time::timeout(Duration::from_secs(1), body.next())
.await
.unwrap()
.unwrap();
assert!(body.next().await.unwrap().is_none());
client.close().await.unwrap();
})
.unwrap();
server.join().unwrap();
}