feat(client): redesign the Connect trait

The original `Connect` trait had some limitations:

- There was no way to provide more details to the connector about how to
  connect, other than the `Uri`.
- There was no way for the connector to return any extra information
  about the connected transport.
- The `Error` was forced to be an `std::io::Error`.
- The transport and future had `'static` requirements.

As hyper gains HTTP/2 support, some of these things needed to be
changed. We want to allow the user to configure whether they hope to
us ALPN to start an HTTP/2 connection, and the connector needs to be
able to return back to hyper if it did so.

The new `Connect` trait is meant to solve this.

- The `connect` method now receives a `Destination` type, instead of a
  `Uri`. This allows us to include additional data about how to connect.
- The `Future` returned from `connect` now must be a tuple of the
  transport, and a `Connected` metadata value. The `Connected` includes
  possibly extra data about what happened when connecting.

BREAKING CHANGE: Custom connectors should now implement `Connect`
  directly, instead of `Service`.

  Calls to `connect` no longer take `Uri`s, but `Destination`. There
  are `scheme`, `host`, and `port` methods to query relevant
  information.

  The returned future must be a tuple of the transport and `Connected`.
  If no relevant extra information is needed, simply return
  `Connected::new()`.

Closes #1428
This commit is contained in:
Sean McArthur
2018-03-14 14:12:36 -07:00
parent fbc449e49c
commit 8c52c2dfd3
5 changed files with 277 additions and 110 deletions

View File

@@ -8,9 +8,8 @@ use bytes::Buf;
use futures::{Async, Poll};
use futures::task::{self, Task};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_service::Service;
use ::Uri;
use ::client::connect::{Connect, Connected, Destination};
#[derive(Debug)]
pub struct MockCursor {
@@ -410,19 +409,23 @@ impl MockConnector {
}
}
impl Service for MockConnector {
type Request = Uri;
type Response = Duplex;
impl Connect for MockConnector {
type Transport = Duplex;
type Error = io::Error;
type Future = ::futures::future::FutureResult<Self::Response, Self::Error>;
type Future = ::futures::future::FutureResult<(Self::Transport, Connected), Self::Error>;
fn call(&self, uri: Uri) -> Self::Future {
fn connect(&self, dst: Destination) -> Self::Future {
use futures::future;
trace!("mock connect: {}", uri);
trace!("mock connect: {:?}", dst);
let key = format!("{}://{}{}", dst.scheme(), dst.host(), if let Some(port) = dst.port() {
format!(":{}", port)
} else {
"".to_owned()
});
let mut mocks = self.mocks.borrow_mut();
let mocks = mocks.get_mut(&uri.to_string())
.expect(&format!("unknown mocks uri: {}", uri));
assert!(!mocks.is_empty(), "no additional mocks for {}", uri);
future::ok(mocks.remove(0))
let mocks = mocks.get_mut(&key)
.expect(&format!("unknown mocks uri: {}", key));
assert!(!mocks.is_empty(), "no additional mocks for {}", key);
future::ok((mocks.remove(0), Connected::new()))
}
}