1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 16:02:59 +01:00

define generic client Connection trait

This commit is contained in:
Nikolay Kim 2018-11-15 11:10:23 -08:00
parent acd42f92d8
commit 6d9733cdf7
5 changed files with 129 additions and 81 deletions

View File

@ -5,14 +5,23 @@ use tokio_io::{AsyncRead, AsyncWrite};
use super::pool::Acquired; use super::pool::Acquired;
pub trait Connection: AsyncRead + AsyncWrite + 'static {
/// Close connection
fn close(&mut self);
/// Release connection to the connection pool
fn release(&mut self);
}
#[doc(hidden)]
/// HTTP client connection /// HTTP client connection
pub struct Connection<T> { pub struct IoConnection<T> {
io: T, io: Option<T>,
created: time::Instant, created: time::Instant,
pool: Option<Acquired<T>>, pool: Option<Acquired<T>>,
} }
impl<T> fmt::Debug for Connection<T> impl<T> fmt::Debug for IoConnection<T>
where where
T: fmt::Debug, T: fmt::Debug,
{ {
@ -21,59 +30,73 @@ where
} }
} }
impl<T: AsyncRead + AsyncWrite + 'static> Connection<T> { impl<T: AsyncRead + AsyncWrite + 'static> IoConnection<T> {
pub(crate) fn new(io: T, created: time::Instant, pool: Acquired<T>) -> Self { pub(crate) fn new(io: T, created: time::Instant, pool: Acquired<T>) -> Self {
Connection { IoConnection {
io,
created, created,
io: Some(io),
pool: Some(pool), pool: Some(pool),
} }
} }
/// Raw IO stream /// Raw IO stream
pub fn get_mut(&mut self) -> &mut T { pub fn get_mut(&mut self) -> &mut T {
&mut self.io self.io.as_mut().unwrap()
} }
pub(crate) fn into_inner(self) -> (T, time::Instant) {
(self.io.unwrap(), self.created)
}
}
impl<T: AsyncRead + AsyncWrite + 'static> Connection for IoConnection<T> {
/// Close connection /// Close connection
pub fn close(mut self) { fn close(&mut self) {
if let Some(mut pool) = self.pool.take() { if let Some(mut pool) = self.pool.take() {
pool.close(self) if let Some(io) = self.io.take() {
pool.close(IoConnection {
io: Some(io),
created: self.created,
pool: None,
})
}
} }
} }
/// Release this connection to the connection pool /// Release this connection to the connection pool
pub fn release(mut self) { fn release(&mut self) {
if let Some(mut pool) = self.pool.take() { if let Some(mut pool) = self.pool.take() {
pool.release(self) if let Some(io) = self.io.take() {
pool.release(IoConnection {
io: Some(io),
created: self.created,
pool: None,
})
} }
} }
pub(crate) fn into_inner(self) -> (T, time::Instant) {
(self.io, self.created)
} }
} }
impl<T: AsyncRead + AsyncWrite + 'static> io::Read for Connection<T> { impl<T: AsyncRead + AsyncWrite + 'static> io::Read for IoConnection<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.io.read(buf) self.io.as_mut().unwrap().read(buf)
} }
} }
impl<T: AsyncRead + AsyncWrite + 'static> AsyncRead for Connection<T> {} impl<T: AsyncRead + AsyncWrite + 'static> AsyncRead for IoConnection<T> {}
impl<T: AsyncRead + AsyncWrite + 'static> io::Write for Connection<T> { impl<T: AsyncRead + AsyncWrite + 'static> io::Write for IoConnection<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.io.write(buf) self.io.as_mut().unwrap().write(buf)
} }
fn flush(&mut self) -> io::Result<()> { fn flush(&mut self) -> io::Result<()> {
self.io.flush() self.io.as_mut().unwrap().flush()
} }
} }
impl<T: AsyncRead + AsyncWrite + 'static> AsyncWrite for Connection<T> { impl<T: AsyncRead + AsyncWrite + 'static> AsyncWrite for IoConnection<T> {
fn shutdown(&mut self) -> Poll<(), io::Error> { fn shutdown(&mut self) -> Poll<(), io::Error> {
self.io.shutdown() self.io.as_mut().unwrap().shutdown()
} }
} }

