feat(server): add Server::with_graceful_shutdown method
This adds a "combinator" method to `Server`, which accepts a user's future to "select" on. All connections received by the `Server` will be tracked, and if the user's future finishes, graceful shutdown will begin. - The listener will be closed immediately. - The currently active connections will all be notified to start a graceful shutdown. For HTTP/1, that means finishing the existing response and using `connection: clone`. For HTTP/2, the graceful `GOAWAY` process is started. - Once all active connections have terminated, the graceful future will return. Closes #1575
This commit is contained in:
115
src/common/drain.rs
Normal file
115
src/common/drain.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use std::mem;
|
||||
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use futures::future::Shared;
|
||||
use futures::sync::{mpsc, oneshot};
|
||||
|
||||
use super::Never;
|
||||
|
||||
pub fn channel() -> (Signal, Watch) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let (drained_tx, drained_rx) = mpsc::channel(0);
|
||||
(
|
||||
Signal {
|
||||
drained_rx,
|
||||
tx,
|
||||
},
|
||||
Watch {
|
||||
drained_tx,
|
||||
rx: rx.shared(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub struct Signal {
|
||||
drained_rx: mpsc::Receiver<Never>,
|
||||
tx: oneshot::Sender<()>,
|
||||
}
|
||||
|
||||
pub struct Draining {
|
||||
drained_rx: mpsc::Receiver<Never>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Watch {
|
||||
drained_tx: mpsc::Sender<Never>,
|
||||
rx: Shared<oneshot::Receiver<()>>,
|
||||
}
|
||||
|
||||
pub struct Watching<F, FN> {
|
||||
future: F,
|
||||
state: State<FN>,
|
||||
watch: Watch,
|
||||
}
|
||||
|
||||
enum State<F> {
|
||||
Watch(F),
|
||||
Draining,
|
||||
}
|
||||
|
||||
impl Signal {
|
||||
pub fn drain(self) -> Draining {
|
||||
let _ = self.tx.send(());
|
||||
Draining {
|
||||
drained_rx: self.drained_rx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for Draining {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
match try_ready!(self.drained_rx.poll()) {
|
||||
Some(never) => match never {},
|
||||
None => Ok(Async::Ready(())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Watch {
|
||||
pub fn watch<F, FN>(self, future: F, on_drain: FN) -> Watching<F, FN>
|
||||
where
|
||||
F: Future,
|
||||
FN: FnOnce(&mut F),
|
||||
{
|
||||
Watching {
|
||||
future,
|
||||
state: State::Watch(on_drain),
|
||||
watch: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, FN> Future for Watching<F, FN>
|
||||
where
|
||||
F: Future,
|
||||
FN: FnOnce(&mut F),
|
||||
{
|
||||
type Item = F::Item;
|
||||
type Error = F::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
match mem::replace(&mut self.state, State::Draining) {
|
||||
State::Watch(on_drain) => {
|
||||
match self.watch.rx.poll() {
|
||||
Ok(Async::Ready(_)) | Err(_) => {
|
||||
// Drain has been triggered!
|
||||
on_drain(&mut self.future);
|
||||
},
|
||||
Ok(Async::NotReady) => {
|
||||
self.state = State::Watch(on_drain);
|
||||
return self.future.poll();
|
||||
},
|
||||
}
|
||||
},
|
||||
State::Draining => {
|
||||
return self.future.poll();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
mod buf;
|
||||
pub(crate) mod drain;
|
||||
mod exec;
|
||||
pub(crate) mod io;
|
||||
mod lazy;
|
||||
|
||||
Reference in New Issue
Block a user