Skip to content

Commit f7532b7

Browse files
committed
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.
1 parent 8153cfa commit f7532b7

File tree

14 files changed

+1042
-157
lines changed

14 files changed

+1042
-157
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ matrix:
88
env: FEATURES="--features nightly"
99
- rust: beta
1010
- rust: stable
11+
- rust: stable
12+
env: HYPER_NO_PROTO=1
1113
- rust: stable
1214
env: FEATURES="--features compat"
1315
- rust: 1.17.0

examples/client.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![deny(warnings)]
1+
//#![deny(warnings)]
22
extern crate futures;
33
extern crate hyper;
44
extern crate tokio_core;
@@ -32,7 +32,9 @@ fn main() {
3232

3333
let mut core = tokio_core::reactor::Core::new().unwrap();
3434
let handle = core.handle();
35-
let client = Client::new(&handle);
35+
let client = Client::configure()
36+
.no_proto()
37+
.build(&handle);
3638

3739
let work = client.get(url).and_then(|res| {
3840
println!("Response: {}", res.status());

examples/hello.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ impl Service for Hello {
3131
fn main() {
3232
pretty_env_logger::init().unwrap();
3333
let addr = "127.0.0.1:3000".parse().unwrap();
34-
let server = Http::new().bind(&addr, || Ok(Hello)).unwrap();
34+
let mut server = Http::new().bind(&addr, || Ok(Hello)).unwrap();
35+
server.no_proto();
3536
println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap());
3637
server.run().unwrap();
3738
}

examples/server.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ fn main() {
4747
pretty_env_logger::init().unwrap();
4848
let addr = "127.0.0.1:1337".parse().unwrap();
4949

50-
let server = Http::new().bind(&addr, || Ok(Echo)).unwrap();
50+
let mut server = Http::new().bind(&addr, || Ok(Echo)).unwrap();
51+
server.no_proto();
5152
println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap());
5253
server.run().unwrap();
5354
}

src/client/mod.rs

Lines changed: 126 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use tokio_proto::util::client_proxy::ClientProxy;
2020
pub use tokio_service::Service;
2121

2222
use header::{Headers, Host};
23-
use proto::{self, TokioBody};
23+
use proto::{self, RequestHead, TokioBody};
2424
use proto::response;
2525
use proto::request;
2626
use method::Method;
@@ -45,7 +45,7 @@ pub mod compat;
4545
pub struct Client<C, B = proto::Body> {
4646
connector: C,
4747
handle: Handle,
48-
pool: Pool<TokioClient<B>>,
48+
pool: Dispatch<B>,
4949
}
5050

5151
impl Client<HttpConnector, proto::Body> {
@@ -93,7 +93,11 @@ impl<C, B> Client<C, B> {
9393
Client {
9494
connector: config.connector,
9595
handle: handle.clone(),
96-
pool: Pool::new(config.keep_alive, config.keep_alive_timeout),
96+
pool: if config.no_proto {
97+
Dispatch::Hyper(Pool::new(config.keep_alive, config.keep_alive_timeout))
98+
} else {
99+
Dispatch::Proto(Pool::new(config.keep_alive, config.keep_alive_timeout))
100+
}
97101
}
98102
}
99103
}
@@ -187,48 +191,100 @@ where C: Connect,
187191
headers.extend(head.headers.iter());
188192
head.headers = headers;
189193

190-
let checkout = self.pool.checkout(domain.as_ref());
191-
let connect = {
192-
let handle = self.handle.clone();
193-
let pool = self.pool.clone();
194-
let pool_key = Rc::new(domain.to_string());
195-
self.connector.connect(url)
196-
.map(move |io| {
197-
let (tx, rx) = oneshot::channel();
198-
let client = HttpClient {
199-
client_rx: RefCell::new(Some(rx)),
200-
}.bind_client(&handle, io);
201-
let pooled = pool.pooled(pool_key, client);
202-
drop(tx.send(pooled.clone()));
203-
pooled
204-
})
205-
};
194+
match self.pool {
195+
Dispatch::Proto(ref pool) => {
196+
trace!("proto_dispatch");
197+
let checkout = pool.checkout(domain.as_ref());
198+
let connect = {
199+
let handle = self.handle.clone();
200+
let pool = pool.clone();
201+
let pool_key = Rc::new(domain.to_string());
202+
self.connector.connect(url)
203+
.map(move |io| {
204+
let (tx, rx) = oneshot::channel();
205+
let client = HttpClient {
206+
client_rx: RefCell::new(Some(rx)),
207+
}.bind_client(&handle, io);
208+
let pooled = pool.pooled(pool_key, client);
209+
drop(tx.send(pooled.clone()));
210+
pooled
211+
})
212+
};
213+
214+
let race = checkout.select(connect)
215+
.map(|(client, _work)| client)
216+
.map_err(|(e, _work)| {
217+
// the Pool Checkout cannot error, so the only error
218+
// is from the Connector
219+
// XXX: should wait on the Checkout? Problem is
220+
// that if the connector is failing, it may be that we
221+
// never had a pooled stream at all
222+
e.into()
223+
});
224+
let resp = race.and_then(move |client| {
225+
let msg = match body {
226+
Some(body) => {
227+
Message::WithBody(head, body.into())
228+
},
229+
None => Message::WithoutBody(head),
230+
};
231+
client.call(msg)
232+
});
233+
FutureResponse(Box::new(resp.map(|msg| {
234+
match msg {
235+
Message::WithoutBody(head) => response::from_wire(head, None),
236+
Message::WithBody(head, body) => response::from_wire(head, Some(body.into())),
237+
}
238+
})))
239+
},
240+
Dispatch::Hyper(ref pool) => {
241+
trace!("no_proto dispatch");
242+
use futures::Sink;
243+
use futures::sync::{mpsc, oneshot};
244+
245+
let checkout = pool.checkout(domain.as_ref());
246+
let connect = {
247+
let handle = self.handle.clone();
248+
let pool = pool.clone();
249+
let pool_key = Rc::new(domain.to_string());
250+
self.connector.connect(url)
251+
.map(move |io| {
252+
let (tx, rx) = mpsc::channel(1);
253+
let pooled = pool.pooled(pool_key, RefCell::new(tx));
254+
let conn = proto::Conn::<_, _, proto::ClientTransaction, _>::new(io, pooled.clone());
255+
let dispatch = proto::dispatch::Dispatcher::new(proto::dispatch::Client::new(rx), conn);
256+
handle.spawn(dispatch.map_err(|err| error!("no_proto error: {}", err)));
257+
pooled
258+
})
259+
};
260+
261+
let race = checkout.select(connect)
262+
.map(|(client, _work)| client)
263+
.map_err(|(e, _work)| {
264+
// the Pool Checkout cannot error, so the only error
265+
// is from the Connector
266+
// XXX: should wait on the Checkout? Problem is
267+
// that if the connector is failing, it may be that we
268+
// never had a pooled stream at all
269+
e.into()
270+
});
271+
272+
let resp = race.and_then(move |client| {
273+
let (callback, rx) = oneshot::channel();
274+
client.borrow_mut().start_send((head, body, callback)).unwrap();
275+
rx.then(|res| {
276+
match res {
277+
Ok(Ok(res)) => Ok(res),
278+
Ok(Err(err)) => Err(err),
279+
Err(_) => panic!("dispatch dropped without returning error"),
280+
}
281+
})
282+
});
283+
284+
FutureResponse(Box::new(resp))
206285

207-
let race = checkout.select(connect)
208-
.map(|(client, _work)| client)
209-
.map_err(|(e, _work)| {
210-
// the Pool Checkout cannot error, so the only error
211-
// is from the Connector
212-
// XXX: should wait on the Checkout? Problem is
213-
// that if the connector is failing, it may be that we
214-
// never had a pooled stream at all
215-
e.into()
216-
});
217-
let resp = race.and_then(move |client| {
218-
let msg = match body {
219-
Some(body) => {
220-
Message::WithBody(head, body.into())
221-
},
222-
None => Message::WithoutBody(head),
223-
};
224-
client.call(msg)
225-
});
226-
FutureResponse(Box::new(resp.map(|msg| {
227-
match msg {
228-
Message::WithoutBody(head) => response::from_wire(head, None),
229-
Message::WithBody(head, body) => response::from_wire(head, Some(body.into())),
230286
}
231-
})))
287+
}
232288
}
233289

234290
}
@@ -238,7 +294,10 @@ impl<C: Clone, B> Clone for Client<C, B> {
238294
Client {
239295
connector: self.connector.clone(),
240296
handle: self.handle.clone(),
241-
pool: self.pool.clone(),
297+
pool: match self.pool {
298+
Dispatch::Proto(ref pool) => Dispatch::Proto(pool.clone()),
299+
Dispatch::Hyper(ref pool) => Dispatch::Hyper(pool.clone()),
300+
}
242301
}
243302
}
244303
}
@@ -249,10 +308,16 @@ impl<C, B> fmt::Debug for Client<C, B> {
249308
}
250309
}
251310

