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

add client http/2 support

This commit is contained in:
Nikolay Kim 2019-01-28 20:41:09 -08:00
parent 12fb94204f
commit 4a388d7ad9
15 changed files with 719 additions and 402 deletions

33
examples/client.rs Normal file
View File

@ -0,0 +1,33 @@
use actix_http::{client, Error};
use actix_rt::System;
use bytes::BytesMut;
use futures::{future::lazy, Future, Stream};
fn main() -> Result<(), Error> {
std::env::set_var("RUST_LOG", "actix_http=trace");
env_logger::init();
System::new("test").block_on(lazy(|| {
let mut connector = client::Connector::default().service();
client::ClientRequest::get("https://www.rust-lang.org/") // <- Create request builder
.header("User-Agent", "Actix-web")
.finish()
.unwrap()
.send(&mut connector) // <- Send http request
.from_err()
.and_then(|response| {
// <- server http response
println!("Response: {:?}", response);
// read response body
response
.from_err()
.fold(BytesMut::new(), move |mut acc, chunk| {
acc.extend_from_slice(&chunk);
Ok::<_, Error>(acc)
})
.map(|body| println!("Downloaded: {:?} bytes", body.len()))
})
}))
}

View File

@ -1,11 +1,35 @@
use std::{fmt, io, time}; use std::{fmt, time};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use futures::Poll; use bytes::Bytes;
use futures::Future;
use h2::client::SendRequest;
use crate::body::MessageBody;
use crate::message::RequestHead;
use super::error::SendRequestError;
use super::pool::Acquired; use super::pool::Acquired;
use super::response::ClientResponse;
use super::{h1proto, h2proto};
pub trait Connection: AsyncRead + AsyncWrite + 'static { pub(crate) enum ConnectionType<Io> {
H1(Io),
H2(SendRequest<Bytes>),
}
pub trait RequestSender {
type Future: Future<Item = ClientResponse, Error = SendRequestError>;
/// Close connection
fn send_request<B: MessageBody + 'static>(
self,
head: RequestHead,
body: B,
) -> Self::Future;
}
pub(crate) trait ConnectionLifetime: AsyncRead + AsyncWrite + 'static {
/// Close connection /// Close connection
fn close(&mut self); fn close(&mut self);
@ -16,7 +40,7 @@ pub trait Connection: AsyncRead + AsyncWrite + 'static {
#[doc(hidden)] #[doc(hidden)]
/// HTTP client connection /// HTTP client connection
pub struct IoConnection<T> { pub struct IoConnection<T> {
io: Option<T>, io: Option<ConnectionType<T>>,
created: time::Instant, created: time::Instant,
pool: Option<Acquired<T>>, pool: Option<Acquired<T>>,
} }
@ -26,77 +50,83 @@ where
T: fmt::Debug, T: fmt::Debug,
{ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Connection {:?}", self.io) match self.io {
Some(ConnectionType::H1(ref io)) => write!(f, "H1Connection({:?})", io),
Some(ConnectionType::H2(_)) => write!(f, "H2Connection"),
None => write!(f, "Connection(Empty)"),
}
} }
} }
impl<T: AsyncRead + AsyncWrite + 'static> IoConnection<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: ConnectionType<T>,
created: time::Instant,
pool: Option<Acquired<T>>,
) -> Self {
IoConnection { IoConnection {
pool,
created, created,
io: Some(io), io: Some(io),
pool: Some(pool),
} }
} }
/// Raw IO stream pub(crate) fn into_inner(self) -> (ConnectionType<T>, time::Instant) {
pub fn get_mut(&mut self) -> &mut T {
self.io.as_mut().unwrap()
}
pub(crate) fn into_inner(self) -> (T, time::Instant) {
(self.io.unwrap(), self.created) (self.io.unwrap(), self.created)
} }
} }
impl<T: AsyncRead + AsyncWrite + 'static> Connection for IoConnection<T> { impl<T> RequestSender for IoConnection<T>
/// Close connection where
fn close(&mut self) { T: AsyncRead + AsyncWrite + 'static,
if let Some(mut pool) = self.pool.take() { {
if let Some(io) = self.io.take() { type Future = Box<Future<Item = ClientResponse, Error = SendRequestError>>;
pool.close(IoConnection {
io: Some(io),
created: self.created,
pool: None,
})
}
}
}
/// Release this connection to the connection pool fn send_request<B: MessageBody + 'static>(
fn release(&mut self) { mut self,
if let Some(mut pool) = self.pool.take() { head: RequestHead,
if let Some(io) = self.io.take() { body: B,
pool.release(IoConnection { ) -> Self::Future {
io: Some(io), match self.io.take().unwrap() {
created: self.created, ConnectionType::H1(io) => Box::new(h1proto::send_request(
pool: None, io,
}) head,
} body,
self.created,
self.pool,
)),
ConnectionType::H2(io) => Box::new(h2proto::send_request(
io,
head,
body,
self.created,
self.pool,
)),
} }
} }
} }
impl<T: AsyncRead + AsyncWrite + 'static> io::Read for IoConnection<T> { #[allow(dead_code)]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { pub(crate) enum EitherConnection<A, B> {
self.io.as_mut().unwrap().read(buf) A(IoConnection<A>),
} B(IoConnection<B>),
} }
impl<T: AsyncRead + AsyncWrite + 'static> AsyncRead for IoConnection<T> {} impl<A, B> RequestSender for EitherConnection<A, B>
where
A: AsyncRead + AsyncWrite + 'static,
B: AsyncRead + AsyncWrite + 'static,
{
type Future = Box<Future<Item = ClientResponse, Error = SendRequestError>>;
impl<T: AsyncRead + AsyncWrite + 'static> io::Write for IoConnection<T> { fn send_request<RB: MessageBody + 'static>(
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self,
self.io.as_mut().unwrap().write(buf) head: RequestHead,
} body: RB,
) -> Self::Future {
fn flush(&mut self) -> io::Result<()> { match self {
self.io.as_mut().unwrap().flush() EitherConnection::A(con) => con.send_request(head, body),
} EitherConnection::B(con) => con.send_request(head, body),
} }
impl<T: AsyncRead + AsyncWrite + 'static> AsyncWrite for IoConnection<T> {
fn shutdown(&mut self) -> Poll<(), io::Error> {
self.io.as_mut().unwrap().shutdown()
} }
} }

View File

