feat(lib): add support to disable tokio-proto internals
For now, this adds `client::Config::no_proto`, `server::Http::no_proto`, and `server::Server::no_proto` to skip tokio-proto implementations, and use an internal dispatch system instead. `Http::no_proto` is similar to `Http::bind_connection`, but returns a `Connection` that is a `Future` to drive HTTP with the provided service. Any errors prior to parsing a request, and after delivering a response (but before flush the response body) will be returned from this future. See #1342 for more.
This commit is contained in:
@@ -20,7 +20,7 @@ use tokio_proto::util::client_proxy::ClientProxy;
|
||||
pub use tokio_service::Service;
|
||||
|
||||
use header::{Headers, Host};
|
||||
use proto::{self, TokioBody};
|
||||
use proto::{self, RequestHead, TokioBody};
|
||||
use proto::response;
|
||||
use proto::request;
|
||||
use method::Method;
|
||||
@@ -45,7 +45,7 @@ pub mod compat;
|
||||
pub struct Client<C, B = proto::Body> {
|
||||
connector: C,
|
||||
handle: Handle,
|
||||
pool: Pool<TokioClient<B>>,
|
||||
pool: Dispatch<B>,
|
||||
}
|
||||
|
||||
impl Client<HttpConnector, proto::Body> {
|
||||
@@ -93,7 +93,11 @@ impl<C, B> Client<C, B> {
|
||||
Client {
|
||||
connector: config.connector,
|
||||
handle: handle.clone(),
|
||||
pool: Pool::new(config.keep_alive, config.keep_alive_timeout),
|
||||
pool: if config.no_proto {
|
||||
Dispatch::Hyper(Pool::new(config.keep_alive, config.keep_alive_timeout))
|
||||
} else {
|
||||
Dispatch::Proto(Pool::new(config.keep_alive, config.keep_alive_timeout))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,48 +191,100 @@ where C: Connect,
|
||||
headers.extend(head.headers.iter());
|
||||
head.headers = headers;
|
||||
|
||||
let checkout = self.pool.checkout(domain.as_ref());
|
||||
let connect = {
|
||||
let handle = self.handle.clone();
|
||||
let pool = self.pool.clone();
|
||||
let pool_key = Rc::new(domain.to_string());
|
||||
self.connector.connect(url)
|
||||
.map(move |io| {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let client = HttpClient {
|
||||
client_rx: RefCell::new(Some(rx)),
|
||||
}.bind_client(&handle, io);
|
||||
let pooled = pool.pooled(pool_key, client);
|
||||
drop(tx.send(pooled.clone()));
|
||||
pooled
|
||||
})
|
||||
};
|
||||
match self.pool {
|
||||
Dispatch::Proto(ref pool) => {
|
||||
trace!("proto_dispatch");
|
||||
let checkout = pool.checkout(domain.as_ref());
|
||||
let connect = {
|
||||
let handle = self.handle.clone();
|
||||
let pool = pool.clone();
|
||||
let pool_key = Rc::new(domain.to_string());
|
||||
self.connector.connect(url)
|
||||
.map(move |io| {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let client = HttpClient {
|
||||
client_rx: RefCell::new(Some(rx)),
|
||||
}.bind_client(&handle, io);
|
||||
let pooled = pool.pooled(pool_key, client);
|
||||
drop(tx.send(pooled.clone()));
|
||||
pooled
|
||||
})
|
||||
};
|
||||
|
||||
let race = checkout.select(connect)
|
||||
.map(|(client, _work)| client)
|
||||
.map_err(|(e, _work)| {
|
||||
// the Pool Checkout cannot error, so the only error
|
||||
// is from the Connector
|
||||
// XXX: should wait on the Checkout? Problem is
|
||||
// that if the connector is failing, it may be that we
|
||||
// never had a pooled stream at all
|
||||
e.into()
|
||||
});
|
||||
let resp = race.and_then(move |client| {
|
||||
let msg = match body {
|
||||
Some(body) => {
|
||||
Message::WithBody(head, body.into())
|
||||
},
|
||||
None => Message::WithoutBody(head),
|
||||
};
|
||||
client.call(msg)
|
||||
});
|
||||
FutureResponse(Box::new(resp.map(|msg| {
|
||||
match msg {
|
||||
Message::WithoutBody(head) => response::from_wire(head, None),
|
||||
Message::WithBody(head, body) => response::from_wire(head, Some(body.into())),
|
||||
}
|
||||
})))
|
||||
},
|
||||
Dispatch::Hyper(ref pool) => {
|
||||
trace!("no_proto dispatch");
|
||||
use futures::Sink;
|
||||
use futures::sync::{mpsc, oneshot};
|
||||
|
||||
let checkout = pool.checkout(domain.as_ref());
|
||||
let connect = {
|
||||
let handle = self.handle.clone();
|
||||
let pool = pool.clone();
|
||||
let pool_key = Rc::new(domain.to_string());
|
||||
self.connector.connect(url)
|
||||
.map(move |io| {
|
||||
let (tx, rx) = mpsc::channel(1);
|
||||
let pooled = pool.pooled(pool_key, RefCell::new(tx));
|
||||
let conn = proto::Conn::<_, _, proto::ClientTransaction, _>::new(io, pooled.clone());
|
||||
let dispatch = proto::dispatch::Dispatcher::new(proto::dispatch::Client::new(rx), conn);
|
||||
handle.spawn(dispatch.map_err(|err| error!("no_proto error: {}", err)));
|
||||
pooled
|
||||
})
|
||||
};
|
||||
|
||||
let race = checkout.select(connect)
|
||||
.map(|(client, _work)| client)
|
||||
.map_err(|(e, _work)| {
|
||||
// the Pool Checkout cannot error, so the only error
|
||||
// is from the Connector
|
||||
// XXX: should wait on the Checkout? Problem is
|
||||
// that if the connector is failing, it may be that we
|
||||
// never had a pooled stream at all
|
||||
e.into()
|
||||
});
|
||||
|
||||
let resp = race.and_then(move |client| {
|
||||
let (callback, rx) = oneshot::channel();
|
||||
client.borrow_mut().start_send((head, body, callback)).unwrap();
|
||||
rx.then(|res| {
|
||||
match res {
|
||||
Ok(Ok(res)) => Ok(res),
|
||||
Ok(Err(err)) => Err(err),
|
||||
Err(_) => panic!("dispatch dropped without returning error"),
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
FutureResponse(Box::new(resp))
|
||||
|
||||
let race = checkout.select(connect)
|
||||
.map(|(client, _work)| client)
|
||||
.map_err(|(e, _work)| {
|
||||
// the Pool Checkout cannot error, so the only error
|
||||
// is from the Connector
|
||||
// XXX: should wait on the Checkout? Problem is
|
||||
// that if the connector is failing, it may be that we
|
||||
// never had a pooled stream at all
|
||||
e.into()
|
||||
});
|
||||
let resp = race.and_then(move |client| {
|
||||
let msg = match body {
|
||||
Some(body) => {
|
||||
Message::WithBody(head, body.into())
|
||||
},
|
||||
None => Message::WithoutBody(head),
|
||||
};
|
||||
client.call(msg)
|
||||
});
|
||||
FutureResponse(Box::new(resp.map(|msg| {
|
||||
match msg {
|
||||
Message::WithoutBody(head) => response::from_wire(head, None),
|
||||
Message::WithBody(head, body) => response::from_wire(head, Some(body.into())),
|
||||
}
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -238,7 +294,10 @@ impl<C: Clone, B> Clone for Client<C, B> {
|
||||
Client {
|
||||
connector: self.connector.clone(),
|
||||
handle: self.handle.clone(),
|
||||
pool: self.pool.clone(),
|
||||
pool: match self.pool {
|
||||
Dispatch::Proto(ref pool) => Dispatch::Proto(pool.clone()),
|
||||
Dispatch::Hyper(ref pool) => Dispatch::Hyper(pool.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,10 +308,16 @@ impl<C, B> fmt::Debug for Client<C, B> {
|
||||
}
|
||||
}
|
||||
|
||||
type TokioClient<B> = ClientProxy<Message<proto::RequestHead, B>, Message<proto::ResponseHead, TokioBody>, ::Error>;
|
||||
type ProtoClient<B> = ClientProxy<Message<RequestHead, B>, Message<proto::ResponseHead, TokioBody>, ::Error>;
|
||||
type HyperClient<B> = RefCell<::futures::sync::mpsc::Sender<(RequestHead, Option<B>, ::futures::sync::oneshot::Sender<::Result<::Response>>)>>;
|
||||
|
||||
enum Dispatch<B> {
|
||||
Proto(Pool<ProtoClient<B>>),
|
||||
Hyper(Pool<HyperClient<B>>),
|
||||
}
|
||||
|
||||
struct HttpClient<B> {
|
||||
client_rx: RefCell<Option<oneshot::Receiver<Pooled<TokioClient<B>>>>>,
|
||||
client_rx: RefCell<Option<oneshot::Receiver<Pooled<ProtoClient<B>>>>>,
|
||||
}
|
||||
|
||||
impl<T, B> ClientProto<T> for HttpClient<B>
|
||||
@@ -265,7 +330,7 @@ where T: AsyncRead + AsyncWrite + 'static,
|
||||
type Response = proto::ResponseHead;
|
||||
type ResponseBody = proto::Chunk;
|
||||
type Error = ::Error;
|
||||
type Transport = proto::Conn<T, B::Item, proto::ClientTransaction, Pooled<TokioClient<B>>>;
|
||||
type Transport = proto::Conn<T, B::Item, proto::ClientTransaction, Pooled<ProtoClient<B>>>;
|
||||
type BindTransport = BindingClient<T, B>;
|
||||
|
||||
fn bind_transport(&self, io: T) -> Self::BindTransport {
|
||||
@@ -277,7 +342,7 @@ where T: AsyncRead + AsyncWrite + 'static,
|
||||
}
|
||||
|
||||
struct BindingClient<T, B> {
|
||||
rx: oneshot::Receiver<Pooled<TokioClient<B>>>,
|
||||
rx: oneshot::Receiver<Pooled<ProtoClient<B>>>,
|
||||
io: Option<T>,
|
||||
}
|
||||
|
||||
@@ -286,7 +351,7 @@ where T: AsyncRead + AsyncWrite + 'static,
|
||||
B: Stream<Error=::Error>,
|
||||
B::Item: AsRef<[u8]>,
|
||||
{
|
||||
type Item = proto::Conn<T, B::Item, proto::ClientTransaction, Pooled<TokioClient<B>>>;
|
||||
type Item = proto::Conn<T, B::Item, proto::ClientTransaction, Pooled<ProtoClient<B>>>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
@@ -309,6 +374,7 @@ pub struct Config<C, B> {
|
||||
keep_alive_timeout: Option<Duration>,
|
||||
//TODO: make use of max_idle config
|
||||
max_idle: usize,
|
||||
no_proto: bool,
|
||||
}
|
||||
|
||||
/// Phantom type used to signal that `Config` should create a `HttpConnector`.
|
||||
@@ -324,6 +390,7 @@ impl Default for Config<UseDefaultConnector, proto::Body> {
|
||||
keep_alive: true,
|
||||
keep_alive_timeout: Some(Duration::from_secs(90)),
|
||||
max_idle: 5,
|
||||
no_proto: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,6 +414,7 @@ impl<C, B> Config<C, B> {
|
||||
keep_alive: self.keep_alive,
|
||||
keep_alive_timeout: self.keep_alive_timeout,
|
||||
max_idle: self.max_idle,
|
||||
no_proto: self.no_proto,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +428,7 @@ impl<C, B> Config<C, B> {
|
||||
keep_alive: self.keep_alive,
|
||||
keep_alive_timeout: self.keep_alive_timeout,
|
||||
max_idle: self.max_idle,
|
||||
no_proto: self.no_proto,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,6 +462,13 @@ impl<C, B> Config<C, B> {
|
||||
self
|
||||
}
|
||||
*/
|
||||
|
||||
/// Disable tokio-proto internal usage.
|
||||
#[inline]
|
||||
pub fn no_proto(mut self) -> Config<C, B> {
|
||||
self.no_proto = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, B> Config<C, B>
|
||||
@@ -431,11 +507,8 @@ impl<C, B> fmt::Debug for Config<C, B> {
|
||||
impl<C: Clone, B> Clone for Config<C, B> {
|
||||
fn clone(&self) -> Config<C, B> {
|
||||
Config {
|
||||
_body_type: PhantomData::<B>,
|
||||
connector: self.connector.clone(),
|
||||
keep_alive: self.keep_alive,
|
||||
keep_alive_timeout: self.keep_alive_timeout,
|
||||
max_idle: self.max_idle,
|
||||
.. *self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user