mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-30 18:44:35 +01:00
refactor actix_http connection types and connector services (#2081)
This commit is contained in:
parent
abcb444dd9
commit
b75b5114c3
@ -1,10 +1,16 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
### Added
|
||||||
|
* `client::Connector::handshake_timeout` method for customize tls connection handshake timeout. [#2081]
|
||||||
|
* `client::ConnectorService` as `client::Connector::finish` method's return type [#2081]
|
||||||
|
* `client::ConnectionIo` trait alias [#2081]
|
||||||
|
|
||||||
### Chaged
|
### Chaged
|
||||||
* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063]
|
* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063]
|
||||||
|
|
||||||
[#2063]: https://github.com/actix/actix-web/pull/2063
|
[#2063]: https://github.com/actix/actix-web/pull/2063
|
||||||
|
[#2081]: https://github.com/actix/actix-web/pull/2081
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.4 - 2021-03-08
|
## 3.0.0-beta.4 - 2021-03-08
|
||||||
|
@ -8,6 +8,7 @@ const DEFAULT_H2_STREAM_WINDOW: u32 = 1024 * 1024; // 1MB
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct ConnectorConfig {
|
pub(crate) struct ConnectorConfig {
|
||||||
pub(crate) timeout: Duration,
|
pub(crate) timeout: Duration,
|
||||||
|
pub(crate) handshake_timeout: Duration,
|
||||||
pub(crate) conn_lifetime: Duration,
|
pub(crate) conn_lifetime: Duration,
|
||||||
pub(crate) conn_keep_alive: Duration,
|
pub(crate) conn_keep_alive: Duration,
|
||||||
pub(crate) disconnect_timeout: Option<Duration>,
|
pub(crate) disconnect_timeout: Option<Duration>,
|
||||||
@ -21,6 +22,7 @@ impl Default for ConnectorConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
timeout: Duration::from_secs(5),
|
timeout: Duration::from_secs(5),
|
||||||
|
handshake_timeout: Duration::from_secs(5),
|
||||||
conn_lifetime: Duration::from_secs(75),
|
conn_lifetime: Duration::from_secs(75),
|
||||||
conn_keep_alive: Duration::from_secs(15),
|
conn_keep_alive: Duration::from_secs(15),
|
||||||
disconnect_timeout: Some(Duration::from_millis(3000)),
|
disconnect_timeout: Some(Duration::from_millis(3000)),
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::{
|
||||||
use std::pin::Pin;
|
io,
|
||||||
use std::task::{Context, Poll};
|
ops::{Deref, DerefMut},
|
||||||
use std::{fmt, io, time};
|
pin::Pin,
|
||||||
|
task::{Context, Poll},
|
||||||
|
time,
|
||||||
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf};
|
||||||
use actix_rt::task::JoinHandle;
|
use actix_rt::task::JoinHandle;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use h2::client::SendRequest;
|
use h2::client::SendRequest;
|
||||||
use pin_project::pin_project;
|
|
||||||
|
|
||||||
use crate::body::MessageBody;
|
use crate::body::MessageBody;
|
||||||
use crate::h1::ClientCodec;
|
use crate::h1::ClientCodec;
|
||||||
@ -19,32 +21,148 @@ use super::error::SendRequestError;
|
|||||||
use super::pool::Acquired;
|
use super::pool::Acquired;
|
||||||
use super::{h1proto, h2proto};
|
use super::{h1proto, h2proto};
|
||||||
|
|
||||||
|
/// Trait alias for types impl [tokio::io::AsyncRead] and [tokio::io::AsyncWrite].
|
||||||
pub trait ConnectionIo: AsyncRead + AsyncWrite + Unpin + 'static {}
|
pub trait ConnectionIo: AsyncRead + AsyncWrite + Unpin + 'static {}
|
||||||
|
|
||||||
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> ConnectionIo for T {}
|
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> ConnectionIo for T {}
|
||||||
|
|
||||||
pub(crate) enum ConnectionType<Io> {
|
/// HTTP client connection
|
||||||
H1(Io),
|
pub struct H1Connection<Io: ConnectionIo> {
|
||||||
H2(H2Connection),
|
io: Option<Io>,
|
||||||
|
created: time::Instant,
|
||||||
|
acquired: Acquired<Io>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `H2Connection` has two parts: `SendRequest` and `Connection`.
|
impl<Io: ConnectionIo> H1Connection<Io> {
|
||||||
|
/// close or release the connection to pool based on flag input
|
||||||
|
pub(super) fn on_release(&mut self, keep_alive: bool) {
|
||||||
|
if keep_alive {
|
||||||
|
self.release();
|
||||||
|
} else {
|
||||||
|
self.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close connection
|
||||||
|
fn close(&mut self) {
|
||||||
|
let io = self.io.take().unwrap();
|
||||||
|
self.acquired.close(ConnectionInnerType::H1(io));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release this connection to the connection pool
|
||||||
|
fn release(&mut self) {
|
||||||
|
let io = self.io.take().unwrap();
|
||||||
|
self.acquired
|
||||||
|
.release(ConnectionInnerType::H1(io), self.created);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Io> {
|
||||||
|
Pin::new(self.get_mut().io.as_mut().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Io: ConnectionIo> AsyncRead for H1Connection<Io> {
|
||||||
|
fn poll_read(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
|
self.io_pin_mut().poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Io: ConnectionIo> AsyncWrite for H1Connection<Io> {
|
||||||
|
fn poll_write(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
self.io_pin_mut().poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
self.io_pin_mut().poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> Poll<Result<(), io::Error>> {
|
||||||
|
self.io_pin_mut().poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_vectored(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
bufs: &[io::IoSlice<'_>],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
self.io_pin_mut().poll_write_vectored(cx, bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
self.io.as_ref().unwrap().is_write_vectored()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HTTP2 client connection
|
||||||
|
pub struct H2Connection<Io: ConnectionIo> {
|
||||||
|
io: Option<H2ConnectionInner>,
|
||||||
|
created: time::Instant,
|
||||||
|
acquired: Acquired<Io>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Io: ConnectionIo> Deref for H2Connection<Io> {
|
||||||
|
type Target = SendRequest<Bytes>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.io.as_ref().unwrap().sender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Io: ConnectionIo> DerefMut for H2Connection<Io> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.io.as_mut().unwrap().sender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Io: ConnectionIo> H2Connection<Io> {
|
||||||
|
/// close or release the connection to pool based on flag input
|
||||||
|
pub(super) fn on_release(&mut self, close: bool) {
|
||||||
|
if close {
|
||||||
|
self.close();
|
||||||
|
} else {
|
||||||
|
self.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close connection
|
||||||
|
fn close(&mut self) {
|
||||||
|
let io = self.io.take().unwrap();
|
||||||
|
self.acquired.close(ConnectionInnerType::H2(io));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release this connection to the connection pool
|
||||||
|
fn release(&mut self) {
|
||||||
|
let io = self.io.take().unwrap();
|
||||||
|
self.acquired
|
||||||
|
.release(ConnectionInnerType::H2(io), self.created);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `H2ConnectionInner` has two parts: `SendRequest` and `Connection`.
|
||||||
///
|
///
|
||||||
/// `Connection` is spawned as an async task on runtime and `H2Connection` holds a handle for
|
/// `Connection` is spawned as an async task on runtime and `H2ConnectionInner` holds a handle
|
||||||
/// this task. Therefore, it can wake up and quit the task when SendRequest is dropped.
|
/// for this task. Therefore, it can wake up and quit the task when SendRequest is dropped.
|
||||||
pub(crate) struct H2Connection {
|
pub(super) struct H2ConnectionInner {
|
||||||
handle: JoinHandle<()>,
|
handle: JoinHandle<()>,
|
||||||
sender: SendRequest<Bytes>,
|
sender: SendRequest<Bytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl H2Connection {
|
impl H2ConnectionInner {
|
||||||
pub(crate) fn new<Io>(
|
pub(super) fn new<Io: ConnectionIo>(
|
||||||
sender: SendRequest<Bytes>,
|
sender: SendRequest<Bytes>,
|
||||||
connection: h2::client::Connection<Io>,
|
connection: h2::client::Connection<Io>,
|
||||||
) -> Self
|
) -> Self {
|
||||||
where
|
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
let handle = actix_rt::spawn(async move {
|
let handle = actix_rt::spawn(async move {
|
||||||
let _ = connection.await;
|
let _ = connection.await;
|
||||||
});
|
});
|
||||||
@ -53,143 +171,80 @@ impl H2Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel spawned connection task on drop.
|
/// Cancel spawned connection task on drop.
|
||||||
impl Drop for H2Connection {
|
impl Drop for H2ConnectionInner {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.handle.abort();
|
self.handle.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// only expose sender type to public.
|
|
||||||
impl Deref for H2Connection {
|
|
||||||
type Target = SendRequest<Bytes>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.sender
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for H2Connection {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.sender
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Connection {
|
|
||||||
type Io: AsyncRead + AsyncWrite + Unpin;
|
|
||||||
|
|
||||||
/// Send request and body
|
|
||||||
fn send_request<B, H>(
|
|
||||||
self,
|
|
||||||
head: H,
|
|
||||||
body: B,
|
|
||||||
) -> LocalBoxFuture<'static, Result<(ResponseHead, Payload), SendRequestError>>
|
|
||||||
where
|
|
||||||
B: MessageBody + 'static,
|
|
||||||
H: Into<RequestHeadType> + 'static;
|
|
||||||
|
|
||||||
/// Send request, returns Response and Framed
|
|
||||||
fn open_tunnel<H: Into<RequestHeadType> + 'static>(
|
|
||||||
self,
|
|
||||||
head: H,
|
|
||||||
) -> LocalBoxFuture<
|
|
||||||
'static,
|
|
||||||
Result<(ResponseHead, Framed<Self::Io, ClientCodec>), SendRequestError>,
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
/// HTTP client connection
|
|
||||||
pub struct IoConnection<T>
|
|
||||||
where
|
|
||||||
T: AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
io: Option<ConnectionType<T>>,
|
|
||||||
created: time::Instant,
|
|
||||||
pool: Acquired<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> fmt::Debug for IoConnection<T>
|
|
||||||
where
|
|
||||||
T: AsyncWrite + Unpin + fmt::Debug + 'static,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
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 + Unpin> IoConnection<T> {
|
|
||||||
pub(crate) fn new(
|
|
||||||
io: ConnectionType<T>,
|
|
||||||
created: time::Instant,
|
|
||||||
pool: Acquired<T>,
|
|
||||||
) -> Self {
|
|
||||||
IoConnection {
|
|
||||||
pool,
|
|
||||||
created,
|
|
||||||
io: Some(io),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn into_parts(self) -> (ConnectionType<T>, time::Instant, Acquired<T>) {
|
|
||||||
(self.io.unwrap(), self.created, self.pool)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_request<B: MessageBody + 'static, H: Into<RequestHeadType>>(
|
|
||||||
mut self,
|
|
||||||
head: H,
|
|
||||||
body: B,
|
|
||||||
) -> Result<(ResponseHead, Payload), SendRequestError> {
|
|
||||||
match self.io.take().unwrap() {
|
|
||||||
ConnectionType::H1(io) => {
|
|
||||||
h1proto::send_request(io, head.into(), body, self.created, self.pool)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
ConnectionType::H2(io) => {
|
|
||||||
h2proto::send_request(io, head.into(), body, self.created, self.pool)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send request, returns Response and Framed
|
|
||||||
async fn open_tunnel<H: Into<RequestHeadType>>(
|
|
||||||
mut self,
|
|
||||||
head: H,
|
|
||||||
) -> Result<(ResponseHead, Framed<T, ClientCodec>), SendRequestError> {
|
|
||||||
match self.io.take().unwrap() {
|
|
||||||
ConnectionType::H1(io) => h1proto::open_tunnel(io, head.into()).await,
|
|
||||||
ConnectionType::H2(io) => {
|
|
||||||
self.pool.release(ConnectionType::H2(io), self.created);
|
|
||||||
Err(SendRequestError::TunnelNotSupported)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) enum EitherIoConnection<A, B>
|
/// Unified connection type cover Http1 Plain/Tls and Http2 protocols
|
||||||
|
pub enum Connection<A, B = Box<dyn ConnectionIo>>
|
||||||
where
|
where
|
||||||
A: AsyncRead + AsyncWrite + Unpin + 'static,
|
A: ConnectionIo,
|
||||||
B: AsyncRead + AsyncWrite + Unpin + 'static,
|
B: ConnectionIo,
|
||||||
{
|
{
|
||||||
A(IoConnection<A>),
|
Tcp(ConnectionType<A>),
|
||||||
B(IoConnection<B>),
|
Tls(ConnectionType<B>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B> Connection for EitherIoConnection<A, B>
|
/// Unified connection type cover Http1/2 protocols
|
||||||
where
|
pub enum ConnectionType<Io: ConnectionIo> {
|
||||||
A: AsyncRead + AsyncWrite + Unpin + 'static,
|
H1(H1Connection<Io>),
|
||||||
B: AsyncRead + AsyncWrite + Unpin + 'static,
|
H2(H2Connection<Io>),
|
||||||
{
|
}
|
||||||
type Io = EitherIo<A, B>;
|
|
||||||
|
|
||||||
fn send_request<RB, H>(
|
/// Helper type for storing connection types in pool.
|
||||||
|
pub(super) enum ConnectionInnerType<Io> {
|
||||||
|
H1(Io),
|
||||||
|
H2(H2ConnectionInner),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Io: ConnectionIo> ConnectionType<Io> {
|
||||||
|
pub(super) fn from_pool(
|
||||||
|
inner: ConnectionInnerType<Io>,
|
||||||
|
created: time::Instant,
|
||||||
|
acquired: Acquired<Io>,
|
||||||
|
) -> Self {
|
||||||
|
match inner {
|
||||||
|
ConnectionInnerType::H1(io) => Self::from_h1(io, created, acquired),
|
||||||
|
ConnectionInnerType::H2(io) => Self::from_h2(io, created, acquired),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn from_h1(
|
||||||
|
io: Io,
|
||||||
|
created: time::Instant,
|
||||||
|
acquired: Acquired<Io>,
|
||||||
|
) -> Self {
|
||||||
|
Self::H1(H1Connection {
|
||||||
|
io: Some(io),
|
||||||
|
created,
|
||||||
|
acquired,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn from_h2(
|
||||||
|
io: H2ConnectionInner,
|
||||||
|
created: time::Instant,
|
||||||
|
acquired: Acquired<Io>,
|
||||||
|
) -> Self {
|
||||||
|
Self::H2(H2Connection {
|
||||||
|
io: Some(io),
|
||||||
|
created,
|
||||||
|
acquired,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, B> Connection<A, B>
|
||||||
|
where
|
||||||
|
A: ConnectionIo,
|
||||||
|
B: ConnectionIo,
|
||||||
|
{
|
||||||
|
/// Send a request through connection.
|
||||||
|
pub fn send_request<RB, H>(
|
||||||
self,
|
self,
|
||||||
head: H,
|
head: H,
|
||||||
body: RB,
|
body: RB,
|
||||||
@ -198,76 +253,106 @@ where
|
|||||||
RB: MessageBody + 'static,
|
RB: MessageBody + 'static,
|
||||||
H: Into<RequestHeadType> + 'static,
|
H: Into<RequestHeadType> + 'static,
|
||||||
{
|
{
|
||||||
match self {
|
Box::pin(async move {
|
||||||
EitherIoConnection::A(con) => Box::pin(con.send_request(head, body)),
|
match self {
|
||||||
EitherIoConnection::B(con) => Box::pin(con.send_request(head, body)),
|
Connection::Tcp(ConnectionType::H1(conn)) => {
|
||||||
}
|
h1proto::send_request(conn, head.into(), body).await
|
||||||
|
}
|
||||||
|
Connection::Tls(ConnectionType::H1(conn)) => {
|
||||||
|
h1proto::send_request(conn, head.into(), body).await
|
||||||
|
}
|
||||||
|
Connection::Tls(ConnectionType::H2(conn)) => {
|
||||||
|
h2proto::send_request(conn, head.into(), body).await
|
||||||
|
}
|
||||||
|
_ => unreachable!(
|
||||||
|
"Plain Tcp connection can be used only in Http1 protocol"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send request, returns Response and Framed
|
/// Send request, returns Response and Framed tunnel.
|
||||||
fn open_tunnel<H: Into<RequestHeadType> + 'static>(
|
pub fn open_tunnel<H: Into<RequestHeadType> + 'static>(
|
||||||
self,
|
self,
|
||||||
head: H,
|
head: H,
|
||||||
) -> LocalBoxFuture<
|
) -> LocalBoxFuture<
|
||||||
'static,
|
'static,
|
||||||
Result<(ResponseHead, Framed<Self::Io, ClientCodec>), SendRequestError>,
|
Result<(ResponseHead, Framed<Connection<A, B>, ClientCodec>), SendRequestError>,
|
||||||
> {
|
> {
|
||||||
match self {
|
Box::pin(async move {
|
||||||
EitherIoConnection::A(con) => Box::pin(async {
|
match self {
|
||||||
let (head, framed) = con.open_tunnel(head).await?;
|
Connection::Tcp(ConnectionType::H1(ref _conn)) => {
|
||||||
Ok((head, framed.into_map_io(EitherIo::A)))
|
let (head, framed) = h1proto::open_tunnel(self, head.into()).await?;
|
||||||
}),
|
Ok((head, framed))
|
||||||
EitherIoConnection::B(con) => Box::pin(async {
|
}
|
||||||
let (head, framed) = con.open_tunnel(head).await?;
|
Connection::Tls(ConnectionType::H1(ref _conn)) => {
|
||||||
Ok((head, framed.into_map_io(EitherIo::B)))
|
let (head, framed) = h1proto::open_tunnel(self, head.into()).await?;
|
||||||
}),
|
Ok((head, framed))
|
||||||
}
|
}
|
||||||
|
Connection::Tls(ConnectionType::H2(mut conn)) => {
|
||||||
|
conn.release();
|
||||||
|
Err(SendRequestError::TunnelNotSupported)
|
||||||
|
}
|
||||||
|
Connection::Tcp(ConnectionType::H2(_)) => {
|
||||||
|
unreachable!(
|
||||||
|
"Plain Tcp connection can be used only in Http1 protocol"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project(project = EitherIoProj)]
|
impl<A, B> AsyncRead for Connection<A, B>
|
||||||
pub enum EitherIo<A, B> {
|
|
||||||
A(#[pin] A),
|
|
||||||
B(#[pin] B),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A, B> AsyncRead for EitherIo<A, B>
|
|
||||||
where
|
where
|
||||||
A: AsyncRead,
|
A: ConnectionIo,
|
||||||
B: AsyncRead,
|
B: ConnectionIo,
|
||||||
{
|
{
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &mut ReadBuf<'_>,
|
buf: &mut ReadBuf<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
match self.project() {
|
match self.get_mut() {
|
||||||
EitherIoProj::A(val) => val.poll_read(cx, buf),
|
Connection::Tcp(ConnectionType::H1(conn)) => {
|
||||||
EitherIoProj::B(val) => val.poll_read(cx, buf),
|
Pin::new(conn).poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
Connection::Tls(ConnectionType::H1(conn)) => {
|
||||||
|
Pin::new(conn).poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
_ => unreachable!("H2Connection can not impl AsyncRead trait"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B> AsyncWrite for EitherIo<A, B>
|
const H2_UNREACHABLE_WRITE: &'static str = "H2Connection can not impl AsyncWrite trait";
|
||||||
|
|
||||||
|
impl<A, B> AsyncWrite for Connection<A, B>
|
||||||
where
|
where
|
||||||
A: AsyncWrite,
|
A: ConnectionIo,
|
||||||
B: AsyncWrite,
|
B: ConnectionIo,
|
||||||
{
|
{
|
||||||
fn poll_write(
|
fn poll_write(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
match self.project() {
|
match self.get_mut() {
|
||||||
EitherIoProj::A(val) => val.poll_write(cx, buf),
|
Connection::Tcp(ConnectionType::H1(conn)) => {
|
||||||
EitherIoProj::B(val) => val.poll_write(cx, buf),
|
Pin::new(conn).poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
Connection::Tls(ConnectionType::H1(conn)) => {
|
||||||
|
Pin::new(conn).poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
match self.project() {
|
match self.get_mut() {
|
||||||
EitherIoProj::A(val) => val.poll_flush(cx),
|
Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_flush(cx),
|
||||||
EitherIoProj::B(val) => val.poll_flush(cx),
|
Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_flush(cx),
|
||||||
|
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,9 +360,38 @@ where
|
|||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
match self.project() {
|
match self.get_mut() {
|
||||||
EitherIoProj::A(val) => val.poll_shutdown(cx),
|
Connection::Tcp(ConnectionType::H1(conn)) => {
|
||||||
EitherIoProj::B(val) => val.poll_shutdown(cx),
|
Pin::new(conn).poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
Connection::Tls(ConnectionType::H1(conn)) => {
|
||||||
|
Pin::new(conn).poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_vectored(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
bufs: &[io::IoSlice<'_>],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
match self.get_mut() {
|
||||||
|
Connection::Tcp(ConnectionType::H1(conn)) => {
|
||||||
|
Pin::new(conn).poll_write_vectored(cx, bufs)
|
||||||
|
}
|
||||||
|
Connection::Tls(ConnectionType::H1(conn)) => {
|
||||||
|
Pin::new(conn).poll_write_vectored(cx, bufs)
|
||||||
|
}
|
||||||
|
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Connection::Tcp(ConnectionType::H1(ref conn)) => conn.is_write_vectored(),
|
||||||
|
Connection::Tls(ConnectionType::H1(ref conn)) => conn.is_write_vectored(),
|
||||||
|
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,10 +414,13 @@ mod test {
|
|||||||
|
|
||||||
let tcp = TcpStream::connect(local).await.unwrap();
|
let tcp = TcpStream::connect(local).await.unwrap();
|
||||||
let (sender, connection) = h2::client::handshake(tcp).await.unwrap();
|
let (sender, connection) = h2::client::handshake(tcp).await.unwrap();
|
||||||
let conn = H2Connection::new(sender.clone(), connection);
|
let conn = H2ConnectionInner::new(sender.clone(), connection);
|
||||||
|
|
||||||
assert!(sender.clone().ready().await.is_ok());
|
assert!(sender.clone().ready().await.is_ok());
|
||||||
assert!(h2::client::SendRequest::clone(&*conn).ready().await.is_ok());
|
assert!(h2::client::SendRequest::clone(&conn.sender)
|
||||||
|
.ready()
|
||||||
|
.await
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
drop(conn);
|
drop(conn);
|
||||||
|
|
||||||
|
@ -3,22 +3,26 @@ use std::{
|
|||||||
future::Future,
|
future::Future,
|
||||||
net::IpAddr,
|
net::IpAddr,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
|
rc::Rc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite};
|
use actix_rt::{
|
||||||
use actix_rt::net::TcpStream;
|
net::TcpStream,
|
||||||
use actix_service::{apply_fn, Service, ServiceExt};
|
time::{sleep, Sleep},
|
||||||
use actix_tls::connect::{
|
|
||||||
new_connector, Connect as TcpConnect, Connection as TcpConnection, Resolver,
|
|
||||||
};
|
};
|
||||||
use actix_utils::timeout::{TimeoutError, TimeoutService};
|
use actix_service::Service;
|
||||||
use futures_core::ready;
|
use actix_tls::connect::{
|
||||||
|
new_connector, Connect as TcpConnect, ConnectError as TcpConnectError,
|
||||||
|
Connection as TcpConnection, Resolver,
|
||||||
|
};
|
||||||
|
use futures_core::{future::LocalBoxFuture, ready};
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
|
use pin_project::pin_project;
|
||||||
|
|
||||||
use super::config::ConnectorConfig;
|
use super::config::ConnectorConfig;
|
||||||
use super::connection::{Connection, ConnectionIo, EitherIoConnection};
|
use super::connection::{Connection, ConnectionIo};
|
||||||
use super::error::ConnectError;
|
use super::error::ConnectError;
|
||||||
use super::pool::ConnectionPool;
|
use super::pool::ConnectionPool;
|
||||||
use super::Connect;
|
use super::Connect;
|
||||||
@ -28,18 +32,15 @@ use super::Protocol;
|
|||||||
use actix_tls::connect::ssl::openssl::SslConnector as OpensslConnector;
|
use actix_tls::connect::ssl::openssl::SslConnector as OpensslConnector;
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
use actix_tls::connect::ssl::rustls::ClientConfig;
|
use actix_tls::connect::ssl::rustls::ClientConfig;
|
||||||
#[cfg(feature = "rustls")]
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
|
||||||
enum SslConnector {
|
enum SslConnector {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
None,
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
Openssl(OpensslConnector),
|
Openssl(OpensslConnector),
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
Rustls(Arc<ClientConfig>),
|
Rustls(std::sync::Arc<ClientConfig>),
|
||||||
}
|
}
|
||||||
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
|
||||||
type SslConnector = ();
|
|
||||||
|
|
||||||
/// Manages HTTP client network connectivity.
|
/// Manages HTTP client network connectivity.
|
||||||
///
|
///
|
||||||
@ -104,23 +105,25 @@ impl Connector<()> {
|
|||||||
config.root_store.add_server_trust_anchors(
|
config.root_store.add_server_trust_anchors(
|
||||||
&actix_tls::connect::ssl::rustls::TLS_SERVER_ROOTS,
|
&actix_tls::connect::ssl::rustls::TLS_SERVER_ROOTS,
|
||||||
);
|
);
|
||||||
SslConnector::Rustls(Arc::new(config))
|
SslConnector::Rustls(std::sync::Arc::new(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ssl turned off, provides empty ssl connector
|
// ssl turned off, provides empty ssl connector
|
||||||
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
||||||
fn build_ssl(_: Vec<Vec<u8>>) -> SslConnector {}
|
fn build_ssl(_: Vec<Vec<u8>>) -> SslConnector {
|
||||||
|
SslConnector::None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Connector<T> {
|
impl<S> Connector<S> {
|
||||||
/// Use custom connector.
|
/// Use custom connector.
|
||||||
pub fn connector<T1, U1>(self, connector: T1) -> Connector<T1>
|
pub fn connector<S1, Io1>(self, connector: S1) -> Connector<S1>
|
||||||
where
|
where
|
||||||
U1: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
|
Io1: ConnectionIo + fmt::Debug,
|
||||||
T1: Service<
|
S1: Service<
|
||||||
TcpConnect<Uri>,
|
TcpConnect<Uri>,
|
||||||
Response = TcpConnection<Uri, U1>,
|
Response = TcpConnection<Uri, Io1>,
|
||||||
Error = actix_tls::connect::ConnectError,
|
Error = TcpConnectError,
|
||||||
> + Clone,
|
> + Clone,
|
||||||
{
|
{
|
||||||
Connector {
|
Connector {
|
||||||
@ -131,23 +134,30 @@ impl<T> Connector<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Connector<T>
|
impl<S, Io> Connector<S>
|
||||||
where
|
where
|
||||||
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static,
|
Io: ConnectionIo + fmt::Debug,
|
||||||
T: Service<
|
S: Service<
|
||||||
TcpConnect<Uri>,
|
TcpConnect<Uri>,
|
||||||
Response = TcpConnection<Uri, U>,
|
Response = TcpConnection<Uri, Io>,
|
||||||
Error = actix_tls::connect::ConnectError,
|
Error = TcpConnectError,
|
||||||
> + Clone
|
> + Clone
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
/// Connection timeout, i.e. max time to connect to remote host including dns name resolution.
|
/// Tcp connection timeout, i.e. max time to connect to remote host including dns name
|
||||||
/// Set to 5 second by default.
|
/// resolution. Set to 5 second by default.
|
||||||
pub fn timeout(mut self, timeout: Duration) -> Self {
|
pub fn timeout(mut self, timeout: Duration) -> Self {
|
||||||
self.config.timeout = timeout;
|
self.config.timeout = timeout;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tls handshake timeout, i.e. max time to do tls handshake with remote host after tcp
|
||||||
|
/// connection established. Set to 5 second by default.
|
||||||
|
pub fn handshake_timeout(mut self, timeout: Duration) -> Self {
|
||||||
|
self.config.handshake_timeout = timeout;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
/// Use custom `SslConnector` instance.
|
/// Use custom `SslConnector` instance.
|
||||||
pub fn ssl(mut self, connector: OpensslConnector) -> Self {
|
pub fn ssl(mut self, connector: OpensslConnector) -> Self {
|
||||||
@ -157,7 +167,7 @@ where
|
|||||||
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
/// Use custom `SslConnector` instance.
|
/// Use custom `SslConnector` instance.
|
||||||
pub fn rustls(mut self, connector: Arc<ClientConfig>) -> Self {
|
pub fn rustls(mut self, connector: std::sync::Arc<ClientConfig>) -> Self {
|
||||||
self.ssl = SslConnector::Rustls(connector);
|
self.ssl = SslConnector::Rustls(connector);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -247,158 +257,369 @@ where
|
|||||||
/// Finish configuration process and create connector service.
|
/// Finish configuration process and create connector service.
|
||||||
/// The Connector builder always concludes by calling `finish()` last in
|
/// The Connector builder always concludes by calling `finish()` last in
|
||||||
/// its combinator chain.
|
/// its combinator chain.
|
||||||
pub fn finish(
|
pub fn finish(self) -> ConnectorService<S, Io> {
|
||||||
self,
|
|
||||||
) -> impl Service<Connect, Response = impl Connection, Error = ConnectError> {
|
|
||||||
let local_address = self.config.local_address;
|
let local_address = self.config.local_address;
|
||||||
let timeout = self.config.timeout;
|
let timeout = self.config.timeout;
|
||||||
|
|
||||||
let tcp_service = TimeoutService::new(
|
let tcp_service_inner =
|
||||||
timeout,
|
TcpConnectorInnerService::new(self.connector, timeout, local_address);
|
||||||
apply_fn(self.connector.clone(), move |msg: Connect, srv| {
|
|
||||||
let mut req = TcpConnect::new(msg.uri).set_addr(msg.addr);
|
|
||||||
|
|
||||||
if let Some(local_addr) = local_address {
|
#[allow(clippy::redundant_clone)]
|
||||||
req = req.set_local_addr(local_addr);
|
let tcp_service = TcpConnectorService {
|
||||||
|
service: tcp_service_inner.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let tls_service = match self.ssl {
|
||||||
|
SslConnector::None => None,
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
SslConnector::Openssl(tls) => {
|
||||||
|
const H2: &[u8] = b"h2";
|
||||||
|
|
||||||
|
use actix_tls::connect::ssl::openssl::{OpensslConnector, SslStream};
|
||||||
|
|
||||||
|
impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, SslStream<Io>> {
|
||||||
|
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {
|
||||||
|
let sock = self.into_parts().0;
|
||||||
|
let h2 = sock
|
||||||
|
.ssl()
|
||||||
|
.selected_alpn_protocol()
|
||||||
|
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||||
|
.unwrap_or(false);
|
||||||
|
if h2 {
|
||||||
|
(Box::new(sock), Protocol::Http2)
|
||||||
|
} else {
|
||||||
|
(Box::new(sock), Protocol::Http1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.call(req)
|
let handshake_timeout = self.config.handshake_timeout;
|
||||||
})
|
|
||||||
.map_err(ConnectError::from)
|
|
||||||
.map(|stream| (stream.into_parts().0, Protocol::Http1)),
|
|
||||||
)
|
|
||||||
.map_err(|e| match e {
|
|
||||||
TimeoutError::Service(e) => e,
|
|
||||||
TimeoutError::Timeout => ConnectError::Timeout,
|
|
||||||
});
|
|
||||||
|
|
||||||
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
let tls_service = TlsConnectorService {
|
||||||
{
|
tcp_service: tcp_service_inner,
|
||||||
// A dummy service for annotate tls pool's type signature.
|
tls_service: OpensslConnector::service(tls),
|
||||||
pub type DummyService = Box<
|
timeout: handshake_timeout,
|
||||||
dyn Service<
|
};
|
||||||
Connect,
|
|
||||||
Response = (Box<dyn ConnectionIo>, Protocol),
|
|
||||||
Error = ConnectError,
|
|
||||||
Future = futures_core::future::LocalBoxFuture<
|
|
||||||
'static,
|
|
||||||
Result<(Box<dyn ConnectionIo>, Protocol), ConnectError>,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
InnerConnector::<_, DummyService, _, Box<dyn ConnectionIo>> {
|
Some(actix_service::boxed::rc_service(tls_service))
|
||||||
tcp_pool: ConnectionPool::new(
|
|
||||||
tcp_service,
|
|
||||||
self.config.no_disconnect_timeout(),
|
|
||||||
),
|
|
||||||
tls_pool: None,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(feature = "openssl", feature = "rustls"))]
|
|
||||||
{
|
|
||||||
const H2: &[u8] = b"h2";
|
|
||||||
use actix_service::{boxed::service, pipeline};
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
use actix_tls::connect::ssl::openssl::OpensslConnector;
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
use actix_tls::connect::ssl::rustls::{RustlsConnector, Session};
|
SslConnector::Rustls(tls) => {
|
||||||
|
const H2: &[u8] = b"h2";
|
||||||
|
|
||||||
let ssl_service = TimeoutService::new(
|
use actix_tls::connect::ssl::rustls::{
|
||||||
timeout,
|
RustlsConnector, Session, TlsStream,
|
||||||
pipeline(
|
};
|
||||||
apply_fn(self.connector.clone(), move |msg: Connect, srv| {
|
|
||||||
let mut req = TcpConnect::new(msg.uri).set_addr(msg.addr);
|
|
||||||
|
|
||||||
if let Some(local_addr) = local_address {
|
impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, TlsStream<Io>> {
|
||||||
req = req.set_local_addr(local_addr);
|
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {
|
||||||
|
let sock = self.into_parts().0;
|
||||||
|
let h2 = sock
|
||||||
|
.get_ref()
|
||||||
|
.1
|
||||||
|
.get_alpn_protocol()
|
||||||
|
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||||
|
.unwrap_or(false);
|
||||||
|
if h2 {
|
||||||
|
(Box::new(sock), Protocol::Http2)
|
||||||
|
} else {
|
||||||
|
(Box::new(sock), Protocol::Http1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
srv.call(req)
|
let handshake_timeout = self.config.handshake_timeout;
|
||||||
})
|
|
||||||
.map_err(ConnectError::from),
|
|
||||||
)
|
|
||||||
.and_then(match self.ssl {
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
SslConnector::Openssl(ssl) => service(
|
|
||||||
OpensslConnector::service(ssl)
|
|
||||||
.map(|stream| {
|
|
||||||
let sock = stream.into_parts().0;
|
|
||||||
let h2 = sock
|
|
||||||
.ssl()
|
|
||||||
.selected_alpn_protocol()
|
|
||||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
|
||||||
.unwrap_or(false);
|
|
||||||
if h2 {
|
|
||||||
(
|
|
||||||
Box::new(sock) as Box<dyn ConnectionIo>,
|
|
||||||
Protocol::Http2,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(Box::new(sock) as _, Protocol::Http1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map_err(ConnectError::from),
|
|
||||||
),
|
|
||||||
#[cfg(feature = "rustls")]
|
|
||||||
SslConnector::Rustls(ssl) => service(
|
|
||||||
RustlsConnector::service(ssl)
|
|
||||||
.map_err(ConnectError::from)
|
|
||||||
.map(|stream| {
|
|
||||||
let sock = stream.into_parts().0;
|
|
||||||
let h2 = sock
|
|
||||||
.get_ref()
|
|
||||||
.1
|
|
||||||
.get_alpn_protocol()
|
|
||||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
|
||||||
.unwrap_or(false);
|
|
||||||
if h2 {
|
|
||||||
(Box::new(sock) as _, Protocol::Http2)
|
|
||||||
} else {
|
|
||||||
(Box::new(sock) as _, Protocol::Http1)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.map_err(|e| match e {
|
|
||||||
TimeoutError::Service(e) => e,
|
|
||||||
TimeoutError::Timeout => ConnectError::Timeout,
|
|
||||||
});
|
|
||||||
|
|
||||||
InnerConnector {
|
let tls_service = TlsConnectorService {
|
||||||
tcp_pool: ConnectionPool::new(
|
tcp_service: tcp_service_inner,
|
||||||
tcp_service,
|
tls_service: RustlsConnector::service(tls),
|
||||||
self.config.no_disconnect_timeout(),
|
timeout: handshake_timeout,
|
||||||
),
|
};
|
||||||
tls_pool: Some(ConnectionPool::new(ssl_service, self.config)),
|
|
||||||
|
Some(actix_service::boxed::rc_service(tls_service))
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let tcp_config = self.config.no_disconnect_timeout();
|
||||||
|
|
||||||
|
let tcp_pool = ConnectionPool::new(tcp_service, tcp_config);
|
||||||
|
|
||||||
|
let tls_config = self.config;
|
||||||
|
let tls_pool = tls_service
|
||||||
|
.map(move |tls_service| ConnectionPool::new(tls_service, tls_config));
|
||||||
|
|
||||||
|
ConnectorServicePriv { tcp_pool, tls_pool }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// tcp service for map `TcpConnection<Uri, Io>` type to `(Io, Protocol)`
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TcpConnectorService<S: Clone> {
|
||||||
|
service: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, Io> Service<Connect> for TcpConnectorService<S>
|
||||||
|
where
|
||||||
|
S: Service<Connect, Response = TcpConnection<Uri, Io>, Error = ConnectError>
|
||||||
|
+ Clone
|
||||||
|
+ 'static,
|
||||||
|
{
|
||||||
|
type Response = (Io, Protocol);
|
||||||
|
type Error = ConnectError;
|
||||||
|
type Future = TcpConnectorFuture<S::Future>;
|
||||||
|
|
||||||
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
|
fn call(&self, req: Connect) -> Self::Future {
|
||||||
|
TcpConnectorFuture {
|
||||||
|
fut: self.service.call(req),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InnerConnector<S1, S2, Io1, Io2>
|
#[pin_project]
|
||||||
|
pub struct TcpConnectorFuture<Fut> {
|
||||||
|
#[pin]
|
||||||
|
fut: Fut,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut, Io> Future for TcpConnectorFuture<Fut>
|
||||||
where
|
where
|
||||||
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + 'static,
|
Fut: Future<Output = Result<TcpConnection<Uri, Io>, ConnectError>>,
|
||||||
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + 'static,
|
{
|
||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
type Output = Result<(Io, Protocol), ConnectError>;
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
self.project()
|
||||||
|
.fut
|
||||||
|
.poll(cx)
|
||||||
|
.map_ok(|res| (res.into_parts().0, Protocol::Http1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// service for establish tcp connection and do client tls handshake.
|
||||||
|
/// operation is canceled when timeout limit reached.
|
||||||
|
struct TlsConnectorService<S, St> {
|
||||||
|
/// tcp connection is canceled on `TcpConnectorInnerService`'s timeout setting.
|
||||||
|
tcp_service: S,
|
||||||
|
/// tls connection is canceled on `TlsConnectorService`'s timeout setting.
|
||||||
|
tls_service: St,
|
||||||
|
timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, St, Io, Res> Service<Connect> for TlsConnectorService<S, St>
|
||||||
|
where
|
||||||
|
S: Service<Connect, Response = TcpConnection<Uri, Io>, Error = ConnectError>
|
||||||
|
+ Clone
|
||||||
|
+ 'static,
|
||||||
|
St: Service<TcpConnection<Uri, Io>, Response = Res, Error = std::io::Error>
|
||||||
|
+ Clone
|
||||||
|
+ 'static,
|
||||||
|
Io: ConnectionIo,
|
||||||
|
Res: IntoConnectionIo,
|
||||||
|
{
|
||||||
|
type Response = (Box<dyn ConnectionIo>, Protocol);
|
||||||
|
type Error = ConnectError;
|
||||||
|
type Future = TlsConnectorFuture<St, S::Future, St::Future>;
|
||||||
|
|
||||||
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
ready!(self.tcp_service.poll_ready(cx))?;
|
||||||
|
ready!(self.tls_service.poll_ready(cx))?;
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, req: Connect) -> Self::Future {
|
||||||
|
let fut = self.tcp_service.call(req);
|
||||||
|
let tls_service = self.tls_service.clone();
|
||||||
|
let timeout = self.timeout;
|
||||||
|
|
||||||
|
TlsConnectorFuture::TcpConnect {
|
||||||
|
fut,
|
||||||
|
tls_service: Some(tls_service),
|
||||||
|
timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pin_project(project = TlsConnectorProj)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
enum TlsConnectorFuture<S, Fut1, Fut2> {
|
||||||
|
TcpConnect {
|
||||||
|
#[pin]
|
||||||
|
fut: Fut1,
|
||||||
|
tls_service: Option<S>,
|
||||||
|
timeout: Duration,
|
||||||
|
},
|
||||||
|
TlsConnect {
|
||||||
|
#[pin]
|
||||||
|
fut: Fut2,
|
||||||
|
#[pin]
|
||||||
|
timeout: Sleep,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// helper trait for generic over different TlsStream types between tls crates.
|
||||||
|
trait IntoConnectionIo {
|
||||||
|
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, Io, Fut1, Fut2, Res> Future for TlsConnectorFuture<S, Fut1, Fut2>
|
||||||
|
where
|
||||||
|
S: Service<
|
||||||
|
TcpConnection<Uri, Io>,
|
||||||
|
Response = Res,
|
||||||
|
Error = std::io::Error,
|
||||||
|
Future = Fut2,
|
||||||
|
>,
|
||||||
|
Fut1: Future<Output = Result<TcpConnection<Uri, Io>, ConnectError>>,
|
||||||
|
Fut2: Future<Output = Result<S::Response, S::Error>>,
|
||||||
|
Io: ConnectionIo,
|
||||||
|
Res: IntoConnectionIo,
|
||||||
|
{
|
||||||
|
type Output = Result<(Box<dyn ConnectionIo>, Protocol), ConnectError>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match self.as_mut().project() {
|
||||||
|
TlsConnectorProj::TcpConnect {
|
||||||
|
fut,
|
||||||
|
tls_service,
|
||||||
|
timeout,
|
||||||
|
} => {
|
||||||
|
let res = ready!(fut.poll(cx))?;
|
||||||
|
let fut = tls_service
|
||||||
|
.take()
|
||||||
|
.expect("TlsConnectorFuture polled after complete")
|
||||||
|
.call(res);
|
||||||
|
let timeout = sleep(*timeout);
|
||||||
|
self.set(TlsConnectorFuture::TlsConnect { fut, timeout });
|
||||||
|
self.poll(cx)
|
||||||
|
}
|
||||||
|
TlsConnectorProj::TlsConnect { fut, timeout } => match fut.poll(cx)? {
|
||||||
|
Poll::Ready(res) => Poll::Ready(Ok(res.into_connection_io())),
|
||||||
|
Poll::Pending => timeout.poll(cx).map(|_| Err(ConnectError::Timeout)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// service for establish tcp connection.
|
||||||
|
/// operation is canceled when timeout limit reached.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TcpConnectorInnerService<S: Clone> {
|
||||||
|
service: S,
|
||||||
|
timeout: Duration,
|
||||||
|
local_address: Option<std::net::IpAddr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Clone> TcpConnectorInnerService<S> {
|
||||||
|
fn new(
|
||||||
|
service: S,
|
||||||
|
timeout: Duration,
|
||||||
|
local_address: Option<std::net::IpAddr>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
service,
|
||||||
|
timeout,
|
||||||
|
local_address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, Io> Service<Connect> for TcpConnectorInnerService<S>
|
||||||
|
where
|
||||||
|
S: Service<
|
||||||
|
TcpConnect<Uri>,
|
||||||
|
Response = TcpConnection<Uri, Io>,
|
||||||
|
Error = TcpConnectError,
|
||||||
|
> + Clone
|
||||||
|
+ 'static,
|
||||||
|
{
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = ConnectError;
|
||||||
|
type Future = TcpConnectorInnerFuture<S::Future>;
|
||||||
|
|
||||||
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
|
fn call(&self, req: Connect) -> Self::Future {
|
||||||
|
let mut req = TcpConnect::new(req.uri).set_addr(req.addr);
|
||||||
|
|
||||||
|
if let Some(local_addr) = self.local_address {
|
||||||
|
req = req.set_local_addr(local_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TcpConnectorInnerFuture {
|
||||||
|
fut: self.service.call(req),
|
||||||
|
timeout: sleep(self.timeout),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pin_project]
|
||||||
|
pub struct TcpConnectorInnerFuture<Fut> {
|
||||||
|
#[pin]
|
||||||
|
fut: Fut,
|
||||||
|
#[pin]
|
||||||
|
timeout: Sleep,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut, Io> Future for TcpConnectorInnerFuture<Fut>
|
||||||
|
where
|
||||||
|
Fut: Future<Output = Result<TcpConnection<Uri, Io>, TcpConnectError>>,
|
||||||
|
{
|
||||||
|
type Output = Result<TcpConnection<Uri, Io>, ConnectError>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
match this.fut.poll(cx) {
|
||||||
|
Poll::Ready(res) => Poll::Ready(res.map_err(ConnectError::from)),
|
||||||
|
Poll::Pending => this.timeout.poll(cx).map(|_| Err(ConnectError::Timeout)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connector service for pooled Plain/Tls Tcp connections.
|
||||||
|
pub type ConnectorService<S, Io> = ConnectorServicePriv<
|
||||||
|
TcpConnectorService<TcpConnectorInnerService<S>>,
|
||||||
|
Rc<
|
||||||
|
dyn Service<
|
||||||
|
Connect,
|
||||||
|
Response = (Box<dyn ConnectionIo>, Protocol),
|
||||||
|
Error = ConnectError,
|
||||||
|
Future = LocalBoxFuture<
|
||||||
|
'static,
|
||||||
|
Result<(Box<dyn ConnectionIo>, Protocol), ConnectError>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
Io,
|
||||||
|
Box<dyn ConnectionIo>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
pub struct ConnectorServicePriv<S1, S2, Io1, Io2>
|
||||||
|
where
|
||||||
|
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>,
|
||||||
|
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>,
|
||||||
|
Io1: ConnectionIo,
|
||||||
|
Io2: ConnectionIo,
|
||||||
{
|
{
|
||||||
tcp_pool: ConnectionPool<S1, Io1>,
|
tcp_pool: ConnectionPool<S1, Io1>,
|
||||||
tls_pool: Option<ConnectionPool<S2, Io2>>,
|
tls_pool: Option<ConnectionPool<S2, Io2>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S1, S2, Io1, Io2> Service<Connect> for InnerConnector<S1, S2, Io1, Io2>
|
impl<S1, S2, Io1, Io2> Service<Connect> for ConnectorServicePriv<S1, S2, Io1, Io2>
|
||||||
where
|
where
|
||||||
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + 'static,
|
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
|
||||||
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + 'static,
|
+ Clone
|
||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
+ 'static,
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
|
||||||
|
+ Clone
|
||||||
|
+ 'static,
|
||||||
|
Io1: ConnectionIo,
|
||||||
|
Io2: ConnectionIo,
|
||||||
{
|
{
|
||||||
type Response = EitherIoConnection<Io1, Io2>;
|
type Response = Connection<Io1, Io2>;
|
||||||
type Error = ConnectError;
|
type Error = ConnectError;
|
||||||
type Future = InnerConnectorResponse<S1, S2, Io1, Io2>;
|
type Future = ConnectorServiceFuture<S1, S2, Io1, Io2>;
|
||||||
|
|
||||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
ready!(self.tcp_pool.poll_ready(cx))?;
|
ready!(self.tcp_pool.poll_ready(cx))?;
|
||||||
@ -411,41 +632,49 @@ where
|
|||||||
fn call(&self, req: Connect) -> Self::Future {
|
fn call(&self, req: Connect) -> Self::Future {
|
||||||
match req.uri.scheme_str() {
|
match req.uri.scheme_str() {
|
||||||
Some("https") | Some("wss") => match self.tls_pool {
|
Some("https") | Some("wss") => match self.tls_pool {
|
||||||
None => InnerConnectorResponse::SslIsNotSupported,
|
None => ConnectorServiceFuture::SslIsNotSupported,
|
||||||
Some(ref pool) => InnerConnectorResponse::Io2(pool.call(req)),
|
Some(ref pool) => ConnectorServiceFuture::Tls(pool.call(req)),
|
||||||
},
|
},
|
||||||
_ => InnerConnectorResponse::Io1(self.tcp_pool.call(req)),
|
_ => ConnectorServiceFuture::Tcp(self.tcp_pool.call(req)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project::pin_project(project = InnerConnectorProj)]
|
#[pin_project(project = ConnectorServiceProj)]
|
||||||
enum InnerConnectorResponse<S1, S2, Io1, Io2>
|
pub enum ConnectorServiceFuture<S1, S2, Io1, Io2>
|
||||||
where
|
where
|
||||||
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + 'static,
|
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
|
||||||
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + 'static,
|
+ Clone
|
||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
+ 'static,
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
|
||||||
|
+ Clone
|
||||||
|
+ 'static,
|
||||||
|
Io1: ConnectionIo,
|
||||||
|
Io2: ConnectionIo,
|
||||||
{
|
{
|
||||||
Io1(#[pin] <ConnectionPool<S1, Io1> as Service<Connect>>::Future),
|
Tcp(#[pin] <ConnectionPool<S1, Io1> as Service<Connect>>::Future),
|
||||||
Io2(#[pin] <ConnectionPool<S2, Io2> as Service<Connect>>::Future),
|
Tls(#[pin] <ConnectionPool<S2, Io2> as Service<Connect>>::Future),
|
||||||
SslIsNotSupported,
|
SslIsNotSupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S1, S2, Io1, Io2> Future for InnerConnectorResponse<S1, S2, Io1, Io2>
|
impl<S1, S2, Io1, Io2> Future for ConnectorServiceFuture<S1, S2, Io1, Io2>
|
||||||
where
|
where
|
||||||
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + 'static,
|
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
|
||||||
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + 'static,
|
+ Clone
|
||||||
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
|
+ 'static,
|
||||||
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
|
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
|
||||||
|
+ Clone
|
||||||
|
+ 'static,
|
||||||
|
Io1: ConnectionIo,
|
||||||
|
Io2: ConnectionIo,
|
||||||
{
|
{
|
||||||
type Output = Result<EitherIoConnection<Io1, Io2>, ConnectError>;
|
type Output = Result<Connection<Io1, Io2>, ConnectError>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
match self.project() {
|
match self.project() {
|
||||||
InnerConnectorProj::Io1(fut) => fut.poll(cx).map_ok(EitherIoConnection::A),
|
ConnectorServiceProj::Tcp(fut) => fut.poll(cx).map_ok(Connection::Tcp),
|
||||||
InnerConnectorProj::Io2(fut) => fut.poll(cx).map_ok(EitherIoConnection::B),
|
ConnectorServiceProj::Tls(fut) => fut.poll(cx).map_ok(Connection::Tls),
|
||||||
InnerConnectorProj::SslIsNotSupported => {
|
ConnectorServiceProj::SslIsNotSupported => {
|
||||||
Poll::Ready(Err(ConnectError::SslIsNotSupported))
|
Poll::Ready(Err(ConnectError::SslIsNotSupported))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use std::io::Write;
|
use std::{
|
||||||
use std::pin::Pin;
|
io::Write,
|
||||||
use std::task::{Context, Poll};
|
pin::Pin,
|
||||||
use std::{io, time};
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf};
|
use actix_codec::Framed;
|
||||||
use bytes::buf::BufMut;
|
use bytes::buf::BufMut;
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
@ -11,28 +12,24 @@ use futures_util::{future::poll_fn, SinkExt as _};
|
|||||||
|
|
||||||
use crate::error::PayloadError;
|
use crate::error::PayloadError;
|
||||||
use crate::h1;
|
use crate::h1;
|
||||||
use crate::header::HeaderMap;
|
|
||||||
use crate::http::{
|
use crate::http::{
|
||||||
header::{IntoHeaderValue, EXPECT, HOST},
|
header::{HeaderMap, IntoHeaderValue, EXPECT, HOST},
|
||||||
StatusCode,
|
StatusCode,
|
||||||
};
|
};
|
||||||
use crate::message::{RequestHeadType, ResponseHead};
|
use crate::message::{RequestHeadType, ResponseHead};
|
||||||
use crate::payload::{Payload, PayloadStream};
|
use crate::payload::{Payload, PayloadStream};
|
||||||
|
|
||||||
use super::connection::ConnectionType;
|
use super::connection::{ConnectionIo, H1Connection};
|
||||||
use super::error::{ConnectError, SendRequestError};
|
use super::error::{ConnectError, SendRequestError};
|
||||||
use super::pool::Acquired;
|
|
||||||
use crate::body::{BodySize, MessageBody};
|
use crate::body::{BodySize, MessageBody};
|
||||||
|
|
||||||
pub(crate) async fn send_request<T, B>(
|
pub(crate) async fn send_request<Io, B>(
|
||||||
io: T,
|
io: H1Connection<Io>,
|
||||||
mut head: RequestHeadType,
|
mut head: RequestHeadType,
|
||||||
body: B,
|
body: B,
|
||||||
created: time::Instant,
|
|
||||||
acquired: Acquired<T>,
|
|
||||||
) -> Result<(ResponseHead, Payload), SendRequestError>
|
) -> Result<(ResponseHead, Payload), SendRequestError>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: ConnectionIo,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
// set request host header
|
// set request host header
|
||||||
@ -62,12 +59,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let io = H1Connection {
|
|
||||||
created,
|
|
||||||
acquired,
|
|
||||||
io: Some(io),
|
|
||||||
};
|
|
||||||
|
|
||||||
// create Framed and prepare sending request
|
// create Framed and prepare sending request
|
||||||
let mut framed = Framed::new(io, h1::ClientCodec::default());
|
let mut framed = Framed::new(io, h1::ClientCodec::default());
|
||||||
|
|
||||||
@ -138,18 +129,18 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn open_tunnel<T>(
|
pub(crate) async fn open_tunnel<Io>(
|
||||||
io: T,
|
io: Io,
|
||||||
head: RequestHeadType,
|
head: RequestHeadType,
|
||||||
) -> Result<(ResponseHead, Framed<T, h1::ClientCodec>), SendRequestError>
|
) -> Result<(ResponseHead, Framed<Io, h1::ClientCodec>), SendRequestError>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: ConnectionIo,
|
||||||
{
|
{
|
||||||
// create Framed and send request
|
// create Framed and send request.
|
||||||
let mut framed = Framed::new(io, h1::ClientCodec::default());
|
let mut framed = Framed::new(io, h1::ClientCodec::default());
|
||||||
framed.send((head, BodySize::None).into()).await?;
|
framed.send((head, BodySize::None).into()).await?;
|
||||||
|
|
||||||
// read response
|
// read response head.
|
||||||
let head = poll_fn(|cx| Pin::new(&mut framed).poll_next(cx))
|
let head = poll_fn(|cx| Pin::new(&mut framed).poll_next(cx))
|
||||||
.await
|
.await
|
||||||
.ok_or(ConnectError::Disconnected)??;
|
.ok_or(ConnectError::Disconnected)??;
|
||||||
@ -158,12 +149,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// send request body to the peer
|
/// send request body to the peer
|
||||||
pub(crate) async fn send_body<T, B>(
|
pub(crate) async fn send_body<Io, B>(
|
||||||
body: B,
|
body: B,
|
||||||
mut framed: Pin<&mut Framed<T, h1::ClientCodec>>,
|
mut framed: Pin<&mut Framed<Io, h1::ClientCodec>>,
|
||||||
) -> Result<(), SendRequestError>
|
) -> Result<(), SendRequestError>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: ConnectionIo,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
actix_rt::pin!(body);
|
actix_rt::pin!(body);
|
||||||
@ -202,92 +193,16 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
/// HTTP client connection
|
|
||||||
pub struct H1Connection<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
/// T should be `Unpin`
|
|
||||||
io: Option<T>,
|
|
||||||
created: time::Instant,
|
|
||||||
acquired: Acquired<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> H1Connection<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
fn on_release(&mut self, keep_alive: bool) {
|
|
||||||
if keep_alive {
|
|
||||||
self.release();
|
|
||||||
} else {
|
|
||||||
self.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Close connection
|
|
||||||
fn close(&mut self) {
|
|
||||||
if let Some(io) = self.io.take() {
|
|
||||||
self.acquired.close(ConnectionType::H1(io));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Release this connection to the connection pool
|
|
||||||
fn release(&mut self) {
|
|
||||||
if let Some(io) = self.io.take() {
|
|
||||||
self.acquired.release(ConnectionType::H1(io), self.created);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> AsyncRead for H1Connection<T> {
|
|
||||||
fn poll_read(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &mut ReadBuf<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
Pin::new(&mut self.io.as_mut().unwrap()).poll_read(cx, buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> AsyncWrite for H1Connection<T> {
|
|
||||||
fn poll_write(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
Pin::new(&mut self.io.as_mut().unwrap()).poll_write(cx, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
Pin::new(self.io.as_mut().unwrap()).poll_flush(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_shutdown(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Result<(), io::Error>> {
|
|
||||||
Pin::new(self.io.as_mut().unwrap()).poll_shutdown(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pin_project::pin_project]
|
#[pin_project::pin_project]
|
||||||
pub(crate) struct PlStream<Io>
|
pub(crate) struct PlStream<Io: ConnectionIo>
|
||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: ConnectionIo,
|
||||||
{
|
{
|
||||||
#[pin]
|
#[pin]
|
||||||
framed: Option<Framed<H1Connection<Io>, h1::ClientPayloadCodec>>,
|
framed: Option<Framed<H1Connection<Io>, h1::ClientPayloadCodec>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io> PlStream<Io>
|
impl<Io: ConnectionIo> PlStream<Io> {
|
||||||
where
|
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
fn new(framed: Framed<H1Connection<Io>, h1::ClientCodec>) -> Self {
|
fn new(framed: Framed<H1Connection<Io>, h1::ClientCodec>) -> Self {
|
||||||
let framed = framed.into_map_codec(|codec| codec.into_payload_codec());
|
let framed = framed.into_map_codec(|codec| codec.into_payload_codec());
|
||||||
|
|
||||||
@ -297,10 +212,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io> Stream for PlStream<Io>
|
impl<Io: ConnectionIo> Stream for PlStream<Io> {
|
||||||
where
|
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
type Item = Result<Bytes, PayloadError>;
|
type Item = Result<Bytes, PayloadError>;
|
||||||
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::time;
|
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite};
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_util::future::poll_fn;
|
use futures_util::future::poll_fn;
|
||||||
use h2::{
|
use h2::{
|
||||||
@ -17,20 +15,16 @@ use crate::message::{RequestHeadType, ResponseHead};
|
|||||||
use crate::payload::Payload;
|
use crate::payload::Payload;
|
||||||
|
|
||||||
use super::config::ConnectorConfig;
|
use super::config::ConnectorConfig;
|
||||||
use super::connection::ConnectionType;
|
use super::connection::{ConnectionIo, H2Connection};
|
||||||
use super::error::SendRequestError;
|
use super::error::SendRequestError;
|
||||||
use super::pool::Acquired;
|
|
||||||
use crate::client::connection::H2Connection;
|
|
||||||
|
|
||||||
pub(crate) async fn send_request<T, B>(
|
pub(crate) async fn send_request<Io, B>(
|
||||||
mut io: H2Connection,
|
mut io: H2Connection<Io>,
|
||||||
head: RequestHeadType,
|
head: RequestHeadType,
|
||||||
body: B,
|
body: B,
|
||||||
created: time::Instant,
|
|
||||||
acquired: Acquired<T>,
|
|
||||||
) -> Result<(ResponseHead, Payload), SendRequestError>
|
) -> Result<(ResponseHead, Payload), SendRequestError>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: ConnectionIo,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
trace!("Sending client request: {:?} {:?}", head, body.size());
|
trace!("Sending client request: {:?} {:?}", head, body.size());
|
||||||
@ -103,13 +97,13 @@ where
|
|||||||
|
|
||||||
let res = poll_fn(|cx| io.poll_ready(cx)).await;
|
let res = poll_fn(|cx| io.poll_ready(cx)).await;
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
release(io, acquired, created, e.is_io());
|
io.on_release(e.is_io());
|
||||||
return Err(SendRequestError::from(e));
|
return Err(SendRequestError::from(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp = match io.send_request(req, eof) {
|
let resp = match io.send_request(req, eof) {
|
||||||
Ok((fut, send)) => {
|
Ok((fut, send)) => {
|
||||||
release(io, acquired, created, false);
|
io.on_release(false);
|
||||||
|
|
||||||
if !eof {
|
if !eof {
|
||||||
send_body(body, send).await?;
|
send_body(body, send).await?;
|
||||||
@ -117,7 +111,7 @@ where
|
|||||||
fut.await.map_err(SendRequestError::from)?
|
fut.await.map_err(SendRequestError::from)?
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
release(io, acquired, created, e.is_io());
|
io.on_release(e.is_io());
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -178,26 +172,10 @@ async fn send_body<B: MessageBody>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// release SendRequest object
|
pub(crate) fn handshake<Io: ConnectionIo>(
|
||||||
fn release<T: AsyncRead + AsyncWrite + Unpin + 'static>(
|
|
||||||
io: H2Connection,
|
|
||||||
acquired: Acquired<T>,
|
|
||||||
created: time::Instant,
|
|
||||||
close: bool,
|
|
||||||
) {
|
|
||||||
if close {
|
|
||||||
acquired.close(ConnectionType::H2(io));
|
|
||||||
} else {
|
|
||||||
acquired.release(ConnectionType::H2(io), created);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn handshake<Io>(
|
|
||||||
io: Io,
|
io: Io,
|
||||||
config: &ConnectorConfig,
|
config: &ConnectorConfig,
|
||||||
) -> impl Future<Output = Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>>
|
) -> impl Future<Output = Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>>
|
||||||
where
|
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
{
|
||||||
let mut builder = Builder::new();
|
let mut builder = Builder::new();
|
||||||
builder
|
builder
|
||||||
|
@ -15,7 +15,7 @@ pub use actix_tls::connect::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub use self::connection::{Connection, ConnectionIo};
|
pub use self::connection::{Connection, ConnectionIo};
|
||||||
pub use self::connector::Connector;
|
pub use self::connector::{Connector, ConnectorService};
|
||||||
pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
|
pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
|
||||||
pub use crate::Protocol;
|
pub use crate::Protocol;
|
||||||
|
|
||||||
|
@ -1,34 +1,38 @@
|
|||||||
//! Client connection pooling keyed on the authority part of the connection URI.
|
//! Client connection pooling keyed on the authority part of the connection URI.
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::{
|
||||||
use std::future::Future;
|
cell::RefCell,
|
||||||
use std::ops::Deref;
|
collections::VecDeque,
|
||||||
use std::pin::Pin;
|
future::Future,
|
||||||
use std::rc::Rc;
|
io,
|
||||||
use std::sync::Arc;
|
ops::Deref,
|
||||||
use std::task::{Context, Poll};
|
pin::Pin,
|
||||||
use std::time::{Duration, Instant};
|
rc::Rc,
|
||||||
use std::{cell::RefCell, io};
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite};
|
use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
use actix_rt::time::{sleep, Sleep};
|
use actix_rt::time::{sleep, Sleep};
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use http::uri::Authority;
|
use http::uri::Authority;
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
use tokio::io::ReadBuf;
|
|
||||||
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
|
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
|
||||||
|
|
||||||
use super::config::ConnectorConfig;
|
use super::config::ConnectorConfig;
|
||||||
use super::connection::{ConnectionType, H2Connection, IoConnection};
|
use super::connection::{
|
||||||
|
ConnectionInnerType, ConnectionIo, ConnectionType, H2ConnectionInner,
|
||||||
|
};
|
||||||
use super::error::ConnectError;
|
use super::error::ConnectError;
|
||||||
use super::h2proto::handshake;
|
use super::h2proto::handshake;
|
||||||
use super::Connect;
|
use super::Connect;
|
||||||
use super::Protocol;
|
use super::Protocol;
|
||||||
|
|
||||||
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
|
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
|
||||||
pub(crate) struct Key {
|
pub struct Key {
|
||||||
authority: Authority,
|
authority: Authority,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,17 +42,18 @@ impl From<Authority> for Key {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
/// Connections pool for reuse Io type for certain [`http::uri::Authority`] as key.
|
/// Connections pool for reuse Io type for certain [`http::uri::Authority`] as key.
|
||||||
pub(crate) struct ConnectionPool<S, Io>
|
pub struct ConnectionPool<S, Io>
|
||||||
where
|
where
|
||||||
Io: AsyncWrite + Unpin + 'static,
|
Io: AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
connector: Rc<S>,
|
connector: S,
|
||||||
inner: ConnectionPoolInner<Io>,
|
inner: ConnectionPoolInner<Io>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// wrapper type for check the ref count of Rc.
|
/// wrapper type for check the ref count of Rc.
|
||||||
struct ConnectionPoolInner<Io>(Rc<ConnectionPoolInnerPriv<Io>>)
|
pub struct ConnectionPoolInner<Io>(Rc<ConnectionPoolInnerPriv<Io>>)
|
||||||
where
|
where
|
||||||
Io: AsyncWrite + Unpin + 'static;
|
Io: AsyncWrite + Unpin + 'static;
|
||||||
|
|
||||||
@ -56,10 +61,21 @@ impl<Io> ConnectionPoolInner<Io>
|
|||||||
where
|
where
|
||||||
Io: AsyncWrite + Unpin + 'static,
|
Io: AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
|
fn new(config: ConnectorConfig) -> Self {
|
||||||
|
let permits = Arc::new(Semaphore::new(config.limit));
|
||||||
|
let available = RefCell::new(AHashMap::default());
|
||||||
|
|
||||||
|
Self(Rc::new(ConnectionPoolInnerPriv {
|
||||||
|
config,
|
||||||
|
available,
|
||||||
|
permits,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/// spawn a async for graceful shutdown h1 Io type with a timeout.
|
/// spawn a async for graceful shutdown h1 Io type with a timeout.
|
||||||
fn close(&self, conn: ConnectionType<Io>) {
|
fn close(&self, conn: ConnectionInnerType<Io>) {
|
||||||
if let Some(timeout) = self.config.disconnect_timeout {
|
if let Some(timeout) = self.config.disconnect_timeout {
|
||||||
if let ConnectionType::H1(io) = conn {
|
if let ConnectionInnerType::H1(io) = conn {
|
||||||
actix_rt::spawn(CloseConnection::new(io, timeout));
|
actix_rt::spawn(CloseConnection::new(io, timeout));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +120,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConnectionPoolInnerPriv<Io>
|
pub struct ConnectionPoolInnerPriv<Io>
|
||||||
where
|
where
|
||||||
Io: AsyncWrite + Unpin + 'static,
|
Io: AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
@ -128,15 +144,7 @@ where
|
|||||||
/// Any requests beyond limit would be wait in fifo order and get notified in async manner
|
/// Any requests beyond limit would be wait in fifo order and get notified in async manner
|
||||||
/// by [`tokio::sync::Semaphore`]
|
/// by [`tokio::sync::Semaphore`]
|
||||||
pub(crate) fn new(connector: S, config: ConnectorConfig) -> Self {
|
pub(crate) fn new(connector: S, config: ConnectorConfig) -> Self {
|
||||||
let permits = Arc::new(Semaphore::new(config.limit));
|
let inner = ConnectionPoolInner::new(config);
|
||||||
let available = RefCell::new(AHashMap::default());
|
|
||||||
let connector = Rc::new(connector);
|
|
||||||
|
|
||||||
let inner = ConnectionPoolInner(Rc::new(ConnectionPoolInnerPriv {
|
|
||||||
config,
|
|
||||||
available,
|
|
||||||
permits,
|
|
||||||
}));
|
|
||||||
|
|
||||||
Self { connector, inner }
|
Self { connector, inner }
|
||||||
}
|
}
|
||||||
@ -144,12 +152,14 @@ where
|
|||||||
|
|
||||||
impl<S, Io> Service<Connect> for ConnectionPool<S, Io>
|
impl<S, Io> Service<Connect> for ConnectionPool<S, Io>
|
||||||
where
|
where
|
||||||
S: Service<Connect, Response = (Io, Protocol), Error = ConnectError> + 'static,
|
S: Service<Connect, Response = (Io, Protocol), Error = ConnectError>
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
+ Clone
|
||||||
|
+ 'static,
|
||||||
|
Io: ConnectionIo,
|
||||||
{
|
{
|
||||||
type Response = IoConnection<Io>;
|
type Response = ConnectionType<Io>;
|
||||||
type Error = ConnectError;
|
type Error = ConnectError;
|
||||||
type Future = LocalBoxFuture<'static, Result<IoConnection<Io>, ConnectError>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
actix_service::forward_ready!(connector);
|
actix_service::forward_ready!(connector);
|
||||||
|
|
||||||
@ -193,7 +203,7 @@ where
|
|||||||
inner.close(c.conn);
|
inner.close(c.conn);
|
||||||
} else {
|
} else {
|
||||||
// check if the connection is still usable
|
// check if the connection is still usable
|
||||||
if let ConnectionType::H1(ref mut io) = c.conn {
|
if let ConnectionInnerType::H1(ref mut io) = c.conn {
|
||||||
let check = ConnectionCheckFuture { io };
|
let check = ConnectionCheckFuture { io };
|
||||||
match check.await {
|
match check.await {
|
||||||
ConnectionState::Tainted => {
|
ConnectionState::Tainted => {
|
||||||
@ -221,7 +231,9 @@ where
|
|||||||
|
|
||||||
// match the connection and spawn new one if did not get anything.
|
// match the connection and spawn new one if did not get anything.
|
||||||
match conn {
|
match conn {
|
||||||
Some(conn) => Ok(IoConnection::new(conn.conn, conn.created, acquired)),
|
Some(conn) => {
|
||||||
|
Ok(ConnectionType::from_pool(conn.conn, conn.created, acquired))
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
let (io, proto) = connector.call(req).await?;
|
let (io, proto) = connector.call(req).await?;
|
||||||
|
|
||||||
@ -229,19 +241,12 @@ where
|
|||||||
assert!(proto != Protocol::Http3);
|
assert!(proto != Protocol::Http3);
|
||||||
|
|
||||||
if proto == Protocol::Http1 {
|
if proto == Protocol::Http1 {
|
||||||
Ok(IoConnection::new(
|
Ok(ConnectionType::from_h1(io, Instant::now(), acquired))
|
||||||
ConnectionType::H1(io),
|
|
||||||
Instant::now(),
|
|
||||||
acquired,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
let config = &acquired.inner.config;
|
let config = &acquired.inner.config;
|
||||||
let (sender, connection) = handshake(io, config).await?;
|
let (sender, connection) = handshake(io, config).await?;
|
||||||
Ok(IoConnection::new(
|
let inner = H2ConnectionInner::new(sender, connection);
|
||||||
ConnectionType::H2(H2Connection::new(sender, connection)),
|
Ok(ConnectionType::from_h2(inner, Instant::now(), acquired))
|
||||||
Instant::now(),
|
|
||||||
acquired,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,7 +297,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct PooledConnection<Io> {
|
struct PooledConnection<Io> {
|
||||||
conn: ConnectionType<Io>,
|
conn: ConnectionInnerType<Io>,
|
||||||
used: Instant,
|
used: Instant,
|
||||||
created: Instant,
|
created: Instant,
|
||||||
}
|
}
|
||||||
@ -332,26 +337,26 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Acquired<Io>
|
pub struct Acquired<Io>
|
||||||
where
|
where
|
||||||
Io: AsyncWrite + Unpin + 'static,
|
Io: AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
|
/// authority key for identify connection.
|
||||||
key: Key,
|
key: Key,
|
||||||
|
/// handle to connection pool.
|
||||||
inner: ConnectionPoolInner<Io>,
|
inner: ConnectionPoolInner<Io>,
|
||||||
|
/// permit for limit concurrent in-flight connection for a Client object.
|
||||||
permit: OwnedSemaphorePermit,
|
permit: OwnedSemaphorePermit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io> Acquired<Io>
|
impl<Io: ConnectionIo> Acquired<Io> {
|
||||||
where
|
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
/// Close the IO.
|
/// Close the IO.
|
||||||
pub(crate) fn close(&self, conn: ConnectionType<Io>) {
|
pub(super) fn close(&self, conn: ConnectionInnerType<Io>) {
|
||||||
self.inner.close(conn);
|
self.inner.close(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Release IO back into pool.
|
/// Release IO back into pool.
|
||||||
pub(crate) fn release(&self, conn: ConnectionType<Io>, created: Instant) {
|
pub(super) fn release(&self, conn: ConnectionInnerType<Io>, created: Instant) {
|
||||||
let Acquired { key, inner, .. } = self;
|
let Acquired { key, inner, .. } = self;
|
||||||
|
|
||||||
inner
|
inner
|
||||||
@ -376,7 +381,7 @@ mod test {
|
|||||||
use http::Uri;
|
use http::Uri;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::client::connection::IoConnection;
|
use crate::client::connection::ConnectionType;
|
||||||
|
|
||||||
/// A stream type that always returns pending on async read.
|
/// A stream type that always returns pending on async read.
|
||||||
///
|
///
|
||||||
@ -423,6 +428,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct TestPoolConnector {
|
struct TestPoolConnector {
|
||||||
generated: Rc<Cell<usize>>,
|
generated: Rc<Cell<usize>>,
|
||||||
}
|
}
|
||||||
@ -441,12 +447,14 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release<T>(conn: IoConnection<T>)
|
fn release<T>(conn: ConnectionType<T>)
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
let (conn, created, acquired) = conn.into_parts();
|
match conn {
|
||||||
acquired.release(conn, created);
|
ConnectionType::H1(mut conn) => conn.on_release(true),
|
||||||
|
ConnectionType::H2(mut conn) => conn.on_release(false),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
### Changed
|
||||||
|
* `ConnectorService` type is renamed to `BoxConnectorService` [#2081]
|
||||||
|
|
||||||
|
[#2081]: https://github.com/actix/actix-web/pull/2081
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.3 - 2021-03-08
|
## 3.0.0-beta.3 - 2021-03-08
|
||||||
|
@ -6,7 +6,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite};
|
use actix_codec::{AsyncRead, AsyncWrite};
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
client::{Connector, TcpConnect, TcpConnectError, TcpConnection},
|
client::{Connector, ConnectorService, TcpConnect, TcpConnectError, TcpConnection},
|
||||||
http::{self, header, Error as HttpError, HeaderMap, HeaderName, Uri},
|
http::{self, header, Error as HttpError, HeaderMap, HeaderName, Uri},
|
||||||
};
|
};
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
@ -15,7 +15,7 @@ use actix_service::{boxed, Service};
|
|||||||
use crate::connect::DefaultConnector;
|
use crate::connect::DefaultConnector;
|
||||||
use crate::error::SendRequestError;
|
use crate::error::SendRequestError;
|
||||||
use crate::middleware::{NestTransform, Redirect, Transform};
|
use crate::middleware::{NestTransform, Redirect, Transform};
|
||||||
use crate::{Client, ClientConfig, ConnectRequest, ConnectResponse, ConnectorService};
|
use crate::{Client, ClientConfig, ConnectRequest, ConnectResponse};
|
||||||
|
|
||||||
/// An HTTP Client builder
|
/// An HTTP Client builder
|
||||||
///
|
///
|
||||||
@ -234,7 +234,7 @@ where
|
|||||||
/// Finish build process and create `Client` instance.
|
/// Finish build process and create `Client` instance.
|
||||||
pub fn finish(self) -> Client
|
pub fn finish(self) -> Client
|
||||||
where
|
where
|
||||||
M: Transform<ConnectorService, ConnectRequest> + 'static,
|
M: Transform<DefaultConnector<ConnectorService<S, Io>>, ConnectRequest> + 'static,
|
||||||
M::Transform:
|
M::Transform:
|
||||||
Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError>,
|
Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError>,
|
||||||
{
|
{
|
||||||
@ -250,7 +250,7 @@ where
|
|||||||
|
|
||||||
fn _finish(self) -> Client
|
fn _finish(self) -> Client
|
||||||
where
|
where
|
||||||
M: Transform<ConnectorService, ConnectRequest> + 'static,
|
M: Transform<DefaultConnector<ConnectorService<S, Io>>, ConnectRequest> + 'static,
|
||||||
M::Transform:
|
M::Transform:
|
||||||
Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError>,
|
Service<ConnectRequest, Response = ConnectResponse, Error = SendRequestError>,
|
||||||
{
|
{
|
||||||
@ -269,16 +269,14 @@ where
|
|||||||
connector = connector.local_address(val);
|
connector = connector.local_address(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
let connector = boxed::service(DefaultConnector::new(connector.finish()));
|
let connector = DefaultConnector::new(connector.finish());
|
||||||
let connector = boxed::service(self.middleware.new_transform(connector));
|
let connector = boxed::rc_service(self.middleware.new_transform(connector));
|
||||||
|
|
||||||
let config = ClientConfig {
|
Client(ClientConfig {
|
||||||
headers: self.headers,
|
headers: Rc::new(self.headers),
|
||||||
timeout: self.timeout,
|
timeout: self.timeout,
|
||||||
connector,
|
connector,
|
||||||
};
|
})
|
||||||
|
|
||||||
Client(Rc::new(config))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use std::{
|
|||||||
future::Future,
|
future::Future,
|
||||||
net,
|
net,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
|
rc::Rc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ use futures_core::{future::LocalBoxFuture, ready};
|
|||||||
|
|
||||||
use crate::response::ClientResponse;
|
use crate::response::ClientResponse;
|
||||||
|
|
||||||
pub type ConnectorService = Box<
|
pub type BoxConnectorService = Rc<
|
||||||
dyn Service<
|
dyn Service<
|
||||||
ConnectRequest,
|
ConnectRequest,
|
||||||
Response = ConnectResponse,
|
Response = ConnectResponse,
|
||||||
@ -28,6 +29,8 @@ pub type ConnectorService = Box<
|
|||||||
>,
|
>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
pub type BoxedSocket = Box<dyn ConnectionIo>;
|
||||||
|
|
||||||
pub enum ConnectRequest {
|
pub enum ConnectRequest {
|
||||||
Client(RequestHeadType, Body, Option<net::SocketAddr>),
|
Client(RequestHeadType, Body, Option<net::SocketAddr>),
|
||||||
Tunnel(RequestHead, Option<net::SocketAddr>),
|
Tunnel(RequestHead, Option<net::SocketAddr>),
|
||||||
@ -58,7 +61,7 @@ impl ConnectResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct DefaultConnector<S> {
|
pub struct DefaultConnector<S> {
|
||||||
connector: S,
|
connector: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,15 +71,14 @@ impl<S> DefaultConnector<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Service<ConnectRequest> for DefaultConnector<S>
|
impl<S, Io> Service<ConnectRequest> for DefaultConnector<S>
|
||||||
where
|
where
|
||||||
S: Service<ClientConnect, Error = ConnectError>,
|
S: Service<ClientConnect, Error = ConnectError, Response = Connection<Io>>,
|
||||||
S::Response: Connection,
|
Io: ConnectionIo,
|
||||||
<S::Response as Connection>::Io: 'static,
|
|
||||||
{
|
{
|
||||||
type Response = ConnectResponse;
|
type Response = ConnectResponse;
|
||||||
type Error = SendRequestError;
|
type Error = SendRequestError;
|
||||||
type Future = ConnectRequestFuture<S::Future, <S::Response as Connection>::Io>;
|
type Future = ConnectRequestFuture<S::Future, Io>;
|
||||||
|
|
||||||
actix_service::forward_ready!(connector);
|
actix_service::forward_ready!(connector);
|
||||||
|
|
||||||
@ -102,7 +104,10 @@ where
|
|||||||
|
|
||||||
pin_project_lite::pin_project! {
|
pin_project_lite::pin_project! {
|
||||||
#[project = ConnectRequestProj]
|
#[project = ConnectRequestProj]
|
||||||
pub(crate) enum ConnectRequestFuture<Fut, Io> {
|
pub enum ConnectRequestFuture<Fut, Io>
|
||||||
|
where
|
||||||
|
Io: ConnectionIo
|
||||||
|
{
|
||||||
Connection {
|
Connection {
|
||||||
#[pin]
|
#[pin]
|
||||||
fut: Fut,
|
fut: Fut,
|
||||||
@ -114,16 +119,15 @@ pin_project_lite::pin_project! {
|
|||||||
Tunnel {
|
Tunnel {
|
||||||
fut: LocalBoxFuture<
|
fut: LocalBoxFuture<
|
||||||
'static,
|
'static,
|
||||||
Result<(ResponseHead, Framed<Io, ClientCodec>), SendRequestError>,
|
Result<(ResponseHead, Framed<Connection<Io>, ClientCodec>), SendRequestError>,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Fut, C, Io> Future for ConnectRequestFuture<Fut, Io>
|
impl<Fut, Io> Future for ConnectRequestFuture<Fut, Io>
|
||||||
where
|
where
|
||||||
Fut: Future<Output = Result<C, ConnectError>>,
|
Fut: Future<Output = Result<Connection<Io>, ConnectError>>,
|
||||||
C: Connection<Io = Io>,
|
|
||||||
Io: ConnectionIo,
|
Io: ConnectionIo,
|
||||||
{
|
{
|
||||||
type Output = Result<ConnectResponse, SendRequestError>;
|
type Output = Result<ConnectResponse, SendRequestError>;
|
||||||
@ -165,5 +169,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BoxedSocket = Box<dyn ConnectionIo>;
|
|
||||||
|
@ -23,7 +23,7 @@ pub struct FrozenClientRequest {
|
|||||||
pub(crate) addr: Option<net::SocketAddr>,
|
pub(crate) addr: Option<net::SocketAddr>,
|
||||||
pub(crate) response_decompress: bool,
|
pub(crate) response_decompress: bool,
|
||||||
pub(crate) timeout: Option<Duration>,
|
pub(crate) timeout: Option<Duration>,
|
||||||
pub(crate) config: Rc<ClientConfig>,
|
pub(crate) config: ClientConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrozenClientRequest {
|
impl FrozenClientRequest {
|
||||||
@ -51,7 +51,7 @@ impl FrozenClientRequest {
|
|||||||
self.addr,
|
self.addr,
|
||||||
self.response_decompress,
|
self.response_decompress,
|
||||||
self.timeout,
|
self.timeout,
|
||||||
self.config.as_ref(),
|
&self.config,
|
||||||
body,
|
body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ impl FrozenClientRequest {
|
|||||||
self.addr,
|
self.addr,
|
||||||
self.response_decompress,
|
self.response_decompress,
|
||||||
self.timeout,
|
self.timeout,
|
||||||
self.config.as_ref(),
|
&self.config,
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ impl FrozenClientRequest {
|
|||||||
self.addr,
|
self.addr,
|
||||||
self.response_decompress,
|
self.response_decompress,
|
||||||
self.timeout,
|
self.timeout,
|
||||||
self.config.as_ref(),
|
&self.config,
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ impl FrozenClientRequest {
|
|||||||
self.addr,
|
self.addr,
|
||||||
self.response_decompress,
|
self.response_decompress,
|
||||||
self.timeout,
|
self.timeout,
|
||||||
self.config.as_ref(),
|
&self.config,
|
||||||
stream,
|
stream,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ impl FrozenClientRequest {
|
|||||||
self.addr,
|
self.addr,
|
||||||
self.response_decompress,
|
self.response_decompress,
|
||||||
self.timeout,
|
self.timeout,
|
||||||
self.config.as_ref(),
|
&self.config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ impl FrozenSendBuilder {
|
|||||||
self.req.addr,
|
self.req.addr,
|
||||||
self.req.response_decompress,
|
self.req.response_decompress,
|
||||||
self.req.timeout,
|
self.req.timeout,
|
||||||
self.req.config.as_ref(),
|
&self.req.config,
|
||||||
body,
|
body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -183,7 +183,7 @@ impl FrozenSendBuilder {
|
|||||||
self.req.addr,
|
self.req.addr,
|
||||||
self.req.response_decompress,
|
self.req.response_decompress,
|
||||||
self.req.timeout,
|
self.req.timeout,
|
||||||
self.req.config.as_ref(),
|
&self.req.config,
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -198,7 +198,7 @@ impl FrozenSendBuilder {
|
|||||||
self.req.addr,
|
self.req.addr,
|
||||||
self.req.response_decompress,
|
self.req.response_decompress,
|
||||||
self.req.timeout,
|
self.req.timeout,
|
||||||
self.req.config.as_ref(),
|
&self.req.config,
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -217,7 +217,7 @@ impl FrozenSendBuilder {
|
|||||||
self.req.addr,
|
self.req.addr,
|
||||||
self.req.response_decompress,
|
self.req.response_decompress,
|
||||||
self.req.timeout,
|
self.req.timeout,
|
||||||
self.req.config.as_ref(),
|
&self.req.config,
|
||||||
stream,
|
stream,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -232,7 +232,7 @@ impl FrozenSendBuilder {
|
|||||||
self.req.addr,
|
self.req.addr,
|
||||||
self.req.response_decompress,
|
self.req.response_decompress,
|
||||||
self.req.timeout,
|
self.req.timeout,
|
||||||
self.req.config.as_ref(),
|
&self.req.config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ pub mod test;
|
|||||||
pub mod ws;
|
pub mod ws;
|
||||||
|
|
||||||
pub use self::builder::ClientBuilder;
|
pub use self::builder::ClientBuilder;
|
||||||
pub use self::connect::{BoxedSocket, ConnectRequest, ConnectResponse, ConnectorService};
|
pub use self::connect::{BoxConnectorService, BoxedSocket, ConnectRequest, ConnectResponse};
|
||||||
pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder};
|
pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder};
|
||||||
pub use self::request::ClientRequest;
|
pub use self::request::ClientRequest;
|
||||||
pub use self::response::{ClientResponse, JsonBody, MessageBody};
|
pub use self::response::{ClientResponse, JsonBody, MessageBody};
|
||||||
@ -147,11 +147,12 @@ pub use self::sender::SendClientRequest;
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Client(Rc<ClientConfig>);
|
pub struct Client(ClientConfig);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub(crate) struct ClientConfig {
|
pub(crate) struct ClientConfig {
|
||||||
pub(crate) connector: ConnectorService,
|
pub(crate) connector: BoxConnectorService,
|
||||||
pub(crate) headers: HeaderMap,
|
pub(crate) headers: Rc<HeaderMap>,
|
||||||
pub(crate) timeout: Option<Duration>,
|
pub(crate) timeout: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ where
|
|||||||
// remove body
|
// remove body
|
||||||
.call(ConnectRequest::Client(head, Body::None, addr));
|
.call(ConnectRequest::Client(head, Body::None, addr));
|
||||||
|
|
||||||
self.as_mut().set(RedirectServiceFuture::Client {
|
self.set(RedirectServiceFuture::Client {
|
||||||
fut,
|
fut,
|
||||||
max_redirect_times,
|
max_redirect_times,
|
||||||
uri: Some(uri),
|
uri: Some(uri),
|
||||||
@ -236,7 +236,7 @@ where
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.call(ConnectRequest::Client(head, body_new, addr));
|
.call(ConnectRequest::Client(head, body_new, addr));
|
||||||
|
|
||||||
self.as_mut().set(RedirectServiceFuture::Client {
|
self.set(RedirectServiceFuture::Client {
|
||||||
fut,
|
fut,
|
||||||
max_redirect_times,
|
max_redirect_times,
|
||||||
uri: Some(uri),
|
uri: Some(uri),
|
||||||
|
@ -57,7 +57,7 @@ pub struct ClientRequest {
|
|||||||
addr: Option<net::SocketAddr>,
|
addr: Option<net::SocketAddr>,
|
||||||
response_decompress: bool,
|
response_decompress: bool,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
config: Rc<ClientConfig>,
|
config: ClientConfig,
|
||||||
|
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
cookies: Option<CookieJar>,
|
cookies: Option<CookieJar>,
|
||||||
@ -65,7 +65,7 @@ pub struct ClientRequest {
|
|||||||
|
|
||||||
impl ClientRequest {
|
impl ClientRequest {
|
||||||
/// Create new client request builder.
|
/// Create new client request builder.
|
||||||
pub(crate) fn new<U>(method: Method, uri: U, config: Rc<ClientConfig>) -> Self
|
pub(crate) fn new<U>(method: Method, uri: U, config: ClientConfig) -> Self
|
||||||
where
|
where
|
||||||
Uri: TryFrom<U>,
|
Uri: TryFrom<U>,
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
@ -398,7 +398,7 @@ impl ClientRequest {
|
|||||||
slf.addr,
|
slf.addr,
|
||||||
slf.response_decompress,
|
slf.response_decompress,
|
||||||
slf.timeout,
|
slf.timeout,
|
||||||
slf.config.as_ref(),
|
&slf.config,
|
||||||
body,
|
body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -414,7 +414,7 @@ impl ClientRequest {
|
|||||||
slf.addr,
|
slf.addr,
|
||||||
slf.response_decompress,
|
slf.response_decompress,
|
||||||
slf.timeout,
|
slf.timeout,
|
||||||
slf.config.as_ref(),
|
&slf.config,
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -432,7 +432,7 @@ impl ClientRequest {
|
|||||||
slf.addr,
|
slf.addr,
|
||||||
slf.response_decompress,
|
slf.response_decompress,
|
||||||
slf.timeout,
|
slf.timeout,
|
||||||
slf.config.as_ref(),
|
&slf.config,
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -452,7 +452,7 @@ impl ClientRequest {
|
|||||||
slf.addr,
|
slf.addr,
|
||||||
slf.response_decompress,
|
slf.response_decompress,
|
||||||
slf.timeout,
|
slf.timeout,
|
||||||
slf.config.as_ref(),
|
&slf.config,
|
||||||
stream,
|
stream,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -468,7 +468,7 @@ impl ClientRequest {
|
|||||||
slf.addr,
|
slf.addr,
|
||||||
slf.response_decompress,
|
slf.response_decompress,
|
||||||
slf.timeout,
|
slf.timeout,
|
||||||
slf.config.as_ref(),
|
&slf.config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::{fmt, str};
|
use std::{fmt, str};
|
||||||
|
|
||||||
use actix_codec::Framed;
|
use actix_codec::Framed;
|
||||||
@ -56,7 +55,7 @@ pub struct WebsocketsRequest {
|
|||||||
addr: Option<SocketAddr>,
|
addr: Option<SocketAddr>,
|
||||||
max_size: usize,
|
max_size: usize,
|
||||||
server_mode: bool,
|
server_mode: bool,
|
||||||
config: Rc<ClientConfig>,
|
config: ClientConfig,
|
||||||
|
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
cookies: Option<CookieJar>,
|
cookies: Option<CookieJar>,
|
||||||
@ -64,7 +63,7 @@ pub struct WebsocketsRequest {
|
|||||||
|
|
||||||
impl WebsocketsRequest {
|
impl WebsocketsRequest {
|
||||||
/// Create new WebSocket connection
|
/// Create new WebSocket connection
|
||||||
pub(crate) fn new<U>(uri: U, config: Rc<ClientConfig>) -> Self
|
pub(crate) fn new<U>(uri: U, config: ClientConfig) -> Self
|
||||||
where
|
where
|
||||||
Uri: TryFrom<U>,
|
Uri: TryFrom<U>,
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
Loading…
Reference in New Issue
Block a user