Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,311 @@
|
||||
use super::*;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use tokio::sync::Notify;
|
||||
|
||||
struct Running(Arc<AtomicUsize>);
|
||||
|
||||
impl Drop for Running {
|
||||
fn drop(&mut self) {
|
||||
self.0.fetch_sub(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timeout_and_stop_drain_handler_descendants() {
|
||||
runtime()
|
||||
.run(async |scope| {
|
||||
for cancel in [false, true] {
|
||||
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
||||
let port = listener.port().unwrap();
|
||||
let incoming = listener.clone();
|
||||
let running = Arc::new(AtomicUsize::new(0));
|
||||
let live = running.clone();
|
||||
let started = Arc::new(Notify::new());
|
||||
let ready = started.clone();
|
||||
let server = scope
|
||||
.spawn(move |child| async move {
|
||||
let limits = Limits {
|
||||
timeout_ms: if cancel { 5000 } else { 100 },
|
||||
..Limits::default()
|
||||
};
|
||||
http::serve::<_, _, true>(
|
||||
&incoming,
|
||||
&limits,
|
||||
&child,
|
||||
move |request, scope| {
|
||||
let live = live.clone();
|
||||
let ready = ready.clone();
|
||||
async move {
|
||||
let pending = scope
|
||||
.spawn_leaf(move || async move {
|
||||
live.fetch_add(1, Ordering::SeqCst);
|
||||
let _running = Running(live);
|
||||
ready.notify_one();
|
||||
std::future::pending::<()>().await;
|
||||
})
|
||||
.unwrap();
|
||||
pending.wait().await.unwrap();
|
||||
route(request, scope).await
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
})
|
||||
.unwrap();
|
||||
let client = scope
|
||||
.spawn_leaf(move || async move {
|
||||
http::send("127.0.0.1", port, request("/", b""), &Limits::default()).await
|
||||
})
|
||||
.unwrap();
|
||||
tokio::time::timeout(Duration::from_secs(3), started.notified())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(running.load(Ordering::SeqCst), 1);
|
||||
if cancel {
|
||||
server.stop().await.unwrap();
|
||||
assert!(client.wait().await.unwrap().is_err());
|
||||
listener.close().unwrap();
|
||||
} else {
|
||||
assert_eq!(client.wait().await.unwrap().unwrap().status, 504);
|
||||
listener.close().unwrap();
|
||||
server.wait().await.unwrap().unwrap();
|
||||
}
|
||||
assert_eq!(running.load(Ordering::SeqCst), 0);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closing_listener_drains_an_inflight_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 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::default(),
|
||||
&child,
|
||||
move |request, scope| {
|
||||
let ready = ready.clone();
|
||||
let released = released.clone();
|
||||
async move {
|
||||
ready.notify_one();
|
||||
released.notified().await;
|
||||
route(request, scope).await
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
})
|
||||
.unwrap();
|
||||
let client = scope
|
||||
.spawn_leaf(move || async move {
|
||||
http::send(
|
||||
"127.0.0.1",
|
||||
port,
|
||||
request("/", b"drained"),
|
||||
&Limits::default(),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.unwrap();
|
||||
tokio::time::timeout(Duration::from_secs(3), started.notified())
|
||||
.await
|
||||
.unwrap();
|
||||
listener.close().unwrap();
|
||||
release.notify_one();
|
||||
assert_eq!(
|
||||
client.wait().await.unwrap().unwrap().body.as_ref(),
|
||||
b"drained"
|
||||
);
|
||||
tokio::time::timeout(Duration::from_secs(3), server.wait())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_deadline_drops_its_connection_driver() {
|
||||
runtime()
|
||||
.run(async |scope| {
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let port = i64::from(listener.local_addr().unwrap().port());
|
||||
let peer = scope
|
||||
.spawn(move |child| async move {
|
||||
let (stream, _) = listener.accept().await.unwrap();
|
||||
let mut stream = stream.into_std().unwrap();
|
||||
stream.set_nonblocking(false).unwrap();
|
||||
stream
|
||||
.set_read_timeout(Some(Duration::from_secs(3)))
|
||||
.unwrap();
|
||||
child
|
||||
.blocking(move || {
|
||||
let mut data = Vec::new();
|
||||
stream.read_to_end(&mut data).unwrap();
|
||||
assert!(data.starts_with(b"POST / HTTP/1.1"));
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
let limits = Limits {
|
||||
timeout_ms: 100,
|
||||
..Limits::default()
|
||||
};
|
||||
let error = http::send("127.0.0.1", port, request("/", b""), &limits)
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert_eq!(error, "HTTP request timed out");
|
||||
peer.wait().await.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_service_configuration_keeps_listener_available() {
|
||||
runtime()
|
||||
.run(async |scope| {
|
||||
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
||||
let limits = Limits {
|
||||
connections: 0,
|
||||
..Limits::default()
|
||||
};
|
||||
assert_eq!(
|
||||
http::serve::<_, _, false>(&listener, &limits, scope, route).await,
|
||||
Err(http::ServeError::Limits)
|
||||
);
|
||||
assert!(listener.port().unwrap() > 0);
|
||||
listener.close().unwrap();
|
||||
assert_eq!(
|
||||
http::serve::<_, _, false>(&listener, &Limits::default(), scope, route).await,
|
||||
Err(http::ServeError::Listener)
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exhausted_task_pool_preserves_connections_and_resumes_accepting() {
|
||||
capacity_recovery(CapacityAction::Resume);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closing_listener_interrupts_waiting_for_task_capacity() {
|
||||
capacity_recovery(CapacityAction::Close);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancelling_server_interrupts_waiting_for_task_capacity() {
|
||||
capacity_recovery(CapacityAction::Cancel);
|
||||
}
|
||||
|
||||
enum CapacityAction {
|
||||
Resume,
|
||||
Close,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
fn capacity_recovery(action: CapacityAction) {
|
||||
use http_body_util::{BodyExt, Empty};
|
||||
use hyper_util::rt::TokioIo;
|
||||
|
||||
Runtime::new(dever_runtime::task::Limits {
|
||||
threads: 2,
|
||||
workers: 1,
|
||||
tasks: 3,
|
||||
})
|
||||
.unwrap()
|
||||
.run(async |scope| {
|
||||
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
||||
let port = listener.port().unwrap() as u16;
|
||||
let incoming = listener.clone();
|
||||
let server = scope
|
||||
.spawn(move |child| async move {
|
||||
http::serve::<_, _, false>(&incoming, &Limits::default(), &child, route).await
|
||||
})
|
||||
.unwrap();
|
||||
let mut clients = Vec::new();
|
||||
let mut drivers = Vec::new();
|
||||
for _ in 0..3 {
|
||||
let socket = tokio::net::TcpStream::connect(("127.0.0.1", port))
|
||||
.await
|
||||
.unwrap();
|
||||
let (sender, connection) = hyper::client::conn::http1::handshake(TokioIo::new(socket))
|
||||
.await
|
||||
.unwrap();
|
||||
clients.push(sender);
|
||||
drivers.push(tokio::spawn(connection));
|
||||
}
|
||||
let request = || {
|
||||
hyper::Request::builder()
|
||||
.uri("/echo")
|
||||
.header("host", "fixture.test")
|
||||
.body(Empty::<bytes::Bytes>::new())
|
||||
.unwrap()
|
||||
};
|
||||
for client in &mut clients[..2] {
|
||||
let response = client.send_request(request()).await.unwrap();
|
||||
assert_eq!(response.status(), 200);
|
||||
response.into_body().collect().await.unwrap();
|
||||
}
|
||||
let mut waiting = Box::pin(clients[2].send_request(request()));
|
||||
assert!(
|
||||
tokio::time::timeout(Duration::from_millis(100), &mut waiting)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
if matches!(action, CapacityAction::Resume) {
|
||||
let response = clients[0].send_request(request()).await.unwrap();
|
||||
assert_eq!(response.status(), 200);
|
||||
response.into_body().collect().await.unwrap();
|
||||
drivers[0].abort();
|
||||
let response = tokio::time::timeout(Duration::from_secs(3), &mut waiting)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), 200);
|
||||
response.into_body().collect().await.unwrap();
|
||||
}
|
||||
if matches!(action, CapacityAction::Cancel) {
|
||||
tokio::time::timeout(Duration::from_secs(3), server.stop())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
listener.close().unwrap();
|
||||
} else {
|
||||
listener.close().unwrap();
|
||||
tokio::time::timeout(Duration::from_secs(3), server.wait())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
drop(waiting);
|
||||
drop(clients);
|
||||
for driver in drivers {
|
||||
driver.abort();
|
||||
let _ = driver.await;
|
||||
}
|
||||
assert_eq!(
|
||||
scope
|
||||
.spawn_leaf(|| async { 42 })
|
||||
.unwrap()
|
||||
.wait()
|
||||
.await
|
||||
.unwrap(),
|
||||
42
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
Reference in new issue
Block a user