From 858d4a709ff4d1d24dee67ecd3253b3390d3d55f Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Fri, 6 Nov 2020 16:39:31 -0500 Subject: [PATCH 1/4] First pass at allowing a graceful shutdown --- .vscode/launch.json | 942 ++++++++++++++++++++++++++++ Cargo.toml | 1 + src/cancelation_token.rs | 62 ++ src/lib.rs | 2 + src/listener/concurrent_listener.rs | 7 +- src/listener/failover_listener.rs | 7 +- src/listener/mod.rs | 4 +- src/listener/parsed_listener.rs | 8 +- src/listener/tcp_listener.rs | 46 +- src/listener/unix_listener.rs | 4 +- src/server.rs | 9 +- tests/server.rs | 35 +- 12 files changed, 1080 insertions(+), 47 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/cancelation_token.rs diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..2f68fdb90 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,942 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'tide'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=tide" + ], + "filter": { + "name": "tide", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'cookies'", + "cargo": { + "args": [ + "build", + "--example=cookies", + "--package=tide" + ], + "filter": { + "name": "cookies", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'cookies'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=cookies", + "--package=tide" + ], + "filter": { + "name": "cookies", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'sessions'", + "cargo": { + "args": [ + "build", + "--example=sessions", + "--package=tide" + ], + "filter": { + "name": "sessions", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'sessions'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=sessions", + "--package=tide" + ], + "filter": { + "name": "sessions", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'chunked'", + "cargo": { + "args": [ + "build", + "--example=chunked", + "--package=tide" + ], + "filter": { + "name": "chunked", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'chunked'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=chunked", + "--package=tide" + ], + "filter": { + "name": "chunked", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'sse'", + "cargo": { + "args": [ + "build", + "--example=sse", + "--package=tide" + ], + "filter": { + "name": "sse", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'sse'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=sse", + "--package=tide" + ], + "filter": { + "name": "sse", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'fib'", + "cargo": { + "args": [ + "build", + "--example=fib", + "--package=tide" + ], + "filter": { + "name": "fib", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'fib'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=fib", + "--package=tide" + ], + "filter": { + "name": "fib", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'redirect'", + "cargo": { + "args": [ + "build", + "--example=redirect", + "--package=tide" + ], + "filter": { + "name": "redirect", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'redirect'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=redirect", + "--package=tide" + ], + "filter": { + "name": "redirect", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'upload'", + "cargo": { + "args": [ + "build", + "--example=upload", + "--package=tide" + ], + "filter": { + "name": "upload", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'upload'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=upload", + "--package=tide" + ], + "filter": { + "name": "upload", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'hello'", + "cargo": { + "args": [ + "build", + "--example=hello", + "--package=tide" + ], + "filter": { + "name": "hello", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'hello'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=hello", + "--package=tide" + ], + "filter": { + "name": "hello", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'graphql'", + "cargo": { + "args": [ + "build", + "--example=graphql", + "--package=tide" + ], + "filter": { + "name": "graphql", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'graphql'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=graphql", + "--package=tide" + ], + "filter": { + "name": "graphql", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'json'", + "cargo": { + "args": [ + "build", + "--example=json", + "--package=tide" + ], + "filter": { + "name": "json", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'json'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=json", + "--package=tide" + ], + "filter": { + "name": "json", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'nested'", + "cargo": { + "args": [ + "build", + "--example=nested", + "--package=tide" + ], + "filter": { + "name": "nested", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'nested'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=nested", + "--package=tide" + ], + "filter": { + "name": "nested", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'concurrent_listeners'", + "cargo": { + "args": [ + "build", + "--example=concurrent_listeners", + "--package=tide" + ], + "filter": { + "name": "concurrent_listeners", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'concurrent_listeners'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=concurrent_listeners", + "--package=tide" + ], + "filter": { + "name": "concurrent_listeners", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'catflap'", + "cargo": { + "args": [ + "build", + "--example=catflap", + "--package=tide" + ], + "filter": { + "name": "catflap", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'catflap'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=catflap", + "--package=tide" + ], + "filter": { + "name": "catflap", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'middleware'", + "cargo": { + "args": [ + "build", + "--example=middleware", + "--package=tide" + ], + "filter": { + "name": "middleware", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'middleware'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=middleware", + "--package=tide" + ], + "filter": { + "name": "middleware", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'static_file'", + "cargo": { + "args": [ + "build", + "--example=static_file", + "--package=tide" + ], + "filter": { + "name": "static_file", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'static_file'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=static_file", + "--package=tide" + ], + "filter": { + "name": "static_file", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'error_handling'", + "cargo": { + "args": [ + "build", + "--example=error_handling", + "--package=tide" + ], + "filter": { + "name": "error_handling", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in example 'error_handling'", + "cargo": { + "args": [ + "test", + "--no-run", + "--example=error_handling", + "--package=tide" + ], + "filter": { + "name": "error_handling", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'cookies'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=cookies", + "--package=tide" + ], + "filter": { + "name": "cookies", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'nested'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=nested", + "--package=tide" + ], + "filter": { + "name": "nested", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'sessions'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=sessions", + "--package=tide" + ], + "filter": { + "name": "sessions", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'response'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=response", + "--package=tide" + ], + "filter": { + "name": "response", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'chunked-encode-large'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=chunked-encode-large", + "--package=tide" + ], + "filter": { + "name": "chunked-encode-large", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'chunked-encode-small'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=chunked-encode-small", + "--package=tide" + ], + "filter": { + "name": "chunked-encode-small", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'unix'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=unix", + "--package=tide" + ], + "filter": { + "name": "unix", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'log'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=log", + "--package=tide" + ], + "filter": { + "name": "log", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'server'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=server", + "--package=tide" + ], + "filter": { + "name": "server", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'params'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=params", + "--package=tide" + ], + "filter": { + "name": "params", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'test_utils'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=test_utils", + "--package=tide" + ], + "filter": { + "name": "test_utils", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'route_middleware'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=route_middleware", + "--package=tide" + ], + "filter": { + "name": "route_middleware", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'function_middleware'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=function_middleware", + "--package=tide" + ], + "filter": { + "name": "function_middleware", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'endpoint'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=endpoint", + "--package=tide" + ], + "filter": { + "name": "endpoint", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'serve_dir'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=serve_dir", + "--package=tide" + ], + "filter": { + "name": "serve_dir", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug integration test 'wildcard'", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=wildcard", + "--package=tide" + ], + "filter": { + "name": "wildcard", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug benchmark 'router'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bench=router", + "--package=tide" + ], + "filter": { + "name": "router", + "kind": "bench" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 7e34b7193..30127d8ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ async-sse = "4.0.1" async-std = { version = "1.6.5", features = ["unstable"] } async-trait = "0.1.41" femme = { version = "2.1.1", optional = true } +futures = "0.3.7" futures-util = "0.3.6" http-client = { version = "6.1.0", default-features = false } http-types = "2.5.0" diff --git a/src/cancelation_token.rs b/src/cancelation_token.rs new file mode 100644 index 000000000..47bd46f6f --- /dev/null +++ b/src/cancelation_token.rs @@ -0,0 +1,62 @@ +use { + std::{ + future::Future, + pin::Pin, + sync::{Arc, Mutex}, + task::{Context, Poll, Waker}, + }, +}; + +#[derive(Debug)] +pub struct CancelationToken { + shared_state: Arc> +} + +#[derive(Debug)] +struct CancelationTokenState { + canceled: bool, + waker: Option +} + +impl CancelationToken { + pub fn new() -> CancelationToken { + CancelationToken { + shared_state: Arc::new(Mutex::new(CancelationTokenState { + canceled: false, + waker: None + })) + } + } + + pub fn complete(&self) { + let mut shared_state = self.shared_state.lock().unwrap(); + + shared_state.canceled = true; + if let Some(waker) = shared_state.waker.take() { + waker.wake() + } + } +} + +impl Future for CancelationToken { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut shared_state = self.shared_state.lock().unwrap(); + + if shared_state.canceled { + Poll::Ready(()) + } else { + shared_state.waker = Some(cx.waker().clone()); + Poll::Pending + } + } +} + +impl Clone for CancelationToken { + fn clone(&self) -> Self { + CancelationToken { + shared_state: self.shared_state.clone() + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 20a113962..de26cc85d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,6 +60,7 @@ #![doc(html_favicon_url = "https://yoshuawuyts.com/assets/http-rs/favicon.ico")] #![doc(html_logo_url = "https://yoshuawuyts.com/assets/http-rs/logo-rounded.png")] +mod cancelation_token; #[cfg(feature = "cookies")] mod cookies; mod endpoint; @@ -88,6 +89,7 @@ pub mod utils; #[cfg(feature = "sessions")] pub mod sessions; +pub use cancelation_token::CancelationToken; pub use endpoint::Endpoint; pub use middleware::{Middleware, Next}; pub use redirect::Redirect; diff --git a/src/listener/concurrent_listener.rs b/src/listener/concurrent_listener.rs index 1cff9b0d7..28fbf7524 100644 --- a/src/listener/concurrent_listener.rs +++ b/src/listener/concurrent_listener.rs @@ -1,5 +1,5 @@ use crate::listener::{Listener, ToListener}; -use crate::Server; +use crate::{CancelationToken, Server}; use std::fmt::{self, Debug, Display, Formatter}; @@ -79,12 +79,13 @@ impl ConcurrentListener { #[async_trait::async_trait] impl Listener for ConcurrentListener { - async fn listen(&mut self, app: Server) -> io::Result<()> { + async fn listen(&mut self, app: Server, cancelation_token: CancelationToken) -> io::Result<()> { let mut futures_unordered = FuturesUnordered::new(); for listener in self.0.iter_mut() { let app = app.clone(); - futures_unordered.push(listener.listen(app)); + // TODO: Track the cancelation tokens + futures_unordered.push(listener.listen(app, cancelation_token.clone())); } while let Some(result) = futures_unordered.next().await { diff --git a/src/listener/failover_listener.rs b/src/listener/failover_listener.rs index 4ab1bd242..863c56711 100644 --- a/src/listener/failover_listener.rs +++ b/src/listener/failover_listener.rs @@ -1,5 +1,5 @@ use crate::listener::{Listener, ToListener}; -use crate::Server; +use crate::{CancelationToken, Server}; use std::fmt::{self, Debug, Display, Formatter}; @@ -81,10 +81,11 @@ impl FailoverListener { #[async_trait::async_trait] impl Listener for FailoverListener { - async fn listen(&mut self, app: Server) -> io::Result<()> { + async fn listen(&mut self, app: Server, cancelation_token: CancelationToken) -> io::Result<()> { for listener in self.0.iter_mut() { let app = app.clone(); - match listener.listen(app).await { + // TODO: Track the cancelation tokens + match listener.listen(app, cancelation_token.clone()).await { Ok(_) => return Ok(()), Err(e) => { crate::log::info!("unable to listen", { diff --git a/src/listener/mod.rs b/src/listener/mod.rs index 82033de15..27715f9dd 100644 --- a/src/listener/mod.rs +++ b/src/listener/mod.rs @@ -12,7 +12,7 @@ mod to_listener_impls; #[cfg(all(unix, feature = "h1-server"))] mod unix_listener; -use crate::Server; +use crate::{CancelationToken, Server}; use async_std::io; pub use concurrent_listener::ConcurrentListener; @@ -37,7 +37,7 @@ pub trait Listener: /// This is the primary entrypoint for the Listener trait. listen /// is called exactly once, and is expected to spawn tasks for /// each incoming connection. - async fn listen(&mut self, app: Server) -> io::Result<()>; + async fn listen(&mut self, app: Server, cancelation_token: CancelationToken) -> io::Result<()>; } /// crate-internal shared logic used by tcp and unix listeners to diff --git a/src/listener/parsed_listener.rs b/src/listener/parsed_listener.rs index 4b0e186a9..6a48938c6 100644 --- a/src/listener/parsed_listener.rs +++ b/src/listener/parsed_listener.rs @@ -1,7 +1,7 @@ #[cfg(unix)] use super::UnixListener; use super::{Listener, TcpListener}; -use crate::Server; +use crate::{CancelationToken, Server}; use async_std::io; use std::fmt::{self, Display, Formatter}; @@ -32,11 +32,11 @@ impl Display for ParsedListener { #[async_trait::async_trait] impl Listener for ParsedListener { - async fn listen(&mut self, app: Server) -> io::Result<()> { + async fn listen(&mut self, app: Server, cancelation_token: CancelationToken) -> io::Result<()> { match self { #[cfg(unix)] - Self::Unix(u) => u.listen(app).await, - Self::Tcp(t) => t.listen(app).await, + Self::Unix(u) => u.listen(app, cancelation_token).await, + Self::Tcp(t) => t.listen(app, cancelation_token).await, } } } diff --git a/src/listener/tcp_listener.rs b/src/listener/tcp_listener.rs index db68530ee..731a6c6f5 100644 --- a/src/listener/tcp_listener.rs +++ b/src/listener/tcp_listener.rs @@ -1,13 +1,16 @@ +extern crate futures; + use super::is_transient_error; use crate::listener::Listener; -use crate::{log, Server}; +use crate::{CancelationToken, log, Server}; use std::fmt::{self, Display, Formatter}; use async_std::net::{self, SocketAddr, TcpStream}; use async_std::prelude::*; use async_std::{io, task}; +use futures::future::{self, Either, Future, FutureExt}; /// This represents a tide [Listener](crate::listener::Listener) that /// wraps an [async_std::net::TcpListener]. It is implemented as an @@ -70,28 +73,43 @@ fn handle_tcp(app: Server, stream: #[async_trait::async_trait] impl Listener for TcpListener { - async fn listen(&mut self, app: Server) -> io::Result<()> { + async fn listen(&mut self, app: Server, cancelation_token: CancelationToken) -> io::Result<()> { self.connect().await?; let listener = self.listener()?; crate::log::info!("Server listening on {}", self); - let mut incoming = listener.incoming(); + /*let canceled_task = task::spawn(async { + cancelation_token.await; + let result: Option = None; + result + });*/ - while let Some(stream) = incoming.next().await { - match stream { - Err(ref e) if is_transient_error(e) => continue, - Err(error) => { - let delay = std::time::Duration::from_millis(500); - crate::log::error!("Error: {}. Pausing for {:?}.", error, delay); - task::sleep(delay).await; - continue; - } + let mut incoming = listener.incoming(); - Ok(stream) => { - handle_tcp(app.clone(), stream); + 'serve_loop: + while let Either::Left(result) = future::select(incoming.next(), cancelation_token.clone()).await { + match result.0 { + Some(stream) => { + match stream { + Err(ref e) if is_transient_error(e) => continue, + Err(error) => { + let delay = std::time::Duration::from_millis(500); + crate::log::error!("Error: {}. Pausing for {:?}.", error, delay); + task::sleep(delay).await; + continue; + } + + Ok(stream) => { + handle_tcp(app.clone(), stream); + } + }; + }, + None => { + break 'serve_loop; } }; } + Ok(()) } } diff --git a/src/listener/unix_listener.rs b/src/listener/unix_listener.rs index 72aff852d..01901206e 100644 --- a/src/listener/unix_listener.rs +++ b/src/listener/unix_listener.rs @@ -1,7 +1,7 @@ use super::is_transient_error; use crate::listener::Listener; -use crate::{log, Server}; +use crate::{CancelationToken, log, Server}; use std::fmt::{self, Display, Formatter}; @@ -83,7 +83,7 @@ fn handle_unix(app: Server, stream: #[async_trait::async_trait] impl Listener for UnixListener { - async fn listen(&mut self, app: Server) -> io::Result<()> { + async fn listen(&mut self, app: Server, cancelation_token: CancelationToken) -> io::Result<()> { self.connect().await?; crate::log::info!("Server listening on {}", self); let listener = self.listener()?; diff --git a/src/server.rs b/src/server.rs index 853d39e34..5f0147d64 100644 --- a/src/server.rs +++ b/src/server.rs @@ -9,7 +9,7 @@ use crate::listener::{Listener, ToListener}; use crate::log; use crate::middleware::{Middleware, Next}; use crate::router::{Router, Selection}; -use crate::{Endpoint, Request, Route}; +use crate::{CancelationToken, Endpoint, Request, Route}; /// An HTTP server. /// @@ -187,7 +187,12 @@ impl Server { /// Asynchronously serve the app with the supplied listener. For more details, see [Listener] and [ToListener] pub async fn listen>(self, listener: TL) -> io::Result<()> { - listener.to_listener()?.listen(self).await + self.listen_with_cancelation_token(listener, CancelationToken::new()).await + } + + /// Asynchronously serve the app with the supplied listener and canelation token. For more details, see [Listener] and [ToListener] + pub async fn listen_with_cancelation_token>(self, listener: TL, cancelation_token: CancelationToken) -> io::Result<()> { + listener.to_listener()?.listen(self, cancelation_token).await } /// Respond to a `Request` with a `Response`. diff --git a/tests/server.rs b/tests/server.rs index 070557451..7d143fa54 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -40,27 +40,28 @@ fn hello_world() -> tide::Result<()> { #[test] fn echo_server() -> tide::Result<()> { task::block_on(async { + + let cancelation_token = tide::CancelationToken::new(); + let port = test_utils::find_port().await; - let server = task::spawn(async move { - let mut app = tide::new(); - app.at("/").get(|req| async move { Ok(req) }); + let mut app = tide::new(); + app.at("/").get(|req| async move { Ok(req) }); - app.listen(("localhost", port)).await?; - Result::<(), http_types::Error>::Ok(()) - }); + let server = app.listen_with_cancelation_token(("localhost", port), cancelation_token.clone()); + let server = task::spawn(server); - let client = task::spawn(async move { - task::sleep(Duration::from_millis(100)).await; - let string = surf::get(format!("http://localhost:{}", port)) - .body(Body::from_string("chashu".to_string())) - .recv_string() - .await - .unwrap(); - assert_eq!(string, "chashu".to_string()); - Ok(()) - }); + task::sleep(Duration::from_millis(100)).await; + let string = surf::get(format!("http://localhost:{}", port)) + .body(Body::from_string("chashu".to_string())) + .recv_string() + .await + .unwrap(); + assert_eq!(string, "chashu".to_string()); - server.race(client).await + cancelation_token.complete(); + + server.await.expect("Server did not complete gracefully"); + Result::<(), http_types::Error>::Ok(()) }) } From c19a9101a1b5c7104841def577c531bd10fce762 Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Fri, 6 Nov 2020 22:54:44 -0500 Subject: [PATCH 2/4] Implemented canceling on all listeners, finished testing for tcp_listener --- src/listener/concurrent_listener.rs | 17 +++++- src/listener/failover_listener.rs | 17 +++++- src/listener/tcp_listener.rs | 10 +--- src/listener/unix_listener.rs | 33 +++++++---- tests/server.rs | 88 +++++++++++++++-------------- 5 files changed, 96 insertions(+), 69 deletions(-) diff --git a/src/listener/concurrent_listener.rs b/src/listener/concurrent_listener.rs index 28fbf7524..e1eb2e1c0 100644 --- a/src/listener/concurrent_listener.rs +++ b/src/listener/concurrent_listener.rs @@ -3,7 +3,7 @@ use crate::{CancelationToken, Server}; use std::fmt::{self, Debug, Display, Formatter}; -use async_std::io; +use async_std::{io, task}; use futures_util::stream::{futures_unordered::FuturesUnordered, StreamExt}; /// ConcurrentListener allows tide to listen on any number of transports @@ -82,15 +82,26 @@ impl Listener for ConcurrentListene async fn listen(&mut self, app: Server, cancelation_token: CancelationToken) -> io::Result<()> { let mut futures_unordered = FuturesUnordered::new(); + let mut cancelation_tokens = Vec::new(); + for listener in self.0.iter_mut() { let app = app.clone(); - // TODO: Track the cancelation tokens - futures_unordered.push(listener.listen(app, cancelation_token.clone())); + let sub_cancelation_token = CancelationToken::new(); + futures_unordered.push(listener.listen(app, sub_cancelation_token.clone())); + cancelation_tokens.push(sub_cancelation_token); } + task::spawn(async move { + cancelation_token.await; + for sub_cancelation_token in cancelation_tokens.iter_mut() { + sub_cancelation_token.complete(); + } + }); + while let Some(result) = futures_unordered.next().await { result?; } + Ok(()) } } diff --git a/src/listener/failover_listener.rs b/src/listener/failover_listener.rs index 863c56711..267e01ab3 100644 --- a/src/listener/failover_listener.rs +++ b/src/listener/failover_listener.rs @@ -3,7 +3,7 @@ use crate::{CancelationToken, Server}; use std::fmt::{self, Debug, Display, Formatter}; -use async_std::io; +use async_std::{io, task}; /// FailoverListener allows tide to attempt to listen in a sequential /// order to any number of ports/addresses. The first successful @@ -82,10 +82,13 @@ impl FailoverListener { #[async_trait::async_trait] impl Listener for FailoverListener { async fn listen(&mut self, app: Server, cancelation_token: CancelationToken) -> io::Result<()> { + + let mut cancelation_tokens = Vec::new(); + for listener in self.0.iter_mut() { let app = app.clone(); - // TODO: Track the cancelation tokens - match listener.listen(app, cancelation_token.clone()).await { + let sub_cancelation_token = CancelationToken::new(); + match listener.listen(app, sub_cancelation_token.clone()).await { Ok(_) => return Ok(()), Err(e) => { crate::log::info!("unable to listen", { @@ -94,8 +97,16 @@ impl Listener for FailoverListener< }); } } + cancelation_tokens.push(sub_cancelation_token); } + task::spawn(async move { + cancelation_token.await; + for sub_cancelation_token in cancelation_tokens.iter_mut() { + sub_cancelation_token.complete(); + } + }); + Err(io::Error::new( io::ErrorKind::AddrNotAvailable, "unable to bind to any supplied listener spec", diff --git a/src/listener/tcp_listener.rs b/src/listener/tcp_listener.rs index 731a6c6f5..3c792fe36 100644 --- a/src/listener/tcp_listener.rs +++ b/src/listener/tcp_listener.rs @@ -1,5 +1,3 @@ -extern crate futures; - use super::is_transient_error; use crate::listener::Listener; @@ -10,7 +8,7 @@ use std::fmt::{self, Display, Formatter}; use async_std::net::{self, SocketAddr, TcpStream}; use async_std::prelude::*; use async_std::{io, task}; -use futures::future::{self, Either, Future, FutureExt}; +use futures::future::{self, Either}; /// This represents a tide [Listener](crate::listener::Listener) that /// wraps an [async_std::net::TcpListener]. It is implemented as an @@ -78,12 +76,6 @@ impl Listener for TcpListener { let listener = self.listener()?; crate::log::info!("Server listening on {}", self); - /*let canceled_task = task::spawn(async { - cancelation_token.await; - let result: Option = None; - result - });*/ - let mut incoming = listener.incoming(); 'serve_loop: diff --git a/src/listener/unix_listener.rs b/src/listener/unix_listener.rs index 01901206e..33a612667 100644 --- a/src/listener/unix_listener.rs +++ b/src/listener/unix_listener.rs @@ -8,6 +8,7 @@ use std::fmt::{self, Display, Formatter}; use async_std::os::unix::net::{self, SocketAddr, UnixStream}; use async_std::prelude::*; use async_std::{io, path::PathBuf, task}; +use futures::future::{self, Either}; /// This represents a tide [Listener](crate::listener::Listener) that /// wraps an [async_std::os::unix::net::UnixListener]. It is implemented as an @@ -89,18 +90,26 @@ impl Listener for UnixListener { let listener = self.listener()?; let mut incoming = listener.incoming(); - while let Some(stream) = incoming.next().await { - match stream { - Err(ref e) if is_transient_error(e) => continue, - Err(error) => { - let delay = std::time::Duration::from_millis(500); - crate::log::error!("Error: {}. Pausing for {:?}.", error, delay); - task::sleep(delay).await; - continue; - } - - Ok(stream) => { - handle_unix(app.clone(), stream); + 'serve_loop: + while let Either::Left(result) = future::select(incoming.next(), cancelation_token.clone()).await { + match result.0 { + Some(stream) => { + match stream { + Err(ref e) if is_transient_error(e) => continue, + Err(error) => { + let delay = std::time::Duration::from_millis(500); + crate::log::error!("Error: {}. Pausing for {:?}.", error, delay); + task::sleep(delay).await; + continue; + } + + Ok(stream) => { + handle_unix(app.clone(), stream); + } + }; + }, + None => { + break 'serve_loop; } }; } diff --git a/tests/server.rs b/tests/server.rs index 7d143fa54..574fbe4aa 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -9,31 +9,34 @@ use tide::{Body, Request}; #[test] fn hello_world() -> tide::Result<()> { task::block_on(async { + + let cancelation_token = tide::CancelationToken::new(); + let port = test_utils::find_port().await; - let server = task::spawn(async move { - let mut app = tide::new(); - app.at("/").get(move |mut req: Request<()>| async move { - assert_eq!(req.body_string().await.unwrap(), "nori".to_string()); - assert!(req.local_addr().unwrap().contains(&port.to_string())); - assert!(req.peer_addr().is_some()); - Ok("says hello") - }); - app.listen(("localhost", port)).await?; - Result::<(), http_types::Error>::Ok(()) - }); - let client = task::spawn(async move { - task::sleep(Duration::from_millis(100)).await; - let string = surf::get(format!("http://localhost:{}", port)) - .body(Body::from_string("nori".to_string())) - .recv_string() - .await - .unwrap(); - assert_eq!(string, "says hello"); - Ok(()) + let mut app = tide::new(); + app.at("/").get(move |mut req: Request<()>| async move { + assert_eq!(req.body_string().await.unwrap(), "nori".to_string()); + assert!(req.local_addr().unwrap().contains(&port.to_string())); + assert!(req.peer_addr().is_some()); + Ok("says hello") }); - server.race(client).await + let server = app.listen_with_cancelation_token(("localhost", port), cancelation_token.clone()); + let server = task::spawn(server); + + task::sleep(Duration::from_millis(100)).await; + let string = surf::get(format!("http://localhost:{}", port)) + .body(Body::from_string("nori".to_string())) + .recv_string() + .await + .unwrap(); + assert_eq!(string, "says hello"); + + cancelation_token.complete(); + + server.await.expect("Server did not complete gracefully"); + Result::<(), http_types::Error>::Ok(()) }) } @@ -73,30 +76,31 @@ fn json() -> tide::Result<()> { } task::block_on(async { + + let cancelation_token = tide::CancelationToken::new(); + let port = test_utils::find_port().await; - let server = task::spawn(async move { - let mut app = tide::new(); - app.at("/").get(|mut req: Request<()>| async move { - let mut counter: Counter = req.body_json().await.unwrap(); - assert_eq!(counter.count, 0); - counter.count = 1; - Ok(Body::from_json(&counter)?) - }); - app.listen(("localhost", port)).await?; - Result::<(), http_types::Error>::Ok(()) - }); - let client = task::spawn(async move { - task::sleep(Duration::from_millis(100)).await; - let counter: Counter = surf::get(format!("http://localhost:{}", &port)) - .body(Body::from_json(&Counter { count: 0 })?) - .recv_json() - .await - .unwrap(); - assert_eq!(counter.count, 1); - Ok(()) + let mut app = tide::new(); + app.at("/").get(|mut req: Request<()>| async move { + let mut counter: Counter = req.body_json().await.unwrap(); + assert_eq!(counter.count, 0); + counter.count = 1; + Ok(Body::from_json(&counter)?) }); - server.race(client).await + let server = app.listen_with_cancelation_token(("localhost", port), cancelation_token.clone()); + let server = task::spawn(server); + + task::sleep(Duration::from_millis(100)).await; + let counter: Counter = surf::get(format!("http://localhost:{}", &port)) + .body(Body::from_json(&Counter { count: 0 })?) + .recv_json() + .await + .unwrap(); + assert_eq!(counter.count, 1); + + server.await.expect("Server did not complete gracefully"); + Result::<(), http_types::Error>::Ok(()) }) } From 4af9cc8d0c10712e48f24b05afb15b38cace1a75 Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Fri, 6 Nov 2020 22:56:10 -0500 Subject: [PATCH 3/4] Removed unused import --- tests/server.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/server.rs b/tests/server.rs index 574fbe4aa..9bf53c30d 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -1,5 +1,4 @@ mod test_utils; -use async_std::prelude::*; use async_std::task; use std::time::Duration; From 48782f0039c9c91f1764e435282e5d5a14326e67 Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Sun, 8 Nov 2020 10:47:44 -0500 Subject: [PATCH 4/4] Minor cleanup, excluded Visual Studio Code information --- .gitignore | 3 +- .vscode/launch.json | 942 --------------------------------------- src/cancelation_token.rs | 14 +- 3 files changed, 8 insertions(+), 951 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.gitignore b/.gitignore index 00541512b..f23a7b55e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ dist/ npm-debug.log* Cargo.lock .DS_Store -.idea \ No newline at end of file +.idea +.vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 2f68fdb90..000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,942 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in library 'tide'", - "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=tide" - ], - "filter": { - "name": "tide", - "kind": "lib" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'cookies'", - "cargo": { - "args": [ - "build", - "--example=cookies", - "--package=tide" - ], - "filter": { - "name": "cookies", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'cookies'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=cookies", - "--package=tide" - ], - "filter": { - "name": "cookies", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'sessions'", - "cargo": { - "args": [ - "build", - "--example=sessions", - "--package=tide" - ], - "filter": { - "name": "sessions", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'sessions'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=sessions", - "--package=tide" - ], - "filter": { - "name": "sessions", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'chunked'", - "cargo": { - "args": [ - "build", - "--example=chunked", - "--package=tide" - ], - "filter": { - "name": "chunked", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'chunked'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=chunked", - "--package=tide" - ], - "filter": { - "name": "chunked", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'sse'", - "cargo": { - "args": [ - "build", - "--example=sse", - "--package=tide" - ], - "filter": { - "name": "sse", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'sse'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=sse", - "--package=tide" - ], - "filter": { - "name": "sse", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'fib'", - "cargo": { - "args": [ - "build", - "--example=fib", - "--package=tide" - ], - "filter": { - "name": "fib", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'fib'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=fib", - "--package=tide" - ], - "filter": { - "name": "fib", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'redirect'", - "cargo": { - "args": [ - "build", - "--example=redirect", - "--package=tide" - ], - "filter": { - "name": "redirect", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'redirect'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=redirect", - "--package=tide" - ], - "filter": { - "name": "redirect", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'upload'", - "cargo": { - "args": [ - "build", - "--example=upload", - "--package=tide" - ], - "filter": { - "name": "upload", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'upload'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=upload", - "--package=tide" - ], - "filter": { - "name": "upload", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'hello'", - "cargo": { - "args": [ - "build", - "--example=hello", - "--package=tide" - ], - "filter": { - "name": "hello", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'hello'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=hello", - "--package=tide" - ], - "filter": { - "name": "hello", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'graphql'", - "cargo": { - "args": [ - "build", - "--example=graphql", - "--package=tide" - ], - "filter": { - "name": "graphql", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'graphql'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=graphql", - "--package=tide" - ], - "filter": { - "name": "graphql", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'json'", - "cargo": { - "args": [ - "build", - "--example=json", - "--package=tide" - ], - "filter": { - "name": "json", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'json'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=json", - "--package=tide" - ], - "filter": { - "name": "json", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'nested'", - "cargo": { - "args": [ - "build", - "--example=nested", - "--package=tide" - ], - "filter": { - "name": "nested", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'nested'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=nested", - "--package=tide" - ], - "filter": { - "name": "nested", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'concurrent_listeners'", - "cargo": { - "args": [ - "build", - "--example=concurrent_listeners", - "--package=tide" - ], - "filter": { - "name": "concurrent_listeners", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'concurrent_listeners'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=concurrent_listeners", - "--package=tide" - ], - "filter": { - "name": "concurrent_listeners", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'catflap'", - "cargo": { - "args": [ - "build", - "--example=catflap", - "--package=tide" - ], - "filter": { - "name": "catflap", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'catflap'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=catflap", - "--package=tide" - ], - "filter": { - "name": "catflap", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'middleware'", - "cargo": { - "args": [ - "build", - "--example=middleware", - "--package=tide" - ], - "filter": { - "name": "middleware", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'middleware'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=middleware", - "--package=tide" - ], - "filter": { - "name": "middleware", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'static_file'", - "cargo": { - "args": [ - "build", - "--example=static_file", - "--package=tide" - ], - "filter": { - "name": "static_file", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'static_file'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=static_file", - "--package=tide" - ], - "filter": { - "name": "static_file", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'error_handling'", - "cargo": { - "args": [ - "build", - "--example=error_handling", - "--package=tide" - ], - "filter": { - "name": "error_handling", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'error_handling'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=error_handling", - "--package=tide" - ], - "filter": { - "name": "error_handling", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'cookies'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=cookies", - "--package=tide" - ], - "filter": { - "name": "cookies", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'nested'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=nested", - "--package=tide" - ], - "filter": { - "name": "nested", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'sessions'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=sessions", - "--package=tide" - ], - "filter": { - "name": "sessions", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'response'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=response", - "--package=tide" - ], - "filter": { - "name": "response", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'chunked-encode-large'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=chunked-encode-large", - "--package=tide" - ], - "filter": { - "name": "chunked-encode-large", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'chunked-encode-small'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=chunked-encode-small", - "--package=tide" - ], - "filter": { - "name": "chunked-encode-small", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'unix'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=unix", - "--package=tide" - ], - "filter": { - "name": "unix", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'log'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=log", - "--package=tide" - ], - "filter": { - "name": "log", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'server'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=server", - "--package=tide" - ], - "filter": { - "name": "server", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'params'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=params", - "--package=tide" - ], - "filter": { - "name": "params", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'test_utils'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=test_utils", - "--package=tide" - ], - "filter": { - "name": "test_utils", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'route_middleware'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=route_middleware", - "--package=tide" - ], - "filter": { - "name": "route_middleware", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'function_middleware'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=function_middleware", - "--package=tide" - ], - "filter": { - "name": "function_middleware", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'endpoint'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=endpoint", - "--package=tide" - ], - "filter": { - "name": "endpoint", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'serve_dir'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=serve_dir", - "--package=tide" - ], - "filter": { - "name": "serve_dir", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug integration test 'wildcard'", - "cargo": { - "args": [ - "test", - "--no-run", - "--test=wildcard", - "--package=tide" - ], - "filter": { - "name": "wildcard", - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug benchmark 'router'", - "cargo": { - "args": [ - "test", - "--no-run", - "--bench=router", - "--package=tide" - ], - "filter": { - "name": "router", - "kind": "bench" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - } - ] -} \ No newline at end of file diff --git a/src/cancelation_token.rs b/src/cancelation_token.rs index 47bd46f6f..58d4d46e0 100644 --- a/src/cancelation_token.rs +++ b/src/cancelation_token.rs @@ -1,11 +1,7 @@ -use { - std::{ - future::Future, - pin::Pin, - sync::{Arc, Mutex}, - task::{Context, Poll, Waker}, - }, -}; +use std::future::Future; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll, Waker}; #[derive(Debug)] pub struct CancelationToken { @@ -18,6 +14,7 @@ struct CancelationTokenState { waker: Option } +/// Future that allows gracefully shutting down the server impl CancelationToken { pub fn new() -> CancelationToken { CancelationToken { @@ -28,6 +25,7 @@ impl CancelationToken { } } + /// Call to shut down the server pub fn complete(&self) { let mut shared_state = self.shared_state.lock().unwrap();