View File

@ -11,7 +11,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
use super::connect::Connect; use super::connect::Connect;
use super::connection::Connection; use super::connection::{Connection, IoConnection};
use super::error::ConnectorError; use super::error::ConnectorError;
use super::pool::ConnectionPool; use super::pool::ConnectionPool;
@ -130,7 +130,7 @@ impl Connector {
self, self,
) -> impl Service< ) -> impl Service<
Request = Connect, Request = Connect,
Response = Connection<impl AsyncRead + AsyncWrite + fmt::Debug>, Response = impl Connection,
Error = ConnectorError, Error = ConnectorError,
> + Clone { > + Clone {
#[cfg(not(feature = "ssl"))] #[cfg(not(feature = "ssl"))]
@ -234,11 +234,11 @@ mod connect_impl {
T: Service<Request = Connect, Response = (Connect, Io), Error = ConnectorError>, T: Service<Request = Connect, Response = (Connect, Io), Error = ConnectorError>,
{ {
type Request = Connect; type Request = Connect;
type Response = Connection<Io>; type Response = IoConnection<Io>;
type Error = ConnectorError; type Error = ConnectorError;
type Future = Either< type Future = Either<
<ConnectionPool<T, Io> as Service>::Future, <ConnectionPool<T, Io> as Service>::Future,
FutureResult<Connection<Io>, ConnectorError>, FutureResult<IoConnection<Io>, ConnectorError>,
>; >;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
@ -324,7 +324,7 @@ mod connect_impl {
>, >,
{ {
type Request = Connect; type Request = Connect;
type Response = IoEither<Connection<Io1>, Connection<Io2>>; type Response = IoEither<IoConnection<Io1>, IoConnection<Io2>>;
type Error = ConnectorError; type Error = ConnectorError;
type Future = Either< type Future = Either<
FutureResult<Self::Response, Self::Error>, FutureResult<Self::Response, Self::Error>,
@ -342,13 +342,13 @@ mod connect_impl {
if let Err(e) = req.validate() { if let Err(e) = req.validate() {
Either::A(err(e)) Either::A(err(e))
} else if req.is_secure() { } else if req.is_secure() {
Either::B(Either::A(InnerConnectorResponseA { Either::B(Either::B(InnerConnectorResponseB {
fut: self.tcp_pool.call(req), fut: self.ssl_pool.call(req),
_t: PhantomData, _t: PhantomData,
})) }))
} else { } else {
Either::B(Either::B(InnerConnectorResponseB { Either::B(Either::A(InnerConnectorResponseA {
fut: self.ssl_pool.call(req), fut: self.tcp_pool.call(req),
_t: PhantomData, _t: PhantomData,
})) }))
} }
@ -370,7 +370,7 @@ mod connect_impl {
Io1: AsyncRead + AsyncWrite + 'static, Io1: AsyncRead + AsyncWrite + 'static,
Io2: AsyncRead + AsyncWrite + 'static, Io2: AsyncRead + AsyncWrite + 'static,
{ {
type Item = IoEither<Connection<Io1>, Connection<Io2>>; type Item = IoEither<IoConnection<Io1>, IoConnection<Io2>>;
type Error = ConnectorError; type Error = ConnectorError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
@ -396,7 +396,7 @@ mod connect_impl {
Io1: AsyncRead + AsyncWrite + 'static, Io1: AsyncRead + AsyncWrite + 'static,
Io2: AsyncRead + AsyncWrite + 'static, Io2: AsyncRead + AsyncWrite + 'static,
{ {
type Item = IoEither<Connection<Io1>, Connection<Io2>>; type Item = IoEither<IoConnection<Io1>, IoConnection<Io2>>;
type Error = ConnectorError; type Error = ConnectorError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
@ -413,10 +413,30 @@ pub(crate) enum IoEither<Io1, Io2> {
B(Io2), B(Io2),
} }
impl<Io1, Io2> Connection for IoEither<Io1, Io2>
where
Io1: Connection,
Io2: Connection,
{
fn close(&mut self) {
match self {
IoEither::A(ref mut io) => io.close(),
IoEither::B(ref mut io) => io.close(),
}
}
fn release(&mut self) {
match self {
IoEither::A(ref mut io) => io.release(),
IoEither::B(ref mut io) => io.release(),
}
}
}
impl<Io1, Io2> io::Read for IoEither<Io1, Io2> impl<Io1, Io2> io::Read for IoEither<Io1, Io2>
where where
Io1: io::Read, Io1: Connection,
Io2: io::Read, Io2: Connection,
{ {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self { match self {
@ -428,8 +448,8 @@ where
impl<Io1, Io2> AsyncRead for IoEither<Io1, Io2> impl<Io1, Io2> AsyncRead for IoEither<Io1, Io2>
where where
Io1: AsyncRead, Io1: Connection,
Io2: AsyncRead, Io2: Connection,
{ {
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
match self { match self {
@ -441,8 +461,8 @@ where
impl<Io1, Io2> AsyncWrite for IoEither<Io1, Io2> impl<Io1, Io2> AsyncWrite for IoEither<Io1, Io2>
where where
Io1: AsyncWrite, Io1: Connection,
Io2: AsyncWrite, Io2: Connection,
{ {
fn shutdown(&mut self) -> Poll<(), io::Error> { fn shutdown(&mut self) -> Poll<(), io::Error> {
match self { match self {
@ -468,8 +488,8 @@ where
impl<Io1, Io2> io::Write for IoEither<Io1, Io2> impl<Io1, Io2> io::Write for IoEither<Io1, Io2>
where where
Io1: io::Write, Io1: Connection,
Io2: io::Write, Io2: Connection,
{ {
fn flush(&mut self) -> io::Result<()> { fn flush(&mut self) -> io::Result<()> {
match self { match self {

View File

@ -15,27 +15,32 @@ use body::{BodyType, MessageBody, PayloadStream};
use error::PayloadError; use error::PayloadError;
use h1; use h1;
pub fn send_request<T, Io, B>( pub(crate) fn send_request<T, I, B>(
head: RequestHead, head: RequestHead,
body: B, body: B,
connector: &mut T, connector: &mut T,
) -> impl Future<Item = ClientResponse, Error = SendRequestError> ) -> impl Future<Item = ClientResponse, Error = SendRequestError>
where where
T: Service<Request = Connect, Response = Connection<Io>, Error = ConnectorError>, T: Service<Request = Connect, Response = I, Error = ConnectorError>,
B: MessageBody, B: MessageBody,
Io: AsyncRead + AsyncWrite + 'static, I: Connection,
{ {
let tp = body.tp(); let tp = body.tp();
connector connector
// connect to the host
.call(Connect::new(head.uri.clone())) .call(Connect::new(head.uri.clone()))
.from_err() .from_err()
// create Framed and send reqest
.map(|io| Framed::new(io, h1::ClientCodec::default())) .map(|io| Framed::new(io, h1::ClientCodec::default()))
.and_then(|framed| framed.send((head, tp).into()).from_err()) .and_then(|framed| framed.send((head, tp).into()).from_err())
// send request body
.and_then(move |framed| match body.tp() { .and_then(move |framed| match body.tp() {
BodyType::None | BodyType::Zero => Either::A(ok(framed)), BodyType::None | BodyType::Zero => Either::A(ok(framed)),
_ => Either::B(SendBody::new(body, framed)), _ => Either::B(SendBody::new(body, framed)),
}).and_then(|framed| { })
// read response and init read body
.and_then(|framed| {
framed framed
.into_future() .into_future()
.map_err(|(e, _)| SendRequestError::from(e)) .map_err(|(e, _)| SendRequestError::from(e))
@ -55,19 +60,20 @@ where
}) })
} }
struct SendBody<Io, B> { /// Future responsible for sending request body to the peer
struct SendBody<I, B> {
body: Option<B>, body: Option<B>,
framed: Option<Framed<Connection<Io>, h1::ClientCodec>>, framed: Option<Framed<I, h1::ClientCodec>>,
write_buf: VecDeque<h1::Message<(RequestHead, BodyType)>>, write_buf: VecDeque<h1::Message<(RequestHead, BodyType)>>,
flushed: bool, flushed: bool,
} }
impl<Io, B> SendBody<Io, B> impl<I, B> SendBody<I, B>
where where
Io: AsyncRead + AsyncWrite + 'static, I: AsyncRead + AsyncWrite + 'static,
B: MessageBody, B: MessageBody,
{ {
fn new(body: B, framed: Framed<Connection<Io>, h1::ClientCodec>) -> Self { fn new(body: B, framed: Framed<I, h1::ClientCodec>) -> Self {
SendBody { SendBody {
body: Some(body), body: Some(body),
framed: Some(framed), framed: Some(framed),
@ -77,12 +83,12 @@ where
} }
} }
impl<Io, B> Future for SendBody<Io, B> impl<I, B> Future for SendBody<I, B>
where where
Io: AsyncRead + AsyncWrite + 'static, I: Connection,
B: MessageBody, B: MessageBody,
{ {
type Item = Framed<Connection<Io>, h1::ClientCodec>; type Item = Framed<I, h1::ClientCodec>;
type Error = SendRequestError; type Error = SendRequestError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
@ -135,7 +141,7 @@ impl Stream for EmptyPayload {
} }
pub(crate) struct Payload<Io> { pub(crate) struct Payload<Io> {
framed: Option<Framed<Connection<Io>, h1::ClientPayloadCodec>>, framed: Option<Framed<Io, h1::ClientPayloadCodec>>,
} }
impl Payload<()> { impl Payload<()> {
@ -144,15 +150,15 @@ impl Payload<()> {
} }
} }
impl<Io: AsyncRead + AsyncWrite + 'static> Payload<Io> { impl<Io: Connection> Payload<Io> {
fn stream(framed: Framed<Connection<Io>, h1::ClientCodec>) -> PayloadStream { fn stream(framed: Framed<Io, h1::ClientCodec>) -> PayloadStream {
Box::new(Payload { Box::new(Payload {
framed: Some(framed.map_codec(|codec| codec.into_payload_codec())), framed: Some(framed.map_codec(|codec| codec.into_payload_codec())),
}) })
} }
} }
impl<Io: AsyncRead + AsyncWrite + 'static> Stream for Payload<Io> { impl<Io: Connection> Stream for Payload<Io> {
type Item = Bytes; type Item = Bytes;
type Error = PayloadError; type Error = PayloadError;
@ -170,11 +176,11 @@ impl<Io: AsyncRead + AsyncWrite + 'static> Stream for Payload<Io> {
} }
} }
fn release_connection<T, U>(framed: Framed<Connection<T>, U>) fn release_connection<T, U>(framed: Framed<T, U>)
where where
T: AsyncRead + AsyncWrite + 'static, T: Connection,
{ {
let parts = framed.into_parts(); let mut parts = framed.into_parts();
if parts.read_buf.is_empty() && parts.write_buf.is_empty() { if parts.read_buf.is_empty() && parts.write_buf.is_empty() {
parts.io.release() parts.io.release()
} else { } else {

View File

@ -17,7 +17,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
use tokio_timer::{sleep, Delay}; use tokio_timer::{sleep, Delay};
use super::connect::Connect; use super::connect::Connect;
use super::connection::Connection; use super::connection::IoConnection;
use super::error::ConnectorError; use super::error::ConnectorError;
#[derive(Hash, Eq, PartialEq, Clone, Debug)] #[derive(Hash, Eq, PartialEq, Clone, Debug)]
@ -89,10 +89,10 @@ where
T: Service<Request = Connect, Response = (Connect, Io), Error = ConnectorError>, T: Service<Request = Connect, Response = (Connect, Io), Error = ConnectorError>,
{ {
type Request = Connect; type Request = Connect;
type Response = Connection<Io>; type Response = IoConnection<Io>;
type Error = ConnectorError; type Error = ConnectorError;
type Future = Either< type Future = Either<
FutureResult<Connection<Io>, ConnectorError>, FutureResult<IoConnection<Io>, ConnectorError>,
Either<WaitForConnection<Io>, OpenConnection<T::Future, Io>>, Either<WaitForConnection<Io>, OpenConnection<T::Future, Io>>,
>; >;
@ -107,7 +107,7 @@ where
match self.1.as_ref().borrow_mut().acquire(&key) { match self.1.as_ref().borrow_mut().acquire(&key) {
Acquire::Acquired(io, created) => { Acquire::Acquired(io, created) => {
// use existing connection // use existing connection
Either::A(ok(Connection::new( Either::A(ok(IoConnection::new(
io, io,
created, created,
Acquired(key, Some(self.1.clone())), Acquired(key, Some(self.1.clone())),
@ -142,7 +142,7 @@ where
{ {
key: Key, key: Key,
token: usize, token: usize,
rx: oneshot::Receiver<Result<Connection<Io>, ConnectorError>>, rx: oneshot::Receiver<Result<IoConnection<Io>, ConnectorError>>,
inner: Option<Rc<RefCell<Inner<Io>>>>, inner: Option<Rc<RefCell<Inner<Io>>>>,
} }
@ -163,7 +163,7 @@ impl<Io> Future for WaitForConnection<Io>
where where
Io: AsyncRead + AsyncWrite, Io: AsyncRead + AsyncWrite,
{ {
type Item = Connection<Io>; type Item = IoConnection<Io>;
type Error = ConnectorError; type Error = ConnectorError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
@ -226,7 +226,7 @@ where
F: Future<Item = (Connect, Io), Error = ConnectorError>, F: Future<Item = (Connect, Io), Error = ConnectorError>,
Io: AsyncRead + AsyncWrite, Io: AsyncRead + AsyncWrite,
{ {
type Item = Connection<Io>; type Item = IoConnection<Io>;
type Error = ConnectorError; type Error = ConnectorError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
@ -234,7 +234,7 @@ where
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
Ok(Async::Ready((_, io))) => { Ok(Async::Ready((_, io))) => {
let _ = self.inner.take(); let _ = self.inner.take();
Ok(Async::Ready(Connection::new( Ok(Async::Ready(IoConnection::new(
io, io,
Instant::now(), Instant::now(),
Acquired(self.key.clone(), self.inner.clone()), Acquired(self.key.clone(), self.inner.clone()),
@ -251,7 +251,7 @@ where
{ {
fut: F, fut: F,
key: Key, key: Key,
rx: Option<oneshot::Sender<Result<Connection<Io>, ConnectorError>>>, rx: Option<oneshot::Sender<Result<IoConnection<Io>, ConnectorError>>>,
inner: Option<Rc<RefCell<Inner<Io>>>>, inner: Option<Rc<RefCell<Inner<Io>>>>,
} }
@ -262,7 +262,7 @@ where
{ {
fn spawn( fn spawn(
key: Key, key: Key,
rx: oneshot::Sender<Result<Connection<Io>, ConnectorError>>, rx: oneshot::Sender<Result<IoConnection<Io>, ConnectorError>>,
inner: Rc<RefCell<Inner<Io>>>, inner: Rc<RefCell<Inner<Io>>>,
fut: F, fut: F,
) { ) {
@ -308,7 +308,7 @@ where
Ok(Async::Ready((_, io))) => { Ok(Async::Ready((_, io))) => {
let _ = self.inner.take(); let _ = self.inner.take();
if let Some(rx) = self.rx.take() { if let Some(rx) = self.rx.take() {
let _ = rx.send(Ok(Connection::new( let _ = rx.send(Ok(IoConnection::new(
io, io,
Instant::now(), Instant::now(),
Acquired(self.key.clone(), self.inner.clone()), Acquired(self.key.clone(), self.inner.clone()),
@ -336,7 +336,7 @@ pub(crate) struct Inner<Io> {
available: HashMap<Key, VecDeque<AvailableConnection<Io>>>, available: HashMap<Key, VecDeque<AvailableConnection<Io>>>,
waiters: Slab<( waiters: Slab<(
Connect, Connect,
oneshot::Sender<Result<Connection<Io>, ConnectorError>>, oneshot::Sender<Result<IoConnection<Io>, ConnectorError>>,
)>, )>,
waiters_queue: IndexSet<(Key, usize)>, waiters_queue: IndexSet<(Key, usize)>,
task: AtomicTask, task: AtomicTask,
@ -378,7 +378,7 @@ where
&mut self, &mut self,
connect: Connect, connect: Connect,
) -> ( ) -> (
oneshot::Receiver<Result<Connection<Io>, ConnectorError>>, oneshot::Receiver<Result<IoConnection<Io>, ConnectorError>>,
usize, usize,
) { ) {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
@ -479,7 +479,7 @@ where
Acquire::NotAvailable => break, Acquire::NotAvailable => break,
Acquire::Acquired(io, created) => { Acquire::Acquired(io, created) => {
let (_, tx) = inner.waiters.remove(token); let (_, tx) = inner.waiters.remove(token);
if let Err(conn) = tx.send(Ok(Connection::new( if let Err(conn) = tx.send(Ok(IoConnection::new(
io, io,
created, created,
Acquired(key.clone(), Some(self.inner.clone())), Acquired(key.clone(), Some(self.inner.clone())),
@ -546,13 +546,13 @@ impl<T> Acquired<T>
where where
T: AsyncRead + AsyncWrite + 'static, T: AsyncRead + AsyncWrite + 'static,
{ {
pub(crate) fn close(&mut self, conn: Connection<T>) { pub(crate) fn close(&mut self, conn: IoConnection<T>) {
if let Some(inner) = self.1.take() { if let Some(inner) = self.1.take() {
let (io, _) = conn.into_inner(); let (io, _) = conn.into_inner();
inner.as_ref().borrow_mut().release_close(io); inner.as_ref().borrow_mut().release_close(io);
} }
} }
pub(crate) fn release(&mut self, conn: Connection<T>) { pub(crate) fn release(&mut self, conn: IoConnection<T>) {
if let Some(inner) = self.1.take() { if let Some(inner) = self.1.take() {
let (io, created) = conn.into_inner(); let (io, created) = conn.into_inner();
inner inner

View File

@ -7,7 +7,6 @@ use bytes::{BufMut, Bytes, BytesMut};
use cookie::{Cookie, CookieJar}; use cookie::{Cookie, CookieJar};
use futures::{Future, Stream}; use futures::{Future, Stream};
use percent_encoding::{percent_encode, USERINFO_ENCODE_SET}; use percent_encoding::{percent_encode, USERINFO_ENCODE_SET};
use tokio_io::{AsyncRead, AsyncWrite};
use urlcrate::Url; use urlcrate::Url;
use body::{MessageBody, MessageBodyStream}; use body::{MessageBody, MessageBodyStream};
@ -176,13 +175,13 @@ where
// Send request // Send request
/// ///
/// This method returns a future that resolves to a ClientResponse /// This method returns a future that resolves to a ClientResponse
pub fn send<T, Io>( pub fn send<T, I>(
self, self,
connector: &mut T, connector: &mut T,
) -> impl Future<Item = ClientResponse, Error = SendRequestError> ) -> impl Future<Item = ClientResponse, Error = SendRequestError>
where where
T: Service<Request = Connect, Response = Connection<Io>, Error = ConnectorError>, T: Service<Request = Connect, Response = I, Error = ConnectorError>,
Io: AsyncRead + AsyncWrite + 'static, I: Connection,
{ {
pipeline::send_request(self.head, self.body, connector) pipeline::send_request(self.head, self.body, connector)
} }