@ -1,23 +1,22 @@
use std::time::Duration; use std::time::Duration;
use std::{fmt, io};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_connector::{Resolver, TcpConnector}; use actix_connector::{Resolver, TcpConnector};
use actix_service::{Service, ServiceExt}; use actix_service::{Service, ServiceExt};
use actix_utils::timeout::{TimeoutError, TimeoutService}; use actix_utils::timeout::{TimeoutError, TimeoutService};
use futures::future::Either;
use futures::Poll;
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, IoConnection}; use super::connection::RequestSender;
use super::error::ConnectorError; use super::error::ConnectorError;
use super::pool::ConnectionPool; use super::pool::{ConnectionPool, Protocol};
#[cfg(feature = "ssl")] #[cfg(feature = "ssl")]
use actix_connector::ssl::OpensslConnector; use actix_connector::ssl::OpensslConnector;
#[cfg(feature = "ssl")] #[cfg(feature = "ssl")]
use openssl::ssl::{SslConnector, SslMethod}; use openssl::ssl::{SslConnector, SslMethod};
#[cfg(feature = "ssl")]
const H2: &[u8] = b"h2";
#[cfg(not(feature = "ssl"))] #[cfg(not(feature = "ssl"))]
type SslConnector = (); type SslConnector = ();
@ -40,7 +39,12 @@ impl Default for Connector {
let connector = { let connector = {
#[cfg(feature = "ssl")] #[cfg(feature = "ssl")]
{ {
SslConnector::builder(SslMethod::tls()).unwrap().build() use log::error;
let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap();
let _ = ssl
.set_alpn_protos(b"\x02h2\x08http/1.1")
.map_err(|e| error!("Can not set alpn protocol: {:?}", e));
ssl.build()
} }
#[cfg(not(feature = "ssl"))] #[cfg(not(feature = "ssl"))]
{ {
@ -133,15 +137,17 @@ impl Connector {
/// Finish configuration process and create connector service. /// Finish configuration process and create connector service.
pub fn service( pub fn service(
self, self,
) -> impl Service<Connect, Response = impl Connection, Error = ConnectorError> + Clone ) -> impl Service<Connect, Response = impl RequestSender, Error = ConnectorError> + Clone
{ {
#[cfg(not(feature = "ssl"))] #[cfg(not(feature = "ssl"))]
{ {
let connector = TimeoutService::new( let connector = TimeoutService::new(
self.timeout, self.timeout,
self.resolver self.resolver.map_err(ConnectorError::from).and_then(
.map_err(ConnectorError::from) TcpConnector::default()
.and_then(TcpConnector::default().from_err()), .from_err()
.map(|(msg, io)| (msg, io, Protocol::Http1)),
),
) )
.map_err(|e| match e { .map_err(|e| match e {
TimeoutError::Service(e) => e, TimeoutError::Service(e) => e,
@ -168,7 +174,20 @@ impl Connector {
.and_then(TcpConnector::default().from_err()) .and_then(TcpConnector::default().from_err())
.and_then( .and_then(
OpensslConnector::service(self.connector) OpensslConnector::service(self.connector)
.map_err(ConnectorError::from), .map_err(ConnectorError::from)
.map(|(msg, io)| {
let h2 = io
.get_ref()
.ssl()
.selected_alpn_protocol()
.map(|protos| protos.windows(2).any(|w| w == H2))
.unwrap_or(false);
if h2 {
(msg, io, Protocol::Http2)
} else {
(msg, io, Protocol::Http1)
}
}),
), ),
) )
.map_err(|e| match e { .map_err(|e| match e {
@ -178,9 +197,11 @@ impl Connector {
let tcp_service = TimeoutService::new( let tcp_service = TimeoutService::new(
self.timeout, self.timeout,
self.resolver self.resolver.map_err(ConnectorError::from).and_then(
.map_err(ConnectorError::from) TcpConnector::default()
.and_then(TcpConnector::default().from_err()), .from_err()
.map(|(msg, io)| (msg, io, Protocol::Http1)),
),
) )
.map_err(|e| match e { .map_err(|e| match e {
TimeoutError::Service(e) => e, TimeoutError::Service(e) => e,
@ -209,13 +230,16 @@ impl Connector {
#[cfg(not(feature = "ssl"))] #[cfg(not(feature = "ssl"))]
mod connect_impl { mod connect_impl {
use futures::future::{err, Either, FutureResult};
use futures::Poll;
use super::*; use super::*;
use futures::future::{err, FutureResult}; use crate::client::connection::IoConnection;
pub(crate) struct InnerConnector<T, Io> pub(crate) struct InnerConnector<T, Io>
where where
Io: AsyncRead + AsyncWrite + 'static, Io: AsyncRead + AsyncWrite + 'static,
T: Service<Connect, Response = (Connect, Io), Error = ConnectorError>, T: Service<Connect, Response = (Connect, Io, Protocol), Error = ConnectorError>,
{ {
pub(crate) tcp_pool: ConnectionPool<T, Io>, pub(crate) tcp_pool: ConnectionPool<T, Io>,
} }
@ -223,7 +247,8 @@ mod connect_impl {
impl<T, Io> Clone for InnerConnector<T, Io> impl<T, Io> Clone for InnerConnector<T, Io>
where where
Io: AsyncRead + AsyncWrite + 'static, Io: AsyncRead + AsyncWrite + 'static,
T: Service<Connect, Response = (Connect, Io), Error = ConnectorError> + Clone, T: Service<Connect, Response = (Connect, Io, Protocol), Error = ConnectorError>
+ Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
InnerConnector { InnerConnector {
@ -235,7 +260,7 @@ mod connect_impl {
impl<T, Io> Service<Connect> for InnerConnector<T, Io> impl<T, Io> Service<Connect> for InnerConnector<T, Io>
where where
Io: AsyncRead + AsyncWrite + 'static, Io: AsyncRead + AsyncWrite + 'static,
T: Service<Connect, Response = (Connect, Io), Error = ConnectorError>, T: Service<Connect, Response = (Connect, Io, Protocol), Error = ConnectorError>,
{ {
type Response = IoConnection<Io>; type Response = IoConnection<Io>;
type Error = ConnectorError; type Error = ConnectorError;
@ -264,17 +289,26 @@ mod connect_impl {
mod connect_impl { mod connect_impl {
use std::marker::PhantomData; use std::marker::PhantomData;
use futures::future::{err, FutureResult}; use futures::future::{err, Either, FutureResult};
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use super::*; use super::*;
use crate::client::connection::EitherConnection;
pub(crate) struct InnerConnector<T1, T2, Io1, Io2> pub(crate) struct InnerConnector<T1, T2, Io1, Io2>
where where
Io1: AsyncRead + AsyncWrite + 'static, Io1: AsyncRead + AsyncWrite + 'static,
Io2: AsyncRead + AsyncWrite + 'static, Io2: AsyncRead + AsyncWrite + 'static,
T1: Service<Connect, Response = (Connect, Io1), Error = ConnectorError>, T1: Service<
T2: Service<Connect, Response = (Connect, Io2), Error = ConnectorError>, Connect,
Response = (Connect, Io1, Protocol),
Error = ConnectorError,
>,
T2: Service<
Connect,
Response = (Connect, Io2, Protocol),
Error = ConnectorError,
>,
{ {
pub(crate) tcp_pool: ConnectionPool<T1, Io1>, pub(crate) tcp_pool: ConnectionPool<T1, Io1>,
pub(crate) ssl_pool: ConnectionPool<T2, Io2>, pub(crate) ssl_pool: ConnectionPool<T2, Io2>,
@ -284,8 +318,16 @@ mod connect_impl {
where where
Io1: AsyncRead + AsyncWrite + 'static, Io1: AsyncRead + AsyncWrite + 'static,
Io2: AsyncRead + AsyncWrite + 'static, Io2: AsyncRead + AsyncWrite + 'static,
T1: Service<Connect, Response = (Connect, Io1), Error = ConnectorError> + Clone, T1: Service<
T2: Service<Connect, Response = (Connect, Io2), Error = ConnectorError> + Clone, Connect,
Response = (Connect, Io1, Protocol),
Error = ConnectorError,
> + Clone,
T2: Service<
Connect,
Response = (Connect, Io2, Protocol),
Error = ConnectorError,
> + Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
InnerConnector { InnerConnector {
@ -299,10 +341,18 @@ mod connect_impl {
where where
Io1: AsyncRead + AsyncWrite + 'static, Io1: AsyncRead + AsyncWrite + 'static,
Io2: AsyncRead + AsyncWrite + 'static, Io2: AsyncRead + AsyncWrite + 'static,
T1: Service<Connect, Response = (Connect, Io1), Error = ConnectorError>, T1: Service<
T2: Service<Connect, Response = (Connect, Io2), Error = ConnectorError>, Connect,
Response = (Connect, Io1, Protocol),
Error = ConnectorError,
>,
T2: Service<
Connect,
Response = (Connect, Io2, Protocol),
Error = ConnectorError,
>,
{ {
type Response = IoEither<IoConnection<Io1>, IoConnection<Io2>>; type Response = EitherConnection<Io1, Io2>;
type Error = ConnectorError; type Error = ConnectorError;
type Future = Either< type Future = Either<
FutureResult<Self::Response, Self::Error>, FutureResult<Self::Response, Self::Error>,
@ -336,7 +386,7 @@ mod connect_impl {
pub(crate) struct InnerConnectorResponseA<T, Io1, Io2> pub(crate) struct InnerConnectorResponseA<T, Io1, Io2>
where where
Io1: AsyncRead + AsyncWrite + 'static, Io1: AsyncRead + AsyncWrite + 'static,
T: Service<Connect, Response = (Connect, Io1), Error = ConnectorError>, T: Service<Connect, Response = (Connect, Io1, Protocol), Error = ConnectorError>,
{ {
fut: <ConnectionPool<T, Io1> as Service<Connect>>::Future, fut: <ConnectionPool<T, Io1> as Service<Connect>>::Future,
_t: PhantomData<Io2>, _t: PhantomData<Io2>,
@ -344,17 +394,17 @@ mod connect_impl {
impl<T, Io1, Io2> Future for InnerConnectorResponseA<T, Io1, Io2> impl<T, Io1, Io2> Future for InnerConnectorResponseA<T, Io1, Io2>
where where
T: Service<Connect, Response = (Connect, Io1), Error = ConnectorError>, T: Service<Connect, Response = (Connect, Io1, Protocol), Error = ConnectorError>,
Io1: AsyncRead + AsyncWrite + 'static, Io1: AsyncRead + AsyncWrite + 'static,
Io2: AsyncRead + AsyncWrite + 'static, Io2: AsyncRead + AsyncWrite + 'static,
{ {
type Item = IoEither<IoConnection<Io1>, IoConnection<Io2>>; type Item = EitherConnection<Io1, 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> {
match self.fut.poll()? { match self.fut.poll()? {
Async::NotReady => Ok(Async::NotReady), Async::NotReady => Ok(Async::NotReady),
Async::Ready(res) => Ok(Async::Ready(IoEither::A(res))), Async::Ready(res) => Ok(Async::Ready(EitherConnection::A(res))),
} }
} }
} }
@ -362,7 +412,7 @@ mod connect_impl {
pub(crate) struct InnerConnectorResponseB<T, Io1, Io2> pub(crate) struct InnerConnectorResponseB<T, Io1, Io2>
where where
Io2: AsyncRead + AsyncWrite + 'static, Io2: AsyncRead + AsyncWrite + 'static,
T: Service<Connect, Response = (Connect, Io2), Error = ConnectorError>, T: Service<Connect, Response = (Connect, Io2, Protocol), Error = ConnectorError>,
{ {
fut: <ConnectionPool<T, Io2> as Service<Connect>>::Future, fut: <ConnectionPool<T, Io2> as Service<Connect>>::Future,
_t: PhantomData<Io1>, _t: PhantomData<Io1>,
@ -370,129 +420,18 @@ mod connect_impl {
impl<T, Io1, Io2> Future for InnerConnectorResponseB<T, Io1, Io2> impl<T, Io1, Io2> Future for InnerConnectorResponseB<T, Io1, Io2>
where where
T: Service<Connect, Response = (Connect, Io2), Error = ConnectorError>, T: Service<Connect, Response = (Connect, Io2, Protocol), Error = ConnectorError>,
Io1: AsyncRead + AsyncWrite + 'static, Io1: AsyncRead + AsyncWrite + 'static,
Io2: AsyncRead + AsyncWrite + 'static, Io2: AsyncRead + AsyncWrite + 'static,
{ {
type Item = IoEither<IoConnection<Io1>, IoConnection<Io2>>; type Item = EitherConnection<Io1, 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> {
match self.fut.poll()? { match self.fut.poll()? {
Async::NotReady => Ok(Async::NotReady), Async::NotReady => Ok(Async::NotReady),
Async::Ready(res) => Ok(Async::Ready(IoEither::B(res))), Async::Ready(res) => Ok(Async::Ready(EitherConnection::B(res))),
} }
} }
} }
} }
pub(crate) enum IoEither<Io1, Io2> {
A(Io1),
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>
where
Io1: Connection,
Io2: Connection,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
IoEither::A(ref mut io) => io.read(buf),
IoEither::B(ref mut io) => io.read(buf),
}
}
}
impl<Io1, Io2> AsyncRead for IoEither<Io1, Io2>
where
Io1: Connection,
Io2: Connection,
{
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
match self {
IoEither::A(ref io) => io.prepare_uninitialized_buffer(buf),
IoEither::B(ref io) => io.prepare_uninitialized_buffer(buf),
}
}
}
impl<Io1, Io2> AsyncWrite for IoEither<Io1, Io2>
where
Io1: Connection,
Io2: Connection,
{
fn shutdown(&mut self) -> Poll<(), io::Error> {
match self {
IoEither::A(ref mut io) => io.shutdown(),
IoEither::B(ref mut io) => io.shutdown(),
}
}
fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, io::Error> {
match self {
IoEither::A(ref mut io) => io.poll_write(buf),
IoEither::B(ref mut io) => io.poll_write(buf),
}
}
fn poll_flush(&mut self) -> Poll<(), io::Error> {
match self {
IoEither::A(ref mut io) => io.poll_flush(),
IoEither::B(ref mut io) => io.poll_flush(),
}
}
}
impl<Io1, Io2> io::Write for IoEither<Io1, Io2>
where
Io1: Connection,
Io2: Connection,
{
fn flush(&mut self) -> io::Result<()> {
match self {
IoEither::A(ref mut io) => io.flush(),
IoEither::B(ref mut io) => io.flush(),
}
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
IoEither::A(ref mut io) => io.write(buf),
IoEither::B(ref mut io) => io.write(buf),
}
}
}
impl<Io1, Io2> fmt::Debug for IoEither<Io1, Io2>
where
Io1: fmt::Debug,
Io2: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
IoEither::A(ref io) => io.fmt(fmt),
IoEither::B(ref io) => io.fmt(fmt),
}
}
}

View File

@ -6,7 +6,8 @@ use trust_dns_resolver::error::ResolveError;
#[cfg(feature = "ssl")] #[cfg(feature = "ssl")]
use openssl::ssl::{Error as SslError, HandshakeError}; use openssl::ssl::{Error as SslError, HandshakeError};
use crate::error::{Error, ParseError}; use crate::error::{Error, ParseError, ResponseError};
use crate::response::Response;
/// A set of errors that can occur while connecting to an HTTP host /// A set of errors that can occur while connecting to an HTTP host
#[derive(Debug, Display, From)] #[derive(Debug, Display, From)]
@ -32,6 +33,10 @@ pub enum ConnectorError {
#[display(fmt = "No dns records found for the input")] #[display(fmt = "No dns records found for the input")]
NoRecords, NoRecords,
/// Http2 error
#[display(fmt = "{}", _0)]
H2(h2::Error),
/// Connecting took too long /// Connecting took too long
#[display(fmt = "Timeout out while establishing connection")] #[display(fmt = "Timeout out while establishing connection")]
Timeout, Timeout,
@ -80,6 +85,23 @@ pub enum SendRequestError {
Send(io::Error), Send(io::Error),
/// Error parsing response /// Error parsing response
Response(ParseError), Response(ParseError),
/// Http2 error
#[display(fmt = "{}", _0)]
H2(h2::Error),
/// Error sending request body /// Error sending request body
Body(Error), Body(Error),
} }
/// Convert `SendRequestError` to a server `Response`
impl ResponseError for SendRequestError {
fn error_response(&self) -> Response {
match *self {
SendRequestError::Connector(ConnectorError::Timeout) => {
Response::GatewayTimeout()
}
SendRequestError::Connector(_) => Response::BadGateway(),
_ => Response::InternalServerError(),
}
.into()
}
}

View File

@ -1,38 +1,42 @@
use std::collections::VecDeque; use std::{io, time};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_service::Service;
use bytes::Bytes; use bytes::Bytes;
use futures::future::{err, ok, Either}; use futures::future::{err, ok, Either};
use futures::{Async, Future, Poll, Sink, Stream}; use futures::{Async, Future, Poll, Sink, Stream};
use super::connection::{ConnectionLifetime, ConnectionType, IoConnection};
use super::error::{ConnectorError, SendRequestError}; use super::error::{ConnectorError, SendRequestError};
use super::pool::Acquired;
use super::response::ClientResponse; use super::response::ClientResponse;
use super::{Connect, Connection};
use crate::body::{BodyLength, MessageBody, PayloadStream}; use crate::body::{BodyLength, MessageBody, PayloadStream};
use crate::error::PayloadError; use crate::error::PayloadError;
use crate::h1; use crate::h1;
use crate::message::RequestHead; use crate::message::RequestHead;
pub(crate) fn send_request<T, I, B>( pub(crate) fn send_request<T, B>(
io: T,
head: RequestHead, head: RequestHead,
body: B, body: B,
connector: &mut T, created: time::Instant,
pool: Option<Acquired<T>>,
) -> impl Future<Item = ClientResponse, Error = SendRequestError> ) -> impl Future<Item = ClientResponse, Error = SendRequestError>
where where
T: Service<Connect, Response = I, Error = ConnectorError>, T: AsyncRead + AsyncWrite + 'static,
B: MessageBody, B: MessageBody,
I: Connection,
{ {
let io = H1Connection {
io: Some(io),
created: created,
pool: pool,
};
let len = body.length(); let len = body.length();
connector // create Framed and send reqest
// connect to the host Framed::new(io, h1::ClientCodec::default())
.call(Connect::new(head.uri.clone())) .send((head, len).into())
.from_err() .from_err()
// create Framed and send reqest
.map(|io| Framed::new(io, h1::ClientCodec::default()))
.and_then(move |framed| framed.send((head, len).into()).from_err())
// send request body // send request body
.and_then(move |framed| match body.length() { .and_then(move |framed| match body.length() {
BodyLength::None | BodyLength::Empty | BodyLength::Sized(0) => { BodyLength::None | BodyLength::Empty | BodyLength::Sized(0) => {
@ -64,11 +68,70 @@ where
}) })
} }
#[doc(hidden)]
/// HTTP client connection
pub struct H1Connection<T> {
io: Option<T>,
created: time::Instant,
pool: Option<Acquired<T>>,
}
impl<T: AsyncRead + AsyncWrite + 'static> ConnectionLifetime for H1Connection<T> {
/// Close connection
fn close(&mut self) {
if let Some(mut pool) = self.pool.take() {
if let Some(io) = self.io.take() {
pool.close(IoConnection::new(
ConnectionType::H1(io),
self.created,
None,
));
}
}
}
/// Release this connection to the connection pool
fn release(&mut self) {
if let Some(mut pool) = self.pool.take() {
if let Some(io) = self.io.take() {
pool.release(IoConnection::new(
ConnectionType::H1(io),
self.created,
None,
));
}
}
}
}
impl<T: AsyncRead + AsyncWrite + 'static> io::Read for H1Connection<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.io.as_mut().unwrap().read(buf)
}
}
impl<T: AsyncRead + AsyncWrite + 'static> AsyncRead for H1Connection<T> {}
impl<T: AsyncRead + AsyncWrite + 'static> io::Write for H1Connection<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.io.as_mut().unwrap().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.io.as_mut().unwrap().flush()
}
}
impl<T: AsyncRead + AsyncWrite + 'static> AsyncWrite for H1Connection<T> {
fn shutdown(&mut self) -> Poll<(), io::Error> {
self.io.as_mut().unwrap().shutdown()
}
}
/// Future responsible for sending request body to the peer /// Future responsible for sending request body to the peer
struct SendBody<I, B> { pub(crate) struct SendBody<I, B> {
body: Option<B>, body: Option<B>,
framed: Option<Framed<I, h1::ClientCodec>>, framed: Option<Framed<I, h1::ClientCodec>>,
write_buf: VecDeque<h1::Message<(RequestHead, BodyLength)>>,
flushed: bool, flushed: bool,
} }
@ -77,11 +140,10 @@ where
I: AsyncRead + AsyncWrite + 'static, I: AsyncRead + AsyncWrite + 'static,
B: MessageBody, B: MessageBody,
{ {
fn new(body: B, framed: Framed<I, h1::ClientCodec>) -> Self { pub(crate) fn new(body: B, framed: Framed<I, h1::ClientCodec>) -> Self {
SendBody { SendBody {
body: Some(body), body: Some(body),
framed: Some(framed), framed: Some(framed),
write_buf: VecDeque::new(),
flushed: true, flushed: true,
} }
} }
@ -89,7 +151,7 @@ where
impl<I, B> Future for SendBody<I, B> impl<I, B> Future for SendBody<I, B>
where where
I: Connection, I: ConnectionLifetime,
B: MessageBody, B: MessageBody,
{ {
type Item = Framed<I, h1::ClientCodec>; type Item = Framed<I, h1::ClientCodec>;
@ -158,15 +220,15 @@ impl Payload<()> {
} }
} }
impl<Io: Connection> Payload<Io> { impl<Io: ConnectionLifetime> Payload<Io> {
fn stream(framed: Framed<Io, h1::ClientCodec>) -> PayloadStream { pub 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: Connection> Stream for Payload<Io> { impl<Io: ConnectionLifetime> Stream for Payload<Io> {
type Item = Bytes; type Item = Bytes;
type Error = PayloadError; type Error = PayloadError;
@ -190,7 +252,7 @@ impl<Io: Connection> Stream for Payload<Io> {
fn release_connection<T, U>(framed: Framed<T, U>, force_close: bool) fn release_connection<T, U>(framed: Framed<T, U>, force_close: bool)
where where
T: Connection, T: ConnectionLifetime,
{ {
let mut parts = framed.into_parts(); let mut parts = framed.into_parts();
if !force_close && parts.read_buf.is_empty() && parts.write_buf.is_empty() { if !force_close && parts.read_buf.is_empty() && parts.write_buf.is_empty() {

151
src/client/h2proto.rs Normal file
View File

@ -0,0 +1,151 @@
use std::cell::RefCell;
use std::time;
use actix_codec::{AsyncRead, AsyncWrite};
use bytes::Bytes;
use futures::future::{err, Either};
use futures::{Async, Future, Poll, Stream};
use h2::{client::SendRequest, SendStream};
use http::{request::Request, Version};
use super::connection::{ConnectionType, IoConnection};
use super::error::SendRequestError;
use super::pool::Acquired;
use super::response::ClientResponse;
use crate::body::{BodyLength, MessageBody};
use crate::message::{RequestHead, ResponseHead};
pub(crate) fn send_request<T, B>(
io: SendRequest<Bytes>,
head: RequestHead,
body: B,
created: time::Instant,
pool: Option<Acquired<T>>,
) -> impl Future<Item = ClientResponse, Error = SendRequestError>
where
T: AsyncRead + AsyncWrite + 'static,
B: MessageBody,
{
trace!("Sending client request: {:?} {:?}", head, body.length());
let eof = match body.length() {
BodyLength::None | BodyLength::Empty | BodyLength::Sized(0) => true,
_ => false,
};
io.ready()
.map_err(SendRequestError::from)
.and_then(move |mut io| {
let mut req = Request::new(());
*req.uri_mut() = head.uri;
*req.method_mut() = head.method;
*req.headers_mut() = head.headers;
*req.version_mut() = Version::HTTP_2;
match io.send_request(req, eof) {
Ok((resp, send)) => {
release(io, pool, created, false);
if !eof {
Either::A(Either::B(
SendBody {
body,
send,
buf: None,
}
.and_then(move |_| resp.map_err(SendRequestError::from)),
))
} else {
Either::B(resp.map_err(SendRequestError::from))
}
}
Err(e) => {
release(io, pool, created, e.is_io());
Either::A(Either::A(err(e.into())))
}
}
})
.and_then(|resp| {
let (parts, body) = resp.into_parts();
let mut head = ResponseHead::default();
head.version = parts.version;
head.status = parts.status;
head.headers = parts.headers;
Ok(ClientResponse {
head,
payload: RefCell::new(Some(Box::new(body.from_err()))),
})
})
.from_err()
}
struct SendBody<B: MessageBody> {
body: B,
send: SendStream<Bytes>,
buf: Option<Bytes>,
}
impl<B: MessageBody> Future for SendBody<B> {
type Item = ();
type Error = SendRequestError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if self.buf.is_none() {
match self.body.poll_next() {
Ok(Async::Ready(Some(buf))) => {
self.send.reserve_capacity(buf.len());
self.buf = Some(buf);
}
Ok(Async::Ready(None)) => {
if let Err(e) = self.send.send_data(Bytes::new(), true) {
return Err(e.into());
}
self.send.reserve_capacity(0);
return Ok(Async::Ready(()));
}
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(e) => return Err(e.into()),
}
}
loop {
match self.send.poll_capacity() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(None)) => return Ok(Async::Ready(())),
Ok(Async::Ready(Some(cap))) => {
let mut buf = self.buf.take().unwrap();
let len = buf.len();
let bytes = buf.split_to(std::cmp::min(cap, len));
if let Err(e) = self.send.send_data(bytes, false) {
return Err(e.into());
} else {
if !buf.is_empty() {
self.send.reserve_capacity(buf.len());
self.buf = Some(buf);
}
return self.poll();
}
}
Err(e) => return Err(e.into()),
}
}
}
}
// release SendRequest object
fn release<T: AsyncRead + AsyncWrite + 'static>(
io: SendRequest<Bytes>,
pool: Option<Acquired<T>>,
created: time::Instant,
close: bool,
) {
if let Some(mut pool) = pool {
if close {
pool.close(IoConnection::new(ConnectionType::H2(io), created, None));
} else {
pool.release(IoConnection::new(ConnectionType::H2(io), created, None));
}
}
}

View File

@ -3,13 +3,14 @@ mod connect;
mod connection; mod connection;
mod connector; mod connector;
mod error; mod error;
mod pipeline; mod h1proto;
mod h2proto;
mod pool; mod pool;
mod request; mod request;
mod response; mod response;
pub use self::connect::Connect; pub use self::connect::Connect;
pub use self::connection::Connection; pub use self::connection::RequestSender;
pub use self::connector::Connector; pub use self::connector::Connector;
pub use self::error::{ConnectorError, InvalidUrlKind, SendRequestError}; pub use self::error::{ConnectorError, InvalidUrlKind, SendRequestError};
pub use self::request::{ClientRequest, ClientRequestBuilder}; pub use self::request::{ClientRequest, ClientRequestBuilder};

View File

@ -6,10 +6,12 @@ use std::time::{Duration, Instant};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::Service; use actix_service::Service;
use bytes::Bytes;
use futures::future::{ok, Either, FutureResult}; use futures::future::{ok, Either, FutureResult};
use futures::task::AtomicTask; use futures::task::AtomicTask;
use futures::unsync::oneshot; use futures::unsync::oneshot;
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use h2::client::{handshake, Handshake};
use hashbrown::HashMap; use hashbrown::HashMap;
use http::uri::Authority; use http::uri::Authority;
use indexmap::IndexSet; use indexmap::IndexSet;
@ -17,9 +19,15 @@ use slab::Slab;
use tokio_timer::{sleep, Delay}; use tokio_timer::{sleep, Delay};
use super::connect::Connect; use super::connect::Connect;
use super::connection::IoConnection; use super::connection::{ConnectionType, IoConnection};
use super::error::ConnectorError; use super::error::ConnectorError;
#[derive(Clone, Copy, PartialEq)]
pub enum Protocol {
Http1,
Http2,
}
#[derive(Hash, Eq, PartialEq, Clone, Debug)] #[derive(Hash, Eq, PartialEq, Clone, Debug)]
pub(crate) struct Key { pub(crate) struct Key {
authority: Authority, authority: Authority,
@ -31,13 +39,6 @@ impl From<Authority> for Key {
} }
} }
#[derive(Debug)]
struct AvailableConnection<T> {
io: T,
used: Instant,
created: Instant,
}
/// Connections pool /// Connections pool
pub(crate) struct ConnectionPool<T, Io: AsyncRead + AsyncWrite + 'static>( pub(crate) struct ConnectionPool<T, Io: AsyncRead + AsyncWrite + 'static>(
T, T,
@ -47,7 +48,7 @@ pub(crate) struct ConnectionPool<T, Io: AsyncRead + AsyncWrite + 'static>(
impl<T, Io> ConnectionPool<T, Io> impl<T, Io> ConnectionPool<T, Io>
where where
Io: AsyncRead + AsyncWrite + 'static, Io: AsyncRead + AsyncWrite + 'static,
T: Service<Connect, Response = (Connect, Io), Error = ConnectorError>, T: Service<Connect, Response = (Connect, Io, Protocol), Error = ConnectorError>,
{ {
pub(crate) fn new( pub(crate) fn new(
connector: T, connector: T,
@ -86,7 +87,7 @@ where
impl<T, Io> Service<Connect> for ConnectionPool<T, Io> impl<T, Io> Service<Connect> for ConnectionPool<T, Io>
where where
Io: AsyncRead + AsyncWrite + 'static, Io: AsyncRead + AsyncWrite + 'static,
T: Service<Connect, Response = (Connect, Io), Error = ConnectorError>, T: Service<Connect, Response = (Connect, Io, Protocol), Error = ConnectorError>,
{ {
type Response = IoConnection<Io>; type Response = IoConnection<Io>;
type Error = ConnectorError; type Error = ConnectorError;
@ -109,7 +110,7 @@ where
Either::A(ok(IoConnection::new( Either::A(ok(IoConnection::new(
io, io,
created, created,
Acquired(key, Some(self.1.clone())), Some(Acquired(key, Some(self.1.clone()))),
))) )))
} }
Acquire::NotAvailable => { Acquire::NotAvailable => {
@ -190,12 +191,13 @@ where
{ {
fut: F, fut: F,
key: Key, key: Key,
h2: Option<Handshake<Io, Bytes>>,
inner: Option<Rc<RefCell<Inner<Io>>>>, inner: Option<Rc<RefCell<Inner<Io>>>>,
} }
impl<F, Io> OpenConnection<F, Io> impl<F, Io> OpenConnection<F, Io>
where where
F: Future<Item = (Connect, Io), Error = ConnectorError>, F: Future<Item = (Connect, Io, Protocol), Error = ConnectorError>,
Io: AsyncRead + AsyncWrite + 'static, Io: AsyncRead + AsyncWrite + 'static,
{ {
fn new(key: Key, inner: Rc<RefCell<Inner<Io>>>, fut: F) -> Self { fn new(key: Key, inner: Rc<RefCell<Inner<Io>>>, fut: F) -> Self {
@ -203,6 +205,7 @@ where
key, key,
fut, fut,
inner: Some(inner), inner: Some(inner),
h2: None,
} }
} }
} }
@ -222,110 +225,165 @@ where
impl<F, Io> Future for OpenConnection<F, Io> impl<F, Io> Future for OpenConnection<F, Io>
where where
F: Future<Item = (Connect, Io), Error = ConnectorError>, F: Future<Item = (Connect, Io, Protocol), Error = ConnectorError>,
Io: AsyncRead + AsyncWrite, Io: AsyncRead + AsyncWrite,
{ {
type Item = IoConnection<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> {
if let Some(ref mut h2) = self.h2 {
return match h2.poll() {
Ok(Async::Ready((snd, connection))) => {
tokio_current_thread::spawn(connection.map_err(|_| ()));
Ok(Async::Ready(IoConnection::new(
ConnectionType::H2(snd),
Instant::now(),
Some(Acquired(self.key.clone(), self.inner.clone())),
)))
}
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => Err(e.into()),
};
}
match self.fut.poll() { match self.fut.poll() {
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
Ok(Async::Ready((_, io))) => { Ok(Async::Ready((_, io, proto))) => {
let _ = self.inner.take(); let _ = self.inner.take();
Ok(Async::Ready(IoConnection::new( if proto == Protocol::Http1 {
io, Ok(Async::Ready(IoConnection::new(
Instant::now(), ConnectionType::H1(io),
Acquired(self.key.clone(), self.inner.clone()),
)))
}
Ok(Async::NotReady) => Ok(Async::NotReady),
}
}
}
struct OpenWaitingConnection<F, Io>
where
Io: AsyncRead + AsyncWrite + 'static,
{
fut: F,
key: Key,
rx: Option<oneshot::Sender<Result<IoConnection<Io>, ConnectorError>>>,
inner: Option<Rc<RefCell<Inner<Io>>>>,
}
impl<F, Io> OpenWaitingConnection<F, Io>
where
F: Future<Item = (Connect, Io), Error = ConnectorError> + 'static,
Io: AsyncRead + AsyncWrite + 'static,
{
fn spawn(
key: Key,
rx: oneshot::Sender<Result<IoConnection<Io>, ConnectorError>>,
inner: Rc<RefCell<Inner<Io>>>,
fut: F,
) {
tokio_current_thread::spawn(OpenWaitingConnection {
key,
fut,
rx: Some(rx),
inner: Some(inner),
})
}
}
impl<F, Io> Drop for OpenWaitingConnection<F, Io>
where
Io: AsyncRead + AsyncWrite + 'static,
{
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
let mut inner = inner.as_ref().borrow_mut();
inner.release();
inner.check_availibility();
}
}
}
impl<F, Io> Future for OpenWaitingConnection<F, Io>
where
F: Future<Item = (Connect, Io), Error = ConnectorError>,
Io: AsyncRead + AsyncWrite,
{
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.fut.poll() {
Err(err) => {
let _ = self.inner.take();
if let Some(rx) = self.rx.take() {
let _ = rx.send(Err(err));
}
Err(())
}
Ok(Async::Ready((_, io))) => {
let _ = self.inner.take();
if let Some(rx) = self.rx.take() {
let _ = rx.send(Ok(IoConnection::new(
io,
Instant::now(), Instant::now(),
Acquired(self.key.clone(), self.inner.clone()), Some(Acquired(self.key.clone(), self.inner.clone())),
))); )))
} else {
self.h2 = Some(handshake(io));
return self.poll();
} }
Ok(Async::Ready(()))
} }
Ok(Async::NotReady) => Ok(Async::NotReady), Ok(Async::NotReady) => Ok(Async::NotReady),
} }
} }
} }
// struct OpenWaitingConnection<F, Io>
// where
// Io: AsyncRead + AsyncWrite + 'static,
// {
// fut: F,
// key: Key,
// h2: Option<Handshake<Io, Bytes>>,
// rx: Option<oneshot::Sender<Result<IoConnection<Io>, ConnectorError>>>,
// inner: Option<Rc<RefCell<Inner<Io>>>>,
// }
// impl<F, Io> OpenWaitingConnection<F, Io>
// where
// F: Future<Item = (Connect, Io, Protocol), Error = ConnectorError> + 'static,
// Io: AsyncRead + AsyncWrite + 'static,
// {
// fn spawn(
// key: Key,
// rx: oneshot::Sender<Result<IoConnection<Io>, ConnectorError>>,
// inner: Rc<RefCell<Inner<Io>>>,
// fut: F,
// ) {
// tokio_current_thread::spawn(OpenWaitingConnection {
// key,
// fut,
// h2: None,
// rx: Some(rx),
// inner: Some(inner),
// })
// }
// }
// impl<F, Io> Drop for OpenWaitingConnection<F, Io>
// where
// Io: AsyncRead + AsyncWrite + 'static,
// {
// fn drop(&mut self) {
// if let Some(inner) = self.inner.take() {
// let mut inner = inner.as_ref().borrow_mut();
// inner.release();
// inner.check_availibility();
// }
// }
// }
// impl<F, Io> Future for OpenWaitingConnection<F, Io>
// where
// F: Future<Item = (Connect, Io, Protocol), Error = ConnectorError>,
// Io: AsyncRead + AsyncWrite,
// {
// type Item = ();
// type Error = ();
// fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// if let Some(ref mut h2) = self.h2 {
// return match h2.poll() {
// Ok(Async::Ready((snd, connection))) => {
// tokio_current_thread::spawn(connection.map_err(|_| ()));
// let _ = self.rx.take().unwrap().send(Ok(IoConnection::new(
// ConnectionType::H2(snd),
// Instant::now(),
// Some(Acquired(self.key.clone(), self.inner.clone())),
// )));
// Ok(Async::Ready(()))
// }
// Ok(Async::NotReady) => Ok(Async::NotReady),
// Err(e) => {
// let _ = self.inner.take();
// if let Some(rx) = self.rx.take() {
// let _ = rx.send(Err(e.into()));
// }
// Err(())
// }
// };
// }
// match self.fut.poll() {
// Err(err) => {
// let _ = self.inner.take();
// if let Some(rx) = self.rx.take() {
// let _ = rx.send(Err(err));
// }
// Err(())
// }
// Ok(Async::Ready((_, io, proto))) => {
// let _ = self.inner.take();
// if proto == Protocol::Http1 {
// let _ = self.rx.take().unwrap().send(Ok(IoConnection::new(
// ConnectionType::H1(io),
// Instant::now(),
// Some(Acquired(self.key.clone(), self.inner.clone())),
// )));
// } else {
// self.h2 = Some(handshake(io));
// return self.poll();
// }
// Ok(Async::Ready(()))
// }
// Ok(Async::NotReady) => Ok(Async::NotReady),
// }
// }
// }
enum Acquire<T> { enum Acquire<T> {
Acquired(T, Instant), Acquired(ConnectionType<T>, Instant),
Available, Available,
NotAvailable, NotAvailable,
} }
// #[derive(Debug)]
struct AvailableConnection<Io> {
io: ConnectionType<Io>,
used: Instant,
created: Instant,
}
pub(crate) struct Inner<Io> { pub(crate) struct Inner<Io> {
conn_lifetime: Duration, conn_lifetime: Duration,
conn_keep_alive: Duration, conn_keep_alive: Duration,
@ -355,7 +413,7 @@ impl<Io> Inner<Io> {
self.waiters_queue.remove(&(key.clone(), token)); self.waiters_queue.remove(&(key.clone(), token));
} }
fn release_conn(&mut self, key: &Key, io: Io, created: Instant) { fn release_conn(&mut self, key: &Key, io: ConnectionType<Io>, created: Instant) {
self.acquired -= 1; self.acquired -= 1;
self.available self.available
.entry(key.clone()) .entry(key.clone())
@ -408,24 +466,30 @@ where
|| (now - conn.created) > self.conn_lifetime || (now - conn.created) > self.conn_lifetime
{ {
if let Some(timeout) = self.disconnect_timeout { if let Some(timeout) = self.disconnect_timeout {
tokio_current_thread::spawn(CloseConnection::new( if let ConnectionType::H1(io) = conn.io {
conn.io, timeout, tokio_current_thread::spawn(CloseConnection::new(
)) io, timeout,
))
}
} }
} else { } else {
let mut io = conn.io; let mut io = conn.io;
let mut buf = [0; 2]; let mut buf = [0; 2];
match io.read(&mut buf) { if let ConnectionType::H1(ref mut s) = io {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => (), match s.read(&mut buf) {
Ok(n) if n > 0 => { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => (),
if let Some(timeout) = self.disconnect_timeout { Ok(n) if n > 0 => {
tokio_current_thread::spawn(CloseConnection::new( if let Some(timeout) = self.disconnect_timeout {
io, timeout, if let ConnectionType::H1(io) = io {
)) tokio_current_thread::spawn(
CloseConnection::new(io, timeout),
)
}
}
continue;
} }
continue; Ok(_) | Err(_) => continue,
} }
Ok(_) | Err(_) => continue,
} }
return Acquire::Acquired(io, conn.created); return Acquire::Acquired(io, conn.created);
} }
@ -434,10 +498,12 @@ where
Acquire::Available Acquire::Available
} }
fn release_close(&mut self, io: Io) { fn release_close(&mut self, io: ConnectionType<Io>) {
self.acquired -= 1; self.acquired -= 1;
if let Some(timeout) = self.disconnect_timeout { if let Some(timeout) = self.disconnect_timeout {
tokio_current_thread::spawn(CloseConnection::new(io, timeout)) if let ConnectionType::H1(io) = io {
tokio_current_thread::spawn(CloseConnection::new(io, timeout))
}
} }
} }
@ -448,65 +514,65 @@ where
} }
} }
struct ConnectorPoolSupport<T, Io> // struct ConnectorPoolSupport<T, Io>
where // where
Io: AsyncRead + AsyncWrite + 'static, // Io: AsyncRead + AsyncWrite + 'static,
{ // {
connector: T, // connector: T,
inner: Rc<RefCell<Inner<Io>>>, // inner: Rc<RefCell<Inner<Io>>>,
} // }
impl<T, Io> Future for ConnectorPoolSupport<T, Io> // impl<T, Io> Future for ConnectorPoolSupport<T, Io>
where // where
Io: AsyncRead + AsyncWrite + 'static, // Io: AsyncRead + AsyncWrite + 'static,
T: Service<Connect, Response = (Connect, Io), Error = ConnectorError>, // T: Service<Connect, Response = (Connect, Io, Protocol), Error = ConnectorError>,
T::Future: 'static, // T::Future: 'static,
{ // {
type Item = (); // type Item = ();
type Error = (); // type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { // fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let mut inner = self.inner.as_ref().borrow_mut(); // let mut inner = self.inner.as_ref().borrow_mut();
inner.task.register(); // inner.task.register();
// check waiters // // check waiters
loop { // loop {
let (key, token) = { // let (key, token) = {
if let Some((key, token)) = inner.waiters_queue.get_index(0) { // if let Some((key, token)) = inner.waiters_queue.get_index(0) {
(key.clone(), *token) // (key.clone(), *token)
} else { // } else {
break; // break;
} // }
}; // };
match inner.acquire(&key) { // match inner.acquire(&key) {
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(IoConnection::new( // if let Err(conn) = tx.send(Ok(IoConnection::new(
io, // io,
created, // created,
Acquired(key.clone(), Some(self.inner.clone())), // Some(Acquired(key.clone(), Some(self.inner.clone()))),
))) { // ))) {
let (io, created) = conn.unwrap().into_inner(); // let (io, created) = conn.unwrap().into_inner();
inner.release_conn(&key, io, created); // inner.release_conn(&key, io, created);
} // }
} // }
Acquire::Available => { // Acquire::Available => {
let (connect, tx) = inner.waiters.remove(token); // let (connect, tx) = inner.waiters.remove(token);
OpenWaitingConnection::spawn( // OpenWaitingConnection::spawn(
key.clone(), // key.clone(),
tx, // tx,
self.inner.clone(), // self.inner.clone(),
self.connector.call(connect), // self.connector.call(connect),
); // );
} // }
} // }
let _ = inner.waiters_queue.swap_remove_index(0); // let _ = inner.waiters_queue.swap_remove_index(0);
} // }
Ok(Async::NotReady) // Ok(Async::NotReady)
} // }
} // }
struct CloseConnection<T> { struct CloseConnection<T> {
io: T, io: T,

View File

@ -17,8 +17,9 @@ use crate::http::{
}; };
use crate::message::{ConnectionType, Head, RequestHead}; use crate::message::{ConnectionType, Head, RequestHead};
use super::connection::RequestSender;
use super::response::ClientResponse; use super::response::ClientResponse;
use super::{pipeline, Connect, Connection, ConnectorError, SendRequestError}; use super::{Connect, ConnectorError, SendRequestError};
/// An HTTP Client Request /// An HTTP Client Request
/// ///
@ -37,7 +38,6 @@ use super::{pipeline, Connect, Connection, ConnectorError, SendRequestError};
/// .map_err(|_| ()) /// .map_err(|_| ())
/// .and_then(|response| { // <- server http response /// .and_then(|response| { // <- server http response
/// println!("Response: {:?}", response); /// println!("Response: {:?}", response);
/// # actix_rt::System::current().stop();
/// Ok(()) /// Ok(())
/// }) /// })
/// })); /// }));
@ -175,10 +175,18 @@ where
connector: &mut T, connector: &mut T,
) -> impl Future<Item = ClientResponse, Error = SendRequestError> ) -> impl Future<Item = ClientResponse, Error = SendRequestError>
where where
B: 'static,
T: Service<Connect, Response = I, Error = ConnectorError>, T: Service<Connect, Response = I, Error = ConnectorError>,
I: Connection, I: RequestSender,
{ {
pipeline::send_request(self.head, self.body, connector) let Self { head, body } = self;
connector
// connect to the host
.call(Connect::new(head.uri.clone()))
.from_err()
// send request
.and_then(move |connection| connection.send_request(head, body))
} }
} }
@ -273,7 +281,6 @@ impl ClientRequestBuilder {
/// .unwrap(); /// .unwrap();
/// } /// }
/// ``` /// ```
#[doc(hidden)]
pub fn set<H: Header>(&mut self, hdr: H) -> &mut Self { pub fn set<H: Header>(&mut self, hdr: H) -> &mut Self {
if let Some(parts) = parts(&mut self.head, &self.err) { if let Some(parts) = parts(&mut self.head, &self.err) {
match hdr.try_into() { match hdr.try_into() {

View File

@ -10,7 +10,7 @@ use crate::error::PayloadError;
use crate::httpmessage::HttpMessage; use crate::httpmessage::HttpMessage;
use crate::message::{Head, ResponseHead}; use crate::message::{Head, ResponseHead};
use super::pipeline::Payload; use super::h1proto::Payload;
/// Client Response /// Client Response
pub struct ClientResponse { pub struct ClientResponse {

View File

@ -327,7 +327,7 @@ impl From<httparse::Error> for ParseError {
} }
} }
#[derive(Display, Debug)] #[derive(Display, Debug, From)]
/// A set of errors that can occur during payload parsing /// A set of errors that can occur during payload parsing
pub enum PayloadError { pub enum PayloadError {
/// A payload reached EOF, but is not complete. /// A payload reached EOF, but is not complete.
@ -342,6 +342,9 @@ pub enum PayloadError {
/// A payload length is unknown. /// A payload length is unknown.
#[display(fmt = "A payload length is unknown.")] #[display(fmt = "A payload length is unknown.")]
UnknownLength, UnknownLength,
/// Http2 payload error
#[display(fmt = "{}", _0)]
H2Payload(h2::Error),
} }
impl From<io::Error> for PayloadError { impl From<io::Error> for PayloadError {

View File

@ -1,6 +1,5 @@
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::fmt; use std::fmt;
use std::hash::{BuildHasherDefault, Hasher};
use hashbrown::HashMap; use hashbrown::HashMap;

View File

@ -59,6 +59,9 @@
//! * `session` - enables session support, includes `ring` crate as //! * `session` - enables session support, includes `ring` crate as
//! dependency //! dependency
//! //!
#[macro_use]
extern crate log;
pub mod body; pub mod body;
pub mod client; pub mod client;
mod config; mod config;

View File

@ -1,3 +1,4 @@
#![allow(dead_code)]
//! Http response //! Http response
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;

View File

@ -13,8 +13,8 @@ use net2::TcpBuilder;
use actix_http::body::MessageBody; use actix_http::body::MessageBody;
use actix_http::client::{ use actix_http::client::{
ClientRequest, ClientRequestBuilder, ClientResponse, Connect, Connection, Connector, ClientRequest, ClientRequestBuilder, ClientResponse, Connect, Connector,
ConnectorError, SendRequestError, ConnectorError, RequestSender, SendRequestError,
}; };
use actix_http::ws; use actix_http::ws;
@ -57,7 +57,7 @@ impl TestServer {
pub fn with_factory<F: StreamServiceFactory>( pub fn with_factory<F: StreamServiceFactory>(
factory: F, factory: F,
) -> TestServerRuntime< ) -> TestServerRuntime<
impl Service<Connect, Response = impl Connection, Error = ConnectorError> + Clone, impl Service<Connect, Response = impl RequestSender, Error = ConnectorError> + Clone,
> { > {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
@ -89,7 +89,7 @@ impl TestServer {
} }
fn new_connector( fn new_connector(
) -> impl Service<Connect, Response = impl Connection, Error = ConnectorError> + Clone ) -> impl Service<Connect, Response = impl RequestSender, Error = ConnectorError> + Clone
{ {
#[cfg(feature = "ssl")] #[cfg(feature = "ssl")]
{ {
@ -192,7 +192,7 @@ impl<T> TestServerRuntime<T> {
impl<T> TestServerRuntime<T> impl<T> TestServerRuntime<T>
where where
T: Service<Connect, Error = ConnectorError> + Clone, T: Service<Connect, Error = ConnectorError> + Clone,
T::Response: Connection, T::Response: RequestSender,
{ {
/// Connect to websocket server at a given path /// Connect to websocket server at a given path
pub fn ws_at( pub fn ws_at(
@ -212,7 +212,7 @@ where
} }
/// Send request and read response message /// Send request and read response message
pub fn send_request<B: MessageBody>( pub fn send_request<B: MessageBody + 'static>(
&mut self, &mut self,
req: ClientRequest<B>, req: ClientRequest<B>,
) -> Result<ClientResponse, SendRequestError> { ) -> Result<ClientResponse, SendRequestError> {