feat(client): adds HttpInfo to responses when HttpConnector is used

- Adds `client::connect::Connected::extra()`, which allows connectors to
  specify arbitrary custom information about a connected transport.

If a connector provides this extra value, it will be set in the
`Response` extensions.

Closes #1402
This commit is contained in:
Sean McArthur
2018-10-16 13:19:48 -07:00
parent d55b5efb89
commit 13d53e1d0c
5 changed files with 145 additions and 13 deletions

View File

@@ -6,16 +6,16 @@
//! establishes connections over TCP.
//! - The [`Connect`](Connect) trait and related types to build custom connectors.
use std::error::Error as StdError;
use std::mem;
use std::{fmt, mem};
use bytes::{BufMut, Bytes, BytesMut};
use futures::Future;
use http::{uri, Uri};
use http::{uri, Response, Uri};
use tokio_io::{AsyncRead, AsyncWrite};
#[cfg(feature = "runtime")] mod dns;
#[cfg(feature = "runtime")] mod http;
#[cfg(feature = "runtime")] pub use self::http::HttpConnector;
#[cfg(feature = "runtime")] pub use self::http::{HttpConnector, HttpInfo};
/// Connect to a destination, returning an IO transport.
///
@@ -48,8 +48,11 @@ pub struct Destination {
pub struct Connected {
//alpn: Alpn,
pub(super) is_proxied: bool,
pub(super) extra: Option<Extra>,
}
pub(super) struct Extra(Box<ExtraInner>);
/*TODO: when HTTP1 Upgrades to H2 are added, this will be needed
#[derive(Debug)]
pub(super) enum Alpn {
@@ -245,6 +248,7 @@ impl Connected {
Connected {
//alpn: Alpn::Http1,
is_proxied: false,
extra: None,
}
}
@@ -260,6 +264,12 @@ impl Connected {
self
}
/// Set extra connection information to be set in the extensions of every `Response`.
pub fn extra<T: Clone + Send + Sync + 'static>(mut self, extra: T) -> Connected {
self.extra = Some(Extra(Box::new(ExtraEnvelope(extra))));
self
}
/*
/// Set that the connected transport negotiated HTTP/2 as it's
/// next protocol.
@@ -268,6 +278,61 @@ impl Connected {
self
}
*/
// Don't public expose that `Connected` is `Clone`, unsure if we want to
// keep that contract...
pub(super) fn clone(&self) -> Connected {
Connected {
is_proxied: self.is_proxied,
extra: self.extra.clone(),
}
}
}
// ===== impl Extra =====
impl Extra {
pub(super) fn set(&self, res: &mut Response<::Body>) {
self.0.set(res);
}
}
impl Clone for Extra {
fn clone(&self) -> Extra {
Extra(self.0.clone_box())
}
}
impl fmt::Debug for Extra {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Extra")
.finish()
}
}
// This indirection allows the `Connected` to have a type-erased "extra" value,
// while that type still knows its inner extra type. This allows the correct
// TypeId to be used when inserting into `res.extensions_mut()`.
#[derive(Clone)]
struct ExtraEnvelope<T>(T);
trait ExtraInner: Send + Sync {
fn clone_box(&self) -> Box<ExtraInner>;
fn set(&self, res: &mut Response<::Body>);
}
impl<T> ExtraInner for ExtraEnvelope<T>
where
T: Clone + Send + Sync + 'static
{
fn clone_box(&self) -> Box<ExtraInner> {
Box::new(self.clone())
}
fn set(&self, res: &mut Response<::Body>) {
let extra = self.0.clone();
res.extensions_mut().insert(extra);
}
}
#[cfg(test)]