mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-28 01:32:57 +01:00
add client http/2 support
This commit is contained in:
parent
12fb94204f
commit
4a388d7ad9
33
examples/client.rs
Normal file
33
examples/client.rs
Normal 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()))
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
151
src/client/h2proto.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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};
|
||||||
|
@ -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,
|
||||||
|
@ -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() {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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> {
|
||||||
|
Loading…
Reference in New Issue
Block a user