252-
type TokioClient<B> = ClientProxy<Message<proto::RequestHead, B>, Message<proto::ResponseHead, TokioBody>, ::Error>;
311+
type ProtoClient<B> = ClientProxy<Message<RequestHead, B>, Message<proto::ResponseHead, TokioBody>, ::Error>;
312+
type HyperClient<B> = RefCell<::futures::sync::mpsc::Sender<(RequestHead, Option<B>, ::futures::sync::oneshot::Sender<::Result<::Response>>)>>;
313+
314+
enum Dispatch<B> {
315+
Proto(Pool<ProtoClient<B>>),
316+
Hyper(Pool<HyperClient<B>>),
317+
}
253318

254319
struct HttpClient<B> {
255-
client_rx: RefCell<Option<oneshot::Receiver<Pooled<TokioClient<B>>>>>,
320+
client_rx: RefCell<Option<oneshot::Receiver<Pooled<ProtoClient<B>>>>>,
256321
}
257322

258323
impl<T, B> ClientProto<T> for HttpClient<B>
@@ -265,7 +330,7 @@ where T: AsyncRead + AsyncWrite + 'static,
265330
type Response = proto::ResponseHead;
266331
type ResponseBody = proto::Chunk;
267332
type Error = ::Error;
268-
type Transport = proto::Conn<T, B::Item, proto::ClientTransaction, Pooled<TokioClient<B>>>;
333+
type Transport = proto::Conn<T, B::Item, proto::ClientTransaction, Pooled<ProtoClient<B>>>;
269334
type BindTransport = BindingClient<T, B>;
270335

271336
fn bind_transport(&self, io: T) -> Self::BindTransport {
@@ -277,7 +342,7 @@ where T: AsyncRead + AsyncWrite + 'static,
277342
}
278343

279344
struct BindingClient<T, B> {
280-
rx: oneshot::Receiver<Pooled<TokioClient<B>>>,
345+
rx: oneshot::Receiver<Pooled<ProtoClient<B>>>,
281346
io: Option<T>,
282347
}
283348

@@ -286,7 +351,7 @@ where T: AsyncRead + AsyncWrite + 'static,
286351
B: Stream<Error=::Error>,
287352
B::Item: AsRef<[u8]>,
288353
{
289-
type Item = proto::Conn<T, B::Item, proto::ClientTransaction, Pooled<TokioClient<B>>>;
354+
type Item = proto::Conn<T, B::Item, proto::ClientTransaction, Pooled<ProtoClient<B>>>;
290355
type Error = io::Error;
291356

292357
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
@@ -309,6 +374,7 @@ pub struct Config<C, B> {
309374
keep_alive_timeout: Option<Duration>,
310375
//TODO: make use of max_idle config
311376
max_idle: usize,
377+
no_proto: bool,
312378
}
313379

314380
/// Phantom type used to signal that `Config` should create a `HttpConnector`.
@@ -324,6 +390,7 @@ impl Default for Config<UseDefaultConnector, proto::Body> {
324390
keep_alive: true,
325391
keep_alive_timeout: Some(Duration::from_secs(90)),
326392
max_idle: 5,
393+
no_proto: false,
327394
}
328395
}
329396
}
@@ -347,6 +414,7 @@ impl<C, B> Config<C, B> {
347414
keep_alive: self.keep_alive,
348415
keep_alive_timeout: self.keep_alive_timeout,
349416
max_idle: self.max_idle,
417+
no_proto: self.no_proto,
350418
}
351419
}
352420

@@ -360,6 +428,7 @@ impl<C, B> Config<C, B> {
360428
keep_alive: self.keep_alive,
361429
keep_alive_timeout: self.keep_alive_timeout,
362430
max_idle: self.max_idle,
431+
no_proto: self.no_proto,
363432
}
364433
}
365434

@@ -393,6 +462,13 @@ impl<C, B> Config<C, B> {
393462
self
394463
}
395464
*/
465+
466+
/// Disable tokio-proto internal usage.
467+
#[inline]
468+
pub fn no_proto(mut self) -> Config<C, B> {
469+
self.no_proto = true;
470+
self
471+
}
396472
}
397473

398474
impl<C, B> Config<C, B>
@@ -431,11 +507,8 @@ impl<C, B> fmt::Debug for Config<C, B> {
431507
impl<C: Clone, B> Clone for Config<C, B> {
432508
fn clone(&self) -> Config<C, B> {
433509
Config {
434-
_body_type: PhantomData::<B>,
435510
connector: self.connector.clone(),
436-
keep_alive: self.keep_alive,
437-
keep_alive_timeout: self.keep_alive_timeout,
438-
max_idle: self.max_idle,
511+
.. *self
439512
}
440513
}
441514
}

src/proto/body.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::borrow::Cow;
77
use super::Chunk;
88

99
pub type TokioBody = tokio_proto::streaming::Body<Chunk, ::Error>;
10+
pub type BodySender = mpsc::Sender<Result<Chunk, ::Error>>;
1011

1112
/// A `Stream` for `Chunk`s used in requests and responses.
1213
#[must_use = "streams do nothing unless polled"]

0 commit comments

Comments
 (0)