mirror of
https://github.com/OMGeeky/tarpc.git
synced 2026-01-21 02:37:07 +01:00
Feature rollup (#129)
* Create a directory for the `future::server` module, which has become quite large. server.rs => server/mod.rs. Server submodules for shutdown and connection logic are added. * Add fn thread_pool(...) to sync::server::Options * Configure idle threads to expire after one minute * Add tarpc::util::lazy for lazily executing functions. Similar to `futures::lazy` but useful in different circumstances. Specifically, `futures::lazy` typically requires a closure, whereas `util::lazy` kind of deconstructs a closure into its function and args. * Remove some unstable features, and `cfg(plugin)` only in tests. Features `unboxed_closures` and `fn_traits` are removed by replacing manual Fn impls with Stream impls. This actually leads to slightly more performant code, as well, because some `Rc`s could be removed. * Fix tokio deprecation warnings. Update to use tokio-io in lieu of deprecated tokio-core items. impl AsyncRead's optional `unsafe fn prepare_uninitialized_buffer` for huge perf wins * Add debug impls to all public items and add `deny(missing_debug_implementations)` to the crate. * Bump tokio core version.
This commit is contained in:
@@ -27,6 +27,7 @@ cfg_if! {
|
||||
}
|
||||
|
||||
/// Additional options to configure how the client connects and operates.
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
/// Max packet size in bytes.
|
||||
max_payload_size: u64,
|
||||
@@ -55,7 +56,7 @@ impl Default for Options {
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Set the max payload size in bytes. The default is 2,000,000 (2 MB).
|
||||
/// Set the max payload size in bytes. The default is 2 << 20 (2 MiB).
|
||||
pub fn max_payload_size(mut self, bytes: u64) -> Self {
|
||||
self.max_payload_size = bytes;
|
||||
self
|
||||
@@ -86,6 +87,19 @@ enum Reactor {
|
||||
Remote(reactor::Remote),
|
||||
}
|
||||
|
||||
impl fmt::Debug for Reactor {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
const HANDLE: &'static &'static str = &"Reactor::Handle";
|
||||
const HANDLE_INNER: &'static &'static str = &"Handle { .. }";
|
||||
const REMOTE: &'static &'static str = &"Reactor::Remote";
|
||||
const REMOTE_INNER: &'static &'static str = &"Remote { .. }";
|
||||
|
||||
match *self {
|
||||
Reactor::Handle(_) => f.debug_tuple(HANDLE).field(HANDLE_INNER).finish(),
|
||||
Reactor::Remote(_) => f.debug_tuple(REMOTE).field(REMOTE_INNER).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
pub struct Client<Req, Resp, E>
|
||||
where Req: Serialize + 'static,
|
||||
@@ -213,7 +227,8 @@ impl<Req, Resp, E> ClientExt for Client<Req, Resp, E>
|
||||
let (tx, rx) = futures::oneshot();
|
||||
let setup = move |handle: &reactor::Handle| {
|
||||
connect(handle).then(move |result| {
|
||||
tx.complete(result);
|
||||
// If send fails it means the client no longer cared about connecting.
|
||||
let _ = tx.send(result);
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
|
||||
@@ -1,657 +0,0 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
|
||||
// This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
use {bincode, net2};
|
||||
use errors::WireError;
|
||||
use futures::{Future, Poll, Stream, future as futures, stream};
|
||||
use futures::sync::{mpsc, oneshot};
|
||||
use futures::unsync;
|
||||
use protocol::Proto;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cell::Cell;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::rc::Rc;
|
||||
use tokio_core::io::Io;
|
||||
use tokio_core::net::{Incoming, TcpListener, TcpStream};
|
||||
use tokio_core::reactor;
|
||||
use tokio_proto::BindServer;
|
||||
use tokio_service::{NewService, Service};
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "tls")] {
|
||||
use native_tls::{self, TlsAcceptor};
|
||||
use tokio_tls::{AcceptAsync, TlsAcceptorExt, TlsStream};
|
||||
use errors::native_to_io;
|
||||
use stream_type::StreamType;
|
||||
} else {}
|
||||
}
|
||||
|
||||
/// A handle to a bound server.
|
||||
#[derive(Clone)]
|
||||
pub struct Handle {
|
||||
addr: SocketAddr,
|
||||
shutdown: Shutdown,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
#[doc(hidden)]
|
||||
pub fn listen<S, Req, Resp, E>(new_service: S,
|
||||
addr: SocketAddr,
|
||||
handle: &reactor::Handle,
|
||||
options: Options)
|
||||
-> io::Result<(Self, Listen<S, Req, Resp, E>)>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
let (addr, shutdown, server) =
|
||||
listen_with(new_service,
|
||||
addr, handle,
|
||||
options.max_payload_size,
|
||||
Acceptor::from(options))?;
|
||||
Ok((Handle {
|
||||
addr: addr,
|
||||
shutdown: shutdown,
|
||||
},
|
||||
server))
|
||||
}
|
||||
|
||||
/// Returns a hook for shutting down the server.
|
||||
pub fn shutdown(&self) -> &Shutdown {
|
||||
&self.shutdown
|
||||
}
|
||||
|
||||
/// The socket address the server is bound to.
|
||||
pub fn addr(&self) -> SocketAddr {
|
||||
self.addr
|
||||
}
|
||||
}
|
||||
|
||||
enum Acceptor {
|
||||
Tcp,
|
||||
#[cfg(feature = "tls")]
|
||||
Tls(TlsAcceptor),
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
type Accept = futures::Either<futures::MapErr<futures::Map<AcceptAsync<TcpStream>,
|
||||
fn(TlsStream<TcpStream>) -> StreamType>,
|
||||
fn(native_tls::Error) -> io::Error>,
|
||||
futures::FutureResult<StreamType, io::Error>>;
|
||||
|
||||
#[cfg(not(feature = "tls"))]
|
||||
type Accept = futures::FutureResult<TcpStream, io::Error>;
|
||||
|
||||
impl Acceptor {
|
||||
// TODO(https://github.com/tokio-rs/tokio-proto/issues/132): move this into the ServerProto impl
|
||||
#[cfg(feature = "tls")]
|
||||
fn accept(&self, socket: TcpStream) -> Accept {
|
||||
match *self {
|
||||
Acceptor::Tls(ref tls_acceptor) => {
|
||||
futures::Either::A(tls_acceptor.accept_async(socket)
|
||||
.map(StreamType::Tls as _)
|
||||
.map_err(native_to_io))
|
||||
}
|
||||
Acceptor::Tcp => futures::Either::B(futures::ok(StreamType::Tcp(socket))),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tls"))]
|
||||
fn accept(&self, socket: TcpStream) -> Accept {
|
||||
futures::ok(socket)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
impl From<Options> for Acceptor {
|
||||
fn from(options: Options) -> Self {
|
||||
match options.tls_acceptor {
|
||||
Some(tls_acceptor) => Acceptor::Tls(tls_acceptor),
|
||||
None => Acceptor::Tcp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tls"))]
|
||||
impl From<Options> for Acceptor {
|
||||
fn from(_: Options) -> Self {
|
||||
Acceptor::Tcp
|
||||
}
|
||||
}
|
||||
|
||||
impl FnOnce<((TcpStream, SocketAddr),)> for Acceptor {
|
||||
type Output = Accept;
|
||||
|
||||
extern "rust-call" fn call_once(self, ((socket, _),): ((TcpStream, SocketAddr),)) -> Accept {
|
||||
self.accept(socket)
|
||||
}
|
||||
}
|
||||
|
||||
impl FnMut<((TcpStream, SocketAddr),)> for Acceptor {
|
||||
extern "rust-call" fn call_mut(&mut self,
|
||||
((socket, _),): ((TcpStream, SocketAddr),))
|
||||
-> Accept {
|
||||
self.accept(socket)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fn<((TcpStream, SocketAddr),)> for Acceptor {
|
||||
extern "rust-call" fn call(&self, ((socket, _),): ((TcpStream, SocketAddr),)) -> Accept {
|
||||
self.accept(socket)
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional options to configure how the server operates.
|
||||
pub struct Options {
|
||||
/// Max packet size in bytes.
|
||||
max_payload_size: u64,
|
||||
#[cfg(feature = "tls")]
|
||||
tls_acceptor: Option<TlsAcceptor>,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
#[cfg(not(feature = "tls"))]
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
max_payload_size: 2 << 20,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
max_payload_size: 2 << 20,
|
||||
tls_acceptor: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Set the max payload size in bytes. The default is 2,000,000 (2 MB).
|
||||
pub fn max_payload_size(mut self, bytes: u64) -> Self {
|
||||
self.max_payload_size = bytes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `TlsAcceptor`
|
||||
#[cfg(feature = "tls")]
|
||||
pub fn tls(mut self, tls_acceptor: TlsAcceptor) -> Self {
|
||||
self.tls_acceptor = Some(tls_acceptor);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A message from server to client.
|
||||
#[doc(hidden)]
|
||||
pub type Response<T, E> = Result<T, WireError<E>>;
|
||||
|
||||
/// A hook to shut down a running server.
|
||||
#[derive(Clone)]
|
||||
pub struct Shutdown {
|
||||
tx: mpsc::UnboundedSender<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
/// A future that resolves when server shutdown completes.
|
||||
pub struct ShutdownFuture {
|
||||
inner: futures::Either<futures::FutureResult<(), ()>,
|
||||
futures::OrElse<oneshot::Receiver<()>, Result<(), ()>, AlwaysOk>>,
|
||||
}
|
||||
|
||||
impl Future for ShutdownFuture {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl Shutdown {
|
||||
/// Initiates an orderly server shutdown.
|
||||
///
|
||||
/// First, the server enters lameduck mode, in which
|
||||
/// existing connections are honored but no new connections are accepted. Then, once all
|
||||
/// connections are closed, it initates total shutdown.
|
||||
///
|
||||
/// This fn will not return until the server is completely shut down.
|
||||
pub fn shutdown(&self) -> ShutdownFuture {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let inner = if let Err(_) = self.tx.send(tx) {
|
||||
trace!("Server already initiated shutdown.");
|
||||
futures::Either::A(futures::ok(()))
|
||||
} else {
|
||||
futures::Either::B(rx.or_else(AlwaysOk))
|
||||
};
|
||||
ShutdownFuture { inner: inner }
|
||||
}
|
||||
}
|
||||
|
||||
enum ConnectionAction {
|
||||
Increment,
|
||||
Decrement,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ConnectionTracker {
|
||||
tx: unsync::mpsc::UnboundedSender<ConnectionAction>,
|
||||
}
|
||||
|
||||
impl ConnectionTracker {
|
||||
fn increment(&self) {
|
||||
let _ = self.tx.send(ConnectionAction::Increment);
|
||||
}
|
||||
|
||||
fn decrement(&self) {
|
||||
debug!("Closing connection");
|
||||
let _ = self.tx.send(ConnectionAction::Decrement);
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionTrackingService<S> {
|
||||
service: S,
|
||||
tracker: ConnectionTracker,
|
||||
}
|
||||
|
||||
impl<S: Service> Service for ConnectionTrackingService<S> {
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
|
||||
fn call(&self, req: Self::Request) -> Self::Future {
|
||||
trace!("Calling service.");
|
||||
self.service.call(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Drop for ConnectionTrackingService<S> {
|
||||
fn drop(&mut self) {
|
||||
debug!("Dropping ConnnectionTrackingService.");
|
||||
self.tracker.decrement();
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionTrackingNewService<S> {
|
||||
new_service: S,
|
||||
connection_tracker: ConnectionTracker,
|
||||
}
|
||||
|
||||
impl<S: NewService> NewService for ConnectionTrackingNewService<S> {
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Instance = ConnectionTrackingService<S::Instance>;
|
||||
|
||||
fn new_service(&self) -> io::Result<Self::Instance> {
|
||||
self.connection_tracker.increment();
|
||||
Ok(ConnectionTrackingService {
|
||||
service: self.new_service.new_service()?,
|
||||
tracker: self.connection_tracker.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ShutdownSetter {
|
||||
shutdown: Rc<Cell<Option<oneshot::Sender<()>>>>,
|
||||
}
|
||||
|
||||
impl FnOnce<(oneshot::Sender<()>,)> for ShutdownSetter {
|
||||
type Output = ();
|
||||
|
||||
extern "rust-call" fn call_once(self, tx: (oneshot::Sender<()>,)) {
|
||||
self.call(tx);
|
||||
}
|
||||
}
|
||||
|
||||
impl FnMut<(oneshot::Sender<()>,)> for ShutdownSetter {
|
||||
extern "rust-call" fn call_mut(&mut self, tx: (oneshot::Sender<()>,)) {
|
||||
self.call(tx);
|
||||
}
|
||||
}
|
||||
|
||||
impl Fn<(oneshot::Sender<()>,)> for ShutdownSetter {
|
||||
extern "rust-call" fn call(&self, (tx,): (oneshot::Sender<()>,)) {
|
||||
debug!("Received shutdown request.");
|
||||
self.shutdown.set(Some(tx));
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionWatcher {
|
||||
connections: Rc<Cell<u64>>,
|
||||
}
|
||||
|
||||
impl FnOnce<(ConnectionAction,)> for ConnectionWatcher {
|
||||
type Output = ();
|
||||
|
||||
extern "rust-call" fn call_once(self, action: (ConnectionAction,)) {
|
||||
self.call(action);
|
||||
}
|
||||
}
|
||||
|
||||
impl FnMut<(ConnectionAction,)> for ConnectionWatcher {
|
||||
extern "rust-call" fn call_mut(&mut self, action: (ConnectionAction,)) {
|
||||
self.call(action);
|
||||
}
|
||||
}
|
||||
|
||||
impl Fn<(ConnectionAction,)> for ConnectionWatcher {
|
||||
extern "rust-call" fn call(&self, (action,): (ConnectionAction,)) {
|
||||
match action {
|
||||
ConnectionAction::Increment => self.connections.set(self.connections.get() + 1),
|
||||
ConnectionAction::Decrement => self.connections.set(self.connections.get() - 1),
|
||||
}
|
||||
trace!("Open connections: {}", self.connections.get());
|
||||
}
|
||||
}
|
||||
|
||||
struct ShutdownPredicate {
|
||||
shutdown: Rc<Cell<Option<oneshot::Sender<()>>>>,
|
||||
connections: Rc<Cell<u64>>,
|
||||
}
|
||||
|
||||
impl<T> FnOnce<T> for ShutdownPredicate {
|
||||
type Output = Result<bool, ()>;
|
||||
|
||||
extern "rust-call" fn call_once(self, arg: T) -> Self::Output {
|
||||
self.call(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FnMut<T> for ShutdownPredicate {
|
||||
extern "rust-call" fn call_mut(&mut self, arg: T) -> Self::Output {
|
||||
self.call(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Fn<T> for ShutdownPredicate {
|
||||
extern "rust-call" fn call(&self, _: T) -> Self::Output {
|
||||
match self.shutdown.take() {
|
||||
Some(shutdown) => {
|
||||
let num_connections = self.connections.get();
|
||||
debug!("Lameduck mode: {} open connections", num_connections);
|
||||
if num_connections == 0 {
|
||||
debug!("Shutting down.");
|
||||
let _ = shutdown.complete(());
|
||||
Ok(false)
|
||||
} else {
|
||||
self.shutdown.set(Some(shutdown));
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
None => Ok(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Warn(&'static str);
|
||||
|
||||
impl<T> FnOnce<T> for Warn {
|
||||
type Output = ();
|
||||
|
||||
extern "rust-call" fn call_once(self, arg: T) -> Self::Output {
|
||||
self.call(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FnMut<T> for Warn {
|
||||
extern "rust-call" fn call_mut(&mut self, arg: T) -> Self::Output {
|
||||
self.call(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Fn<T> for Warn {
|
||||
extern "rust-call" fn call(&self, _: T) -> Self::Output {
|
||||
warn!("{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
struct AlwaysOk;
|
||||
|
||||
impl<T> FnOnce<T> for AlwaysOk {
|
||||
type Output = Result<(), ()>;
|
||||
|
||||
extern "rust-call" fn call_once(self, arg: T) -> Self::Output {
|
||||
self.call(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FnMut<T> for AlwaysOk {
|
||||
extern "rust-call" fn call_mut(&mut self, arg: T) -> Self::Output {
|
||||
self.call(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Fn<T> for AlwaysOk {
|
||||
extern "rust-call" fn call(&self, _: T) -> Self::Output {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
type ShutdownStream = stream::Map<stream::Take<mpsc::UnboundedReceiver<oneshot::Sender<()>>>,
|
||||
ShutdownSetter>;
|
||||
|
||||
type ConnectionStream = stream::Map<unsync::mpsc::UnboundedReceiver<ConnectionAction>,
|
||||
ConnectionWatcher>;
|
||||
|
||||
struct ShutdownWatcher {
|
||||
inner: stream::ForEach<stream::MapErr<stream::TakeWhile<stream::Merge<ShutdownStream,
|
||||
ConnectionStream>,
|
||||
ShutdownPredicate,
|
||||
Result<bool, ()>>,
|
||||
Warn>,
|
||||
AlwaysOk,
|
||||
Result<(), ()>>,
|
||||
}
|
||||
|
||||
impl Future for ShutdownWatcher {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a future that completes when a shutdown is signaled and no connections are open.
|
||||
fn shutdown_watcher() -> (ConnectionTracker, Shutdown, ShutdownWatcher) {
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::unbounded::<oneshot::Sender<()>>();
|
||||
let (connection_tx, connection_rx) = unsync::mpsc::unbounded();
|
||||
let shutdown = Rc::new(Cell::new(None));
|
||||
let connections = Rc::new(Cell::new(0));
|
||||
let shutdown2 = shutdown.clone();
|
||||
let connections2 = connections.clone();
|
||||
|
||||
let inner = shutdown_rx.take(1)
|
||||
.map(ShutdownSetter { shutdown: shutdown })
|
||||
.merge(connection_rx.map(ConnectionWatcher { connections: connections }))
|
||||
.take_while(ShutdownPredicate {
|
||||
shutdown: shutdown2,
|
||||
connections: connections2,
|
||||
})
|
||||
.map_err(Warn("UnboundedReceiver resolved to an Err; can it do that?"))
|
||||
.for_each(AlwaysOk);
|
||||
|
||||
(ConnectionTracker { tx: connection_tx },
|
||||
Shutdown { tx: shutdown_tx },
|
||||
ShutdownWatcher { inner: inner })
|
||||
}
|
||||
|
||||
type AcceptStream = stream::AndThen<Incoming, Acceptor, Accept>;
|
||||
|
||||
type BindStream<S> = stream::ForEach<AcceptStream,
|
||||
Bind<ConnectionTrackingNewService<S>>,
|
||||
io::Result<()>>;
|
||||
|
||||
/// The future representing a running server.
|
||||
#[doc(hidden)]
|
||||
pub struct Listen<S, Req, Resp, E>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
inner: futures::Then<futures::Select<futures::MapErr<BindStream<S>, fn(io::Error)>,
|
||||
ShutdownWatcher>,
|
||||
Result<(), ()>,
|
||||
AlwaysOk>,
|
||||
}
|
||||
|
||||
impl<S, Req, Resp, E> Future for Listen<S, Req, Resp, E>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns a service that binds to the given address using the given handle.
|
||||
fn listen_with<S, Req, Resp, E>(new_service: S,
|
||||
addr: SocketAddr,
|
||||
handle: &reactor::Handle,
|
||||
max_payload_size: u64,
|
||||
acceptor: Acceptor)
|
||||
-> io::Result<(SocketAddr, Shutdown, Listen<S, Req, Resp, E>)>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
let listener = listener(&addr, handle)?;
|
||||
let addr = listener.local_addr()?;
|
||||
debug!("Listening on {}.", addr);
|
||||
|
||||
let handle = handle.clone();
|
||||
|
||||
let (connection_tracker, shutdown, shutdown_future) = shutdown_watcher();
|
||||
let server = listener.incoming()
|
||||
.and_then(acceptor)
|
||||
.for_each(Bind {
|
||||
max_payload_size: max_payload_size,
|
||||
handle: handle,
|
||||
new_service: ConnectionTrackingNewService {
|
||||
connection_tracker: connection_tracker,
|
||||
new_service: new_service,
|
||||
},
|
||||
})
|
||||
.map_err(log_err as _);
|
||||
|
||||
let server = server.select(shutdown_future).then(AlwaysOk);
|
||||
Ok((addr, shutdown, Listen { inner: server }))
|
||||
}
|
||||
|
||||
fn log_err(e: io::Error) {
|
||||
error!("While processing incoming connections: {}", e);
|
||||
}
|
||||
|
||||
struct Bind<S> {
|
||||
max_payload_size: u64,
|
||||
handle: reactor::Handle,
|
||||
new_service: S,
|
||||
}
|
||||
|
||||
impl<S, Req, Resp, E> Bind<S>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
fn bind<I>(&self, socket: I) -> io::Result<()>
|
||||
where I: Io + 'static
|
||||
{
|
||||
Proto::new(self.max_payload_size)
|
||||
.bind_server(&self.handle, socket, self.new_service.new_service()?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S, Req, Resp, E> FnOnce<(I,)> for Bind<S>
|
||||
where I: Io + 'static,
|
||||
S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
type Output = io::Result<()>;
|
||||
|
||||
extern "rust-call" fn call_once(self, (socket,): (I,)) -> io::Result<()> {
|
||||
self.bind(socket)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S, Req, Resp, E> FnMut<(I,)> for Bind<S>
|
||||
where I: Io + 'static,
|
||||
S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
extern "rust-call" fn call_mut(&mut self, (socket,): (I,)) -> io::Result<()> {
|
||||
self.bind(socket)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S, Req, Resp, E> Fn<(I,)> for Bind<S>
|
||||
where I: Io + 'static,
|
||||
S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
extern "rust-call" fn call(&self, (socket,): (I,)) -> io::Result<()> {
|
||||
self.bind(socket)
|
||||
}
|
||||
}
|
||||
|
||||
fn listener(addr: &SocketAddr, handle: &reactor::Handle) -> io::Result<TcpListener> {
|
||||
const PENDING_CONNECTION_BACKLOG: i32 = 1024;
|
||||
|
||||
let builder = match *addr {
|
||||
SocketAddr::V4(_) => net2::TcpBuilder::new_v4(),
|
||||
SocketAddr::V6(_) => net2::TcpBuilder::new_v6(),
|
||||
}?;
|
||||
configure_tcp(&builder)?;
|
||||
builder.reuse_address(true)?;
|
||||
builder.bind(addr)?
|
||||
.listen(PENDING_CONNECTION_BACKLOG)
|
||||
.and_then(|l| TcpListener::from_listener(l, addr, handle))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn configure_tcp(tcp: &net2::TcpBuilder) -> io::Result<()> {
|
||||
use net2::unix::UnixTcpBuilderExt;
|
||||
|
||||
tcp.reuse_port(true)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn configure_tcp(_tcp: &net2::TcpBuilder) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
76
src/future/server/connection.rs
Normal file
76
src/future/server/connection.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use futures::unsync;
|
||||
use std::io;
|
||||
use tokio_service::{NewService, Service};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Action {
|
||||
Increment,
|
||||
Decrement,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Tracker {
|
||||
pub tx: unsync::mpsc::UnboundedSender<Action>,
|
||||
}
|
||||
|
||||
impl Tracker {
|
||||
pub fn pair() -> (Self, unsync::mpsc::UnboundedReceiver<Action>) {
|
||||
let (tx, rx) = unsync::mpsc::unbounded();
|
||||
(Self { tx }, rx)
|
||||
}
|
||||
|
||||
pub fn increment(&self) {
|
||||
let _ = self.tx.send(Action::Increment);
|
||||
}
|
||||
|
||||
pub fn decrement(&self) {
|
||||
debug!("Closing connection");
|
||||
let _ = self.tx.send(Action::Decrement);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TrackingService<S> {
|
||||
pub service: S,
|
||||
pub tracker: Tracker,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TrackingNewService<S> {
|
||||
pub new_service: S,
|
||||
pub connection_tracker: Tracker,
|
||||
}
|
||||
|
||||
impl<S: Service> Service for TrackingService<S> {
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
|
||||
fn call(&self, req: Self::Request) -> Self::Future {
|
||||
trace!("Calling service.");
|
||||
self.service.call(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Drop for TrackingService<S> {
|
||||
fn drop(&mut self) {
|
||||
debug!("Dropping ConnnectionTrackingService.");
|
||||
self.tracker.decrement();
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: NewService> NewService for TrackingNewService<S> {
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Instance = TrackingService<S::Instance>;
|
||||
|
||||
fn new_service(&self) -> io::Result<Self::Instance> {
|
||||
self.connection_tracker.increment();
|
||||
Ok(TrackingService {
|
||||
service: self.new_service.new_service()?,
|
||||
tracker: self.connection_tracker.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
448
src/future/server/mod.rs
Normal file
448
src/future/server/mod.rs
Normal file
@@ -0,0 +1,448 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
|
||||
// This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
use {bincode, net2};
|
||||
use errors::WireError;
|
||||
use futures::{Async, Future, Poll, Stream, future as futures};
|
||||
use protocol::Proto;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use stream_type::StreamType;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_core::net::{Incoming, TcpListener, TcpStream};
|
||||
use tokio_core::reactor;
|
||||
use tokio_proto::BindServer;
|
||||
use tokio_service::NewService;
|
||||
|
||||
mod connection;
|
||||
mod shutdown;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "tls")] {
|
||||
use native_tls::{self, TlsAcceptor};
|
||||
use tokio_tls::{AcceptAsync, TlsAcceptorExt, TlsStream};
|
||||
use errors::native_to_io;
|
||||
} else {}
|
||||
}
|
||||
|
||||
pub use self::shutdown::Shutdown;
|
||||
|
||||
/// A handle to a bound server.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Handle {
|
||||
addr: SocketAddr,
|
||||
shutdown: Shutdown,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
/// Returns a hook for shutting down the server.
|
||||
pub fn shutdown(&self) -> &Shutdown {
|
||||
&self.shutdown
|
||||
}
|
||||
|
||||
/// The socket address the server is bound to.
|
||||
pub fn addr(&self) -> SocketAddr {
|
||||
self.addr
|
||||
}
|
||||
}
|
||||
|
||||
enum Acceptor {
|
||||
Tcp,
|
||||
#[cfg(feature = "tls")]
|
||||
Tls(TlsAcceptor),
|
||||
}
|
||||
|
||||
struct Accept {
|
||||
#[cfg(feature = "tls")]
|
||||
inner: futures::Either<futures::MapErr<futures::Map<AcceptAsync<TcpStream>,
|
||||
fn(TlsStream<TcpStream>) -> StreamType>,
|
||||
fn(native_tls::Error) -> io::Error>,
|
||||
futures::FutureResult<StreamType, io::Error>>,
|
||||
#[cfg(not(feature = "tls"))]
|
||||
inner: futures::FutureResult<StreamType, io::Error>,
|
||||
}
|
||||
|
||||
impl Future for Accept {
|
||||
type Item = StreamType;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl Acceptor {
|
||||
// TODO(https://github.com/tokio-rs/tokio-proto/issues/132): move this into the ServerProto impl
|
||||
#[cfg(feature = "tls")]
|
||||
fn accept(&self, socket: TcpStream) -> Accept {
|
||||
Accept {
|
||||
inner: match *self {
|
||||
Acceptor::Tls(ref tls_acceptor) => {
|
||||
futures::Either::A(tls_acceptor.accept_async(socket)
|
||||
.map(StreamType::Tls as _)
|
||||
.map_err(native_to_io))
|
||||
}
|
||||
Acceptor::Tcp => futures::Either::B(futures::ok(StreamType::Tcp(socket))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tls"))]
|
||||
fn accept(&self, socket: TcpStream) -> Accept {
|
||||
Accept {
|
||||
inner: futures::ok(StreamType::Tcp(socket))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
impl From<Options> for Acceptor {
|
||||
fn from(options: Options) -> Self {
|
||||
match options.tls_acceptor {
|
||||
Some(tls_acceptor) => Acceptor::Tls(tls_acceptor),
|
||||
None => Acceptor::Tcp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tls"))]
|
||||
impl From<Options> for Acceptor {
|
||||
fn from(_: Options) -> Self {
|
||||
Acceptor::Tcp
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Acceptor {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Acceptor::*;
|
||||
#[cfg(feature = "tls")]
|
||||
const TLS: &'static &'static str = &"TlsAcceptor { .. }";
|
||||
|
||||
match *self {
|
||||
Tcp => fmt.debug_tuple("Acceptor::Tcp").finish(),
|
||||
#[cfg(feature = "tls")]
|
||||
Tls(_) => fmt.debug_tuple("Acceptlr::Tls").field(TLS).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Accept {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Accept").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AcceptStream<S> {
|
||||
stream: S,
|
||||
acceptor: Acceptor,
|
||||
future: Option<Accept>,
|
||||
}
|
||||
|
||||
impl<S> Stream for AcceptStream<S>
|
||||
where S: Stream<Item=(TcpStream, SocketAddr), Error = io::Error>,
|
||||
{
|
||||
type Item = <Accept as Future>::Item;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, io::Error> {
|
||||
if self.future.is_none() {
|
||||
let stream = match try_ready!(self.stream.poll()) {
|
||||
None => return Ok(Async::Ready(None)),
|
||||
Some((stream, _)) => stream,
|
||||
};
|
||||
self.future = Some(self.acceptor.accept(stream));
|
||||
}
|
||||
assert!(self.future.is_some());
|
||||
match self.future.as_mut().unwrap().poll() {
|
||||
Ok(Async::Ready(e)) => {
|
||||
self.future = None;
|
||||
Ok(Async::Ready(Some(e)))
|
||||
}
|
||||
Err(e) => {
|
||||
self.future = None;
|
||||
Err(e)
|
||||
}
|
||||
Ok(Async::NotReady) => Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional options to configure how the server operates.
|
||||
pub struct Options {
|
||||
/// Max packet size in bytes.
|
||||
max_payload_size: u64,
|
||||
#[cfg(feature = "tls")]
|
||||
tls_acceptor: Option<TlsAcceptor>,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
#[cfg(not(feature = "tls"))]
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
max_payload_size: 2 << 20,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
max_payload_size: 2 << 20,
|
||||
tls_acceptor: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Set the max payload size in bytes. The default is 2 << 20 (2 MiB).
|
||||
pub fn max_payload_size(mut self, bytes: u64) -> Self {
|
||||
self.max_payload_size = bytes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `TlsAcceptor`
|
||||
#[cfg(feature = "tls")]
|
||||
pub fn tls(mut self, tls_acceptor: TlsAcceptor) -> Self {
|
||||
self.tls_acceptor = Some(tls_acceptor);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Options {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[cfg(feature = "tls")]
|
||||
const SOME: &'static &'static str = &"Some(_)";
|
||||
#[cfg(feature = "tls")]
|
||||
const NONE: &'static &'static str = &"None";
|
||||
|
||||
let mut debug_struct = fmt.debug_struct("Options");
|
||||
#[cfg(feature = "tls")]
|
||||
debug_struct.field("tls_acceptor", if self.tls_acceptor.is_some() { SOME } else { NONE });
|
||||
debug_struct.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A message from server to client.
|
||||
#[doc(hidden)]
|
||||
pub type Response<T, E> = Result<T, WireError<E>>;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn listen<S, Req, Resp, E>(new_service: S,
|
||||
addr: SocketAddr,
|
||||
handle: &reactor::Handle,
|
||||
options: Options)
|
||||
-> io::Result<(Handle, Listen<S, Req, Resp, E>)>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
let (addr, shutdown, server) = listen_with(
|
||||
new_service, addr, handle, options.max_payload_size, Acceptor::from(options))?;
|
||||
Ok((Handle {
|
||||
addr: addr,
|
||||
shutdown: shutdown,
|
||||
},
|
||||
server))
|
||||
}
|
||||
|
||||
/// Spawns a service that binds to the given address using the given handle.
|
||||
fn listen_with<S, Req, Resp, E>(new_service: S,
|
||||
addr: SocketAddr,
|
||||
handle: &reactor::Handle,
|
||||
max_payload_size: u64,
|
||||
acceptor: Acceptor)
|
||||
-> io::Result<(SocketAddr, Shutdown, Listen<S, Req, Resp, E>)>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
let listener = listener(&addr, handle)?;
|
||||
let addr = listener.local_addr()?;
|
||||
debug!("Listening on {}.", addr);
|
||||
|
||||
let handle = handle.clone();
|
||||
let (connection_tracker, shutdown, shutdown_future) = shutdown::Watcher::triple();
|
||||
let server = BindStream {
|
||||
handle: handle,
|
||||
new_service: connection::TrackingNewService {
|
||||
connection_tracker: connection_tracker,
|
||||
new_service: new_service,
|
||||
},
|
||||
stream: AcceptStream {
|
||||
stream: listener.incoming(),
|
||||
acceptor: acceptor,
|
||||
future: None,
|
||||
},
|
||||
max_payload_size: max_payload_size,
|
||||
};
|
||||
|
||||
let server = AlwaysOkUnit(server.select(shutdown_future));
|
||||
Ok((addr, shutdown, Listen { inner: server }))
|
||||
}
|
||||
|
||||
fn listener(addr: &SocketAddr, handle: &reactor::Handle) -> io::Result<TcpListener> {
|
||||
const PENDING_CONNECTION_BACKLOG: i32 = 1024;
|
||||
|
||||
let builder = match *addr {
|
||||
SocketAddr::V4(_) => net2::TcpBuilder::new_v4(),
|
||||
SocketAddr::V6(_) => net2::TcpBuilder::new_v6(),
|
||||
}?;
|
||||
configure_tcp(&builder)?;
|
||||
builder.reuse_address(true)?;
|
||||
builder.bind(addr)?
|
||||
.listen(PENDING_CONNECTION_BACKLOG)
|
||||
.and_then(|l| TcpListener::from_listener(l, addr, handle))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn configure_tcp(tcp: &net2::TcpBuilder) -> io::Result<()> {
|
||||
use net2::unix::UnixTcpBuilderExt;
|
||||
tcp.reuse_port(true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn configure_tcp(_tcp: &net2::TcpBuilder) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct BindStream<S, St> {
|
||||
handle: reactor::Handle,
|
||||
new_service: connection::TrackingNewService<S>,
|
||||
stream: St,
|
||||
max_payload_size: u64,
|
||||
}
|
||||
|
||||
impl<S, St> fmt::Debug for BindStream<S, St>
|
||||
where S: fmt::Debug,
|
||||
St: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
const HANDLE: &'static &'static str = &"Handle { .. }";
|
||||
f.debug_struct("BindStream")
|
||||
.field("handle", HANDLE)
|
||||
.field("new_service", &self.new_service)
|
||||
.field("stream", &self.stream)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Req, Resp, E, I, St> BindStream<S, St>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static,
|
||||
I: AsyncRead + AsyncWrite + 'static,
|
||||
St: Stream<Item=I, Error=io::Error>,
|
||||
{
|
||||
fn bind_each(&mut self) -> Poll<(), io::Error> {
|
||||
loop {
|
||||
match try!(self.stream.poll()) {
|
||||
Async::Ready(Some(socket)) => {
|
||||
Proto::new(self.max_payload_size)
|
||||
.bind_server(&self.handle, socket, self.new_service.new_service()?);
|
||||
}
|
||||
Async::Ready(None) => return Ok(Async::Ready(())),
|
||||
Async::NotReady => return Ok(Async::NotReady),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Req, Resp, E, I, St> Future for BindStream<S, St>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static,
|
||||
I: AsyncRead + AsyncWrite + 'static,
|
||||
St: Stream<Item=I, Error=io::Error>,
|
||||
{
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
match self.bind_each() {
|
||||
Ok(Async::Ready(())) => Ok(Async::Ready(())),
|
||||
Ok(Async::NotReady) => Ok(Async::NotReady),
|
||||
Err(e) => {
|
||||
error!("While processing incoming connections: {}", e);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The future representing a running server.
|
||||
#[doc(hidden)]
|
||||
pub struct Listen<S, Req, Resp, E>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
inner: AlwaysOkUnit<futures::Select<BindStream<S, AcceptStream<Incoming>>,
|
||||
shutdown::Watcher>>,
|
||||
}
|
||||
|
||||
impl<S, Req, Resp, E> Future for Listen<S, Req, Resp, E>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static
|
||||
{
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Req, Resp, E> fmt::Debug for Listen<S, Req, Resp, E>
|
||||
where S: NewService<Request = Result<Req, bincode::Error>,
|
||||
Response = Response<Resp, E>,
|
||||
Error = io::Error> + 'static,
|
||||
Req: Deserialize + 'static,
|
||||
Resp: Serialize + 'static,
|
||||
E: Serialize + 'static,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
f.debug_struct("Listen").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AlwaysOkUnit<F>(F);
|
||||
|
||||
impl<F> Future for AlwaysOkUnit<F>
|
||||
where F: Future,
|
||||
{
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
match self.0.poll() {
|
||||
Ok(Async::Ready(_)) | Err(_) => Ok(Async::Ready(())),
|
||||
Ok(Async::NotReady) => Ok(Async::NotReady),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
181
src/future/server/shutdown.rs
Normal file
181
src/future/server/shutdown.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use futures::{Async, Future, Poll, Stream, future as futures, stream};
|
||||
use futures::sync::{mpsc, oneshot};
|
||||
use futures::unsync;
|
||||
|
||||
use super::{AlwaysOkUnit, connection};
|
||||
|
||||
/// A hook to shut down a running server.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Shutdown {
|
||||
tx: mpsc::UnboundedSender<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A future that resolves when server shutdown completes.
|
||||
pub struct ShutdownFuture {
|
||||
inner: futures::Either<futures::FutureResult<(), ()>,
|
||||
AlwaysOkUnit<oneshot::Receiver<()>>>,
|
||||
}
|
||||
|
||||
impl Future for ShutdownFuture {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl Shutdown {
|
||||
/// Initiates an orderly server shutdown.
|
||||
///
|
||||
/// First, the server enters lameduck mode, in which
|
||||
/// existing connections are honored but no new connections are accepted. Then, once all
|
||||
/// connections are closed, it initates total shutdown.
|
||||
///
|
||||
/// This fn will not return until the server is completely shut down.
|
||||
pub fn shutdown(&self) -> ShutdownFuture {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let inner = if let Err(_) = self.tx.send(tx) {
|
||||
trace!("Server already initiated shutdown.");
|
||||
futures::Either::A(futures::ok(()))
|
||||
} else {
|
||||
futures::Either::B(AlwaysOkUnit(rx))
|
||||
};
|
||||
ShutdownFuture { inner: inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Watcher {
|
||||
shutdown_rx: stream::Take<mpsc::UnboundedReceiver<oneshot::Sender<()>>>,
|
||||
connections: unsync::mpsc::UnboundedReceiver<connection::Action>,
|
||||
queued_error: Option<()>,
|
||||
shutdown: Option<oneshot::Sender<()>>,
|
||||
done: bool,
|
||||
num_connections: u64,
|
||||
}
|
||||
|
||||
impl Watcher {
|
||||
pub fn triple() -> (connection::Tracker, Shutdown, Self) {
|
||||
let (connection_tx, connections) = connection::Tracker::pair();
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::unbounded();
|
||||
(connection_tx,
|
||||
Shutdown { tx: shutdown_tx },
|
||||
Watcher {
|
||||
shutdown_rx: shutdown_rx.take(1),
|
||||
connections: connections,
|
||||
queued_error: None,
|
||||
shutdown: None,
|
||||
done: false,
|
||||
num_connections: 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn process_connection(&mut self, action: connection::Action) {
|
||||
match action {
|
||||
connection::Action::Increment => self.num_connections += 1,
|
||||
connection::Action::Decrement => self.num_connections -= 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_shutdown_requests(&mut self) -> Poll<Option<()>, ()> {
|
||||
Ok(Async::Ready(match try_ready!(self.shutdown_rx.poll()) {
|
||||
Some(tx) => {
|
||||
debug!("Received shutdown request.");
|
||||
self.shutdown = Some(tx);
|
||||
Some(())
|
||||
}
|
||||
None => None,
|
||||
}))
|
||||
}
|
||||
|
||||
fn poll_connections(&mut self) -> Poll<Option<()>, ()> {
|
||||
Ok(Async::Ready(match try_ready!(self.connections.poll()) {
|
||||
Some(action) => {
|
||||
self.process_connection(action);
|
||||
Some(())
|
||||
}
|
||||
None => None,
|
||||
}))
|
||||
}
|
||||
|
||||
fn poll_shutdown_requests_and_connections(&mut self) -> Poll<Option<()>, ()> {
|
||||
if let Some(e) = self.queued_error.take() {
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
match try!(self.poll_shutdown_requests()) {
|
||||
Async::NotReady => {
|
||||
match try_ready!(self.poll_connections()) {
|
||||
Some(()) => Ok(Async::Ready(Some(()))),
|
||||
None => Ok(Async::NotReady),
|
||||
}
|
||||
}
|
||||
Async::Ready(None) => {
|
||||
match try_ready!(self.poll_connections()) {
|
||||
Some(()) => Ok(Async::Ready(Some(()))),
|
||||
None => Ok(Async::Ready(None)),
|
||||
}
|
||||
}
|
||||
Async::Ready(Some(())) => {
|
||||
match self.poll_connections() {
|
||||
Err(e) => {
|
||||
self.queued_error = Some(e);
|
||||
Ok(Async::Ready(Some(())))
|
||||
}
|
||||
Ok(Async::NotReady) | Ok(Async::Ready(None)) | Ok(Async::Ready(Some(()))) => {
|
||||
Ok(Async::Ready(Some(())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_continue(&mut self) -> bool {
|
||||
match self.shutdown.take() {
|
||||
Some(shutdown) => {
|
||||
debug!("Lameduck mode: {} open connections", self.num_connections);
|
||||
if self.num_connections == 0 {
|
||||
debug!("Shutting down.");
|
||||
// Not required for the shutdown future to be waited on, so this
|
||||
// can fail (which is fine).
|
||||
let _ = shutdown.send(());
|
||||
false
|
||||
} else {
|
||||
self.shutdown = Some(shutdown);
|
||||
true
|
||||
}
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn process_request(&mut self) -> Poll<Option<()>, ()> {
|
||||
if self.done {
|
||||
return Ok(Async::Ready(None));
|
||||
}
|
||||
if self.should_continue() {
|
||||
self.poll_shutdown_requests_and_connections()
|
||||
} else {
|
||||
self.done = true;
|
||||
Ok(Async::Ready(None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for Watcher {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
loop {
|
||||
match try!(self.process_request()) {
|
||||
Async::Ready(Some(())) => continue,
|
||||
Async::Ready(None) => return Ok(Async::Ready(())),
|
||||
Async::NotReady => return Ok(Async::NotReady),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user