use super::*; use std::sync::atomic::{AtomicUsize, Ordering}; use tokio::sync::Notify; struct Running(Arc); 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::::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(); }