1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-25 17:02:44 +01:00
actix-web/src/client/connector.rs

1178 lines
40 KiB
Rust
Raw Normal View History

2018-04-14 01:02:01 +02:00
use std::collections::{HashMap, VecDeque};
use std::net::Shutdown;
2018-03-16 20:04:01 +01:00
use std::time::{Duration, Instant};
2018-04-14 01:02:01 +02:00
use std::{fmt, io, mem, time};
2018-01-30 08:01:20 +01:00
2018-06-10 19:14:13 +02:00
use actix::resolver::{Connect as ResolveConnect, Resolver, ResolverError};
2018-06-01 18:36:16 +02:00
use actix::{
2018-06-09 16:53:46 +02:00
fut, Actor, ActorContext, ActorFuture, ActorResponse, Addr, AsyncContext, Context,
2018-06-21 09:34:36 +02:00
ContextFutureSpawner, Handler, Message, Recipient, StreamHandler2, Supervised,
SystemService, WrapFuture,
2018-05-17 21:20:20 +02:00
};
2018-01-30 08:01:20 +01:00
use futures::sync::{mpsc, oneshot};
2018-04-14 01:02:01 +02:00
use futures::{Async, Future, Poll};
use http::{Error as HttpError, HttpTryFrom, Uri};
use tokio_io::{AsyncRead, AsyncWrite};
2018-05-25 06:03:16 +02:00
use tokio_timer::Delay;
2018-01-30 08:01:20 +01:00
2018-04-14 01:02:01 +02:00
#[cfg(feature = "alpn")]
use openssl::ssl::{Error as OpensslError, SslConnector, SslMethod};
#[cfg(feature = "alpn")]
2018-01-30 20:17:17 +01:00
use tokio_openssl::SslConnectorExt;
2018-04-14 01:02:01 +02:00
#[cfg(all(feature = "tls", not(feature = "alpn")))]
use native_tls::{Error as TlsError, TlsConnector};
#[cfg(all(feature = "tls", not(feature = "alpn")))]
2018-03-07 02:34:46 +01:00
use tokio_tls::TlsConnectorExt;
2018-01-30 08:01:20 +01:00
use server::IoStream;
2018-04-14 01:02:01 +02:00
use {HAS_OPENSSL, HAS_TLS};
2018-01-30 08:01:20 +01:00
2018-04-12 01:11:11 +02:00
/// Client connector usage stats
#[derive(Default, Message)]
pub struct ClientConnectorStats {
/// Number of waited-on connections
2018-04-12 01:11:11 +02:00
pub waits: usize,
/// Number of reused connections
2018-04-12 01:11:11 +02:00
pub reused: usize,
/// Number of opened connections
2018-04-12 01:11:11 +02:00
pub opened: usize,
/// Number of closed connections
2018-04-12 01:11:11 +02:00
pub closed: usize,
/// Number of connections with errors
2018-04-12 01:11:11 +02:00
pub errors: usize,
/// Number of connection timeouts
2018-04-12 22:08:13 +02:00
pub timeouts: usize,
2018-04-12 01:11:11 +02:00
}
2018-01-30 20:17:17 +01:00
2018-01-30 08:01:20 +01:00
#[derive(Debug)]
/// `Connect` type represents a message that can be sent to
/// `ClientConnector` with a connection request.
2018-03-06 23:26:09 +01:00
pub struct Connect {
2018-04-06 03:33:58 +02:00
pub(crate) uri: Uri,
pub(crate) wait_timeout: Duration,
2018-04-06 03:33:58 +02:00
pub(crate) conn_timeout: Duration,
2018-03-06 23:26:09 +01:00
}
2018-01-30 08:01:20 +01:00
2018-01-30 20:17:17 +01:00
impl Connect {
/// Create `Connect` message for specified `Uri`
2018-04-14 01:02:01 +02:00
pub fn new<U>(uri: U) -> Result<Connect, HttpError>
where
Uri: HttpTryFrom<U>,
{
2018-03-06 23:26:09 +01:00
Ok(Connect {
uri: Uri::try_from(uri).map_err(|e| e.into())?,
wait_timeout: Duration::from_secs(5),
2018-04-06 03:33:58 +02:00
conn_timeout: Duration::from_secs(1),
2018-03-06 23:26:09 +01:00
})
2018-01-30 20:17:17 +01:00
}
2018-04-06 03:33:58 +02:00
/// Connection timeout, i.e. max time to connect to remote host.
/// Set to 1 second by default.
2018-04-06 03:33:58 +02:00
pub fn conn_timeout(mut self, timeout: Duration) -> Self {
self.conn_timeout = timeout;
self
}
/// If connection pool limits are enabled, wait time indicates
/// max time to wait for a connection to become available.
/// Set to 5 seconds by default.
pub fn wait_timeout(mut self, timeout: Duration) -> Self {
self.wait_timeout = timeout;
2018-04-06 03:33:58 +02:00
self
}
2018-01-30 20:17:17 +01:00
}
2018-02-12 21:17:30 +01:00
impl Message for Connect {
type Result = Result<Connection, ClientConnectorError>;
2018-01-30 08:01:20 +01:00
}
/// Pause connection process for `ClientConnector`
///
/// All connect requests enter wait state during connector pause.
pub struct Pause {
time: Option<Duration>,
}
2018-04-06 21:31:31 +02:00
impl Pause {
/// Create message with pause duration parameter
2018-04-06 21:34:24 +02:00
pub fn new(time: Duration) -> Pause {
2018-04-29 18:09:08 +02:00
Pause { time: Some(time) }
2018-04-06 21:31:31 +02:00
}
}
impl Default for Pause {
fn default() -> Pause {
2018-04-29 18:09:08 +02:00
Pause { time: None }
2018-04-06 21:31:31 +02:00
}
}
impl Message for Pause {
type Result = ();
}
/// Resume connection process for `ClientConnector`
#[derive(Message)]
pub struct Resume;
/// A set of errors that can occur while connecting to an HTTP host
2018-01-30 08:01:20 +01:00
#[derive(Fail, Debug)]
pub enum ClientConnectorError {
/// Invalid URL
2018-04-14 01:02:01 +02:00
#[fail(display = "Invalid URL")]
2018-01-30 08:01:20 +01:00
InvalidUrl,
/// SSL feature is not enabled
2018-04-14 01:02:01 +02:00
#[fail(display = "SSL is not supported")]
2018-01-30 08:01:20 +01:00
SslIsNotSupported,
2018-01-30 20:17:17 +01:00
/// SSL error
2018-04-14 01:02:01 +02:00
#[cfg(feature = "alpn")]
#[fail(display = "{}", _0)]
SslError(#[cause] OpensslError),
2018-01-30 20:17:17 +01:00
2018-03-07 02:34:46 +01:00
/// SSL error
2018-04-14 01:02:01 +02:00
#[cfg(all(feature = "tls", not(feature = "alpn")))]
#[fail(display = "{}", _0)]
2018-03-07 02:34:46 +01:00
SslError(#[cause] TlsError),
2018-06-10 19:24:34 +02:00
/// Resolver error
2018-01-30 08:01:20 +01:00
#[fail(display = "{}", _0)]
2018-06-10 19:24:34 +02:00
Resolver(#[cause] ResolverError),
2018-01-30 08:01:20 +01:00
2018-03-09 22:03:15 +01:00
/// Connection took too long
#[fail(display = "Timeout while establishing connection")]
2018-01-30 08:01:20 +01:00
Timeout,
/// Connector has been disconnected
#[fail(display = "Internal error: connector has been disconnected")]
Disconnected,
/// Connection IO error
2018-01-30 08:01:20 +01:00
#[fail(display = "{}", _0)]
IoError(#[cause] io::Error),
2018-01-30 08:01:20 +01:00
}
2018-06-10 19:14:13 +02:00
impl From<ResolverError> for ClientConnectorError {
fn from(err: ResolverError) -> ClientConnectorError {
2018-03-07 21:09:53 +01:00
match err {
2018-06-10 19:14:13 +02:00
ResolverError::Timeout => ClientConnectorError::Timeout,
2018-06-10 19:24:34 +02:00
_ => ClientConnectorError::Resolver(err),
2018-03-07 21:09:53 +01:00
}
2018-01-30 08:01:20 +01:00
}
}
2018-04-05 01:39:01 +02:00
struct Waiter {
tx: oneshot::Sender<Result<Connection, ClientConnectorError>>,
2018-04-06 03:33:58 +02:00
wait: Instant,
2018-04-05 01:39:01 +02:00
conn_timeout: Duration,
}
enum Paused {
No,
Yes,
Timeout(Instant, Delay),
}
impl Paused {
fn is_paused(&self) -> bool {
match *self {
Paused::No => false,
_ => true,
}
}
}
/// `ClientConnector` type is responsible for transport layer of a
/// client connection.
2018-01-30 08:01:20 +01:00
pub struct ClientConnector {
2018-04-14 01:02:01 +02:00
#[cfg(all(feature = "alpn"))]
2018-01-30 20:17:17 +01:00
connector: SslConnector,
2018-04-14 01:02:01 +02:00
#[cfg(all(feature = "tls", not(feature = "alpn")))]
2018-03-07 02:34:46 +01:00
connector: TlsConnector,
2018-04-05 01:39:01 +02:00
2018-04-12 01:11:11 +02:00
stats: ClientConnectorStats,
subscriber: Option<Recipient<ClientConnectorStats>>,
2018-04-12 01:11:11 +02:00
acq_tx: mpsc::UnboundedSender<AcquiredConnOperation>,
acq_rx: Option<mpsc::UnboundedReceiver<AcquiredConnOperation>>,
2018-04-06 03:33:58 +02:00
2018-06-10 19:14:13 +02:00
resolver: Option<Addr<Resolver>>,
2018-04-05 01:39:01 +02:00
conn_lifetime: Duration,
conn_keep_alive: Duration,
limit: usize,
limit_per_host: usize,
acquired: usize,
acquired_per_host: HashMap<Key, usize>,
available: HashMap<Key, VecDeque<Conn>>,
to_close: Vec<Connection>,
waiters: Option<HashMap<Key, VecDeque<Waiter>>>,
2018-05-25 06:03:16 +02:00
wait_timeout: Option<(Instant, Delay)>,
paused: Paused,
2018-01-30 08:01:20 +01:00
}
impl Actor for ClientConnector {
type Context = Context<ClientConnector>;
2018-03-16 20:04:01 +01:00
fn started(&mut self, ctx: &mut Self::Context) {
2018-06-09 16:53:46 +02:00
if self.resolver.is_none() {
2018-06-10 19:14:13 +02:00
self.resolver = Some(Resolver::from_registry())
2018-06-09 16:53:46 +02:00
}
2018-04-05 01:39:01 +02:00
self.collect_periodic(ctx);
2018-06-21 09:34:36 +02:00
ctx.add_stream2(self.acq_rx.take().unwrap());
2018-04-05 01:39:01 +02:00
ctx.spawn(Maintenance);
2018-03-16 20:04:01 +01:00
}
2018-01-30 08:01:20 +01:00
}
impl Supervised for ClientConnector {}
impl SystemService for ClientConnector {}
2018-01-30 08:01:20 +01:00
2018-01-30 20:17:17 +01:00
impl Default for ClientConnector {
fn default() -> ClientConnector {
2018-04-14 01:02:01 +02:00
#[cfg(all(feature = "alpn"))]
2018-01-30 20:17:17 +01:00
{
2018-02-27 01:11:00 +01:00
let builder = SslConnector::builder(SslMethod::tls()).unwrap();
2018-04-05 01:39:01 +02:00
ClientConnector::with_connector(builder.build())
2018-01-30 20:17:17 +01:00
}
2018-04-14 01:02:01 +02:00
#[cfg(all(feature = "tls", not(feature = "alpn")))]
2018-03-07 02:34:46 +01:00
{
2018-05-27 14:18:37 +02:00
let (tx, rx) = mpsc::unbounded();
2018-03-07 02:34:46 +01:00
let builder = TlsConnector::builder().unwrap();
ClientConnector {
2018-04-12 01:11:11 +02:00
stats: ClientConnectorStats::default(),
subscriber: None,
acq_tx: tx,
acq_rx: Some(rx),
2018-06-09 16:53:46 +02:00
resolver: None,
2018-04-05 01:39:01 +02:00
connector: builder.build().unwrap(),
conn_lifetime: Duration::from_secs(75),
conn_keep_alive: Duration::from_secs(15),
2018-04-05 01:39:01 +02:00
limit: 100,
limit_per_host: 0,
acquired: 0,
acquired_per_host: HashMap::new(),
available: HashMap::new(),
to_close: Vec::new(),
waiters: Some(HashMap::new()),
2018-04-06 03:33:58 +02:00
wait_timeout: None,
paused: Paused::No,
2018-03-07 02:34:46 +01:00
}
}
2018-01-30 20:17:17 +01:00
2018-04-14 01:02:01 +02:00
#[cfg(not(any(feature = "alpn", feature = "tls")))]
2018-05-27 14:18:37 +02:00
{
let (tx, rx) = mpsc::unbounded();
ClientConnector {
stats: ClientConnectorStats::default(),
subscriber: None,
acq_tx: tx,
acq_rx: Some(rx),
2018-06-09 16:53:46 +02:00
resolver: None,
2018-05-27 14:18:37 +02:00
conn_lifetime: Duration::from_secs(75),
conn_keep_alive: Duration::from_secs(15),
limit: 100,
limit_per_host: 0,
acquired: 0,
acquired_per_host: HashMap::new(),
available: HashMap::new(),
to_close: Vec::new(),
waiters: Some(HashMap::new()),
2018-05-27 14:18:37 +02:00
wait_timeout: None,
paused: Paused::No,
}
2018-04-05 01:39:01 +02:00
}
2018-01-30 20:17:17 +01:00
}
}
impl ClientConnector {
2018-04-14 01:02:01 +02:00
#[cfg(feature = "alpn")]
2018-01-30 20:17:17 +01:00
/// Create `ClientConnector` actor with custom `SslConnector` instance.
///
/// By default `ClientConnector` uses very a simple SSL configuration.
/// With `with_connector` method it is possible to use a custom
/// `SslConnector` object.
2018-01-30 20:17:17 +01:00
///
2018-06-19 15:19:31 +02:00
/// ```rust,ignore
2018-01-30 20:17:17 +01:00
/// # #![cfg(feature="alpn")]
/// # extern crate actix_web;
/// # extern crate futures;
2018-05-30 03:48:29 +02:00
/// # use futures::{future, Future};
2018-01-30 20:17:17 +01:00
/// # use std::io::Write;
2018-05-30 03:31:39 +02:00
/// # use std::process;
2018-06-01 20:22:40 +02:00
/// # use actix_web::actix::Actor;
2018-01-30 20:17:17 +01:00
/// extern crate openssl;
2018-06-19 08:07:07 +02:00
/// use actix_web::{actix, client::ClientConnector, client::Connect};
2018-01-30 20:17:17 +01:00
///
2018-06-01 18:36:16 +02:00
/// use openssl::ssl::{SslConnector, SslMethod};
2018-01-30 20:17:17 +01:00
///
/// fn main() {
2018-06-19 08:07:07 +02:00
/// actix::run(|| {
/// // Start `ClientConnector` with custom `SslConnector`
/// let ssl_conn = SslConnector::builder(SslMethod::tls()).unwrap().build();
/// let conn = ClientConnector::with_connector(ssl_conn).start();
2018-01-30 20:17:17 +01:00
///
2018-02-13 16:50:49 +01:00
/// conn.send(
2018-01-31 18:28:53 +01:00
/// Connect::new("https://www.rust-lang.org").unwrap()) // <- connect to host
2018-01-30 20:17:17 +01:00
/// .map_err(|_| ())
/// .and_then(|res| {
/// if let Ok(mut stream) = res {
/// stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
/// }
2018-06-19 08:07:07 +02:00
/// # actix::System::current().stop();
2018-01-30 20:17:17 +01:00
/// Ok(())
/// })
2018-06-17 00:09:07 +02:00
/// );
2018-01-30 20:17:17 +01:00
/// }
/// ```
pub fn with_connector(connector: SslConnector) -> ClientConnector {
let (tx, rx) = mpsc::unbounded();
2018-04-05 01:39:01 +02:00
ClientConnector {
connector,
2018-04-12 01:11:11 +02:00
stats: ClientConnectorStats::default(),
subscriber: None,
2018-05-27 14:18:37 +02:00
acq_tx: tx,
acq_rx: Some(rx),
2018-06-09 16:53:46 +02:00
resolver: None,
2018-04-12 22:08:13 +02:00
conn_lifetime: Duration::from_secs(75),
conn_keep_alive: Duration::from_secs(15),
2018-04-05 01:39:01 +02:00
limit: 100,
limit_per_host: 0,
acquired: 0,
acquired_per_host: HashMap::new(),
available: HashMap::new(),
to_close: Vec::new(),
waiters: Some(HashMap::new()),
2018-04-06 03:33:58 +02:00
wait_timeout: None,
paused: Paused::No,
2018-04-05 01:39:01 +02:00
}
}
/// Set total number of simultaneous connections.
///
/// If limit is 0, the connector has no limit.
/// The default limit size is 100.
pub fn limit(mut self, limit: usize) -> Self {
self.limit = limit;
self
}
/// Set total number of simultaneous connections to the same endpoint.
///
/// Endpoints are the same if they have equal (host, port, ssl) triplets.
2018-04-05 01:39:01 +02:00
/// If limit is 0, the connector has no limit. The default limit size is 0.
pub fn limit_per_host(mut self, limit: usize) -> Self {
self.limit_per_host = limit;
self
}
/// Set keep-alive period for opened connection.
///
/// Keep-alive period is the period between connection usage. If
/// the delay between repeated usages of the same connection
/// exceeds this period, the connection is closed.
2018-05-08 14:54:06 +02:00
/// Default keep-alive period is 15 seconds.
2018-04-05 01:39:01 +02:00
pub fn conn_keep_alive(mut self, dur: Duration) -> Self {
self.conn_keep_alive = dur;
self
}
/// Set max lifetime period for connection.
///
/// Connection lifetime is max lifetime of any opened connection
/// until it is closed regardless of keep-alive period.
2018-05-08 14:54:06 +02:00
/// Default lifetime period is 75 seconds.
2018-04-05 01:39:01 +02:00
pub fn conn_lifetime(mut self, dur: Duration) -> Self {
self.conn_lifetime = dur;
self
}
2018-04-12 01:11:11 +02:00
/// Subscribe for connector stats. Only one subscriber is supported.
pub fn stats(mut self, subs: Recipient<ClientConnectorStats>) -> Self {
2018-04-12 01:11:11 +02:00
self.subscriber = Some(subs);
self
}
/// Use custom resolver actor
2018-06-10 19:14:13 +02:00
pub fn resolver(mut self, addr: Addr<Resolver>) -> Self {
2018-06-09 16:53:46 +02:00
self.resolver = Some(addr);
self
}
2018-04-05 01:39:01 +02:00
fn acquire(&mut self, key: &Key) -> Acquire {
// check limits
if self.limit > 0 {
if self.acquired >= self.limit {
2018-04-14 01:02:01 +02:00
return Acquire::NotAvailable;
2018-04-05 01:39:01 +02:00
}
if self.limit_per_host > 0 {
if let Some(per_host) = self.acquired_per_host.get(key) {
if self.limit_per_host >= *per_host {
2018-04-14 01:02:01 +02:00
return Acquire::NotAvailable;
2018-04-05 01:39:01 +02:00
}
}
}
2018-04-14 01:02:01 +02:00
} else if self.limit_per_host > 0 {
2018-04-05 01:39:01 +02:00
if let Some(per_host) = self.acquired_per_host.get(key) {
if self.limit_per_host >= *per_host {
2018-04-14 01:02:01 +02:00
return Acquire::NotAvailable;
2018-04-05 01:39:01 +02:00
}
}
}
self.reserve(key);
// check if open connection is available
// cleanup stale connections at the same time
if let Some(ref mut connections) = self.available.get_mut(key) {
let now = Instant::now();
while let Some(conn) = connections.pop_back() {
// check if it still usable
if (now - conn.0) > self.conn_keep_alive
|| (now - conn.1.ts) > self.conn_lifetime
{
2018-04-12 01:11:11 +02:00
self.stats.closed += 1;
2018-04-05 01:39:01 +02:00
self.to_close.push(conn.1);
} else {
let mut conn = conn.1;
let mut buf = [0; 2];
match conn.stream().read(&mut buf) {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => (),
Ok(n) if n > 0 => {
2018-04-12 01:11:11 +02:00
self.stats.closed += 1;
2018-04-05 01:39:01 +02:00
self.to_close.push(conn);
2018-04-14 01:02:01 +02:00
continue;
}
2018-04-05 01:39:01 +02:00
Ok(_) | Err(_) => continue,
}
2018-04-14 01:02:01 +02:00
return Acquire::Acquired(conn);
2018-04-05 01:39:01 +02:00
}
}
}
Acquire::Available
}
fn reserve(&mut self, key: &Key) {
self.acquired += 1;
2018-04-14 01:02:01 +02:00
let per_host = if let Some(per_host) = self.acquired_per_host.get(key) {
*per_host
} else {
0
};
2018-05-17 21:20:20 +02:00
self.acquired_per_host.insert(key.clone(), per_host + 1);
2018-01-30 20:17:17 +01:00
}
2018-03-16 20:04:01 +01:00
2018-04-05 01:39:01 +02:00
fn release_key(&mut self, key: &Key) {
self.acquired -= 1;
2018-04-14 01:02:01 +02:00
let per_host = if let Some(per_host) = self.acquired_per_host.get(key) {
*per_host
} else {
return;
};
2018-04-05 01:39:01 +02:00
if per_host > 1 {
2018-05-17 21:20:20 +02:00
self.acquired_per_host.insert(key.clone(), per_host - 1);
2018-04-05 01:39:01 +02:00
} else {
self.acquired_per_host.remove(key);
}
}
fn collect_periodic(&mut self, ctx: &mut Context<Self>) {
2018-04-05 01:39:01 +02:00
// check connections for shutdown
let mut idx = 0;
while idx < self.to_close.len() {
match AsyncWrite::shutdown(&mut self.to_close[idx]) {
Ok(Async::NotReady) => idx += 1,
_ => {
self.to_close.swap_remove(idx);
2018-04-05 01:39:01 +02:00
}
}
}
2018-04-06 03:33:58 +02:00
2018-04-05 01:39:01 +02:00
// re-schedule next collect period
2018-05-17 21:20:20 +02:00
ctx.run_later(Duration::from_secs(1), |act, ctx| act.collect_periodic(ctx));
2018-04-12 01:11:11 +02:00
// send stats
let stats = mem::replace(&mut self.stats, ClientConnectorStats::default());
if let Some(ref mut subscr) = self.subscriber {
let _ = subscr.do_send(stats);
}
2018-03-16 20:04:01 +01:00
}
2018-04-06 03:33:58 +02:00
fn collect_waiters(&mut self) {
let now = Instant::now();
let mut next = None;
for waiters in self.waiters.as_mut().unwrap().values_mut() {
2018-04-06 03:33:58 +02:00
let mut idx = 0;
while idx < waiters.len() {
if waiters[idx].wait <= now {
2018-04-12 22:08:13 +02:00
self.stats.timeouts += 1;
2018-04-06 03:33:58 +02:00
let waiter = waiters.swap_remove_back(idx).unwrap();
let _ = waiter.tx.send(Err(ClientConnectorError::Timeout));
} else {
if let Some(n) = next {
if waiters[idx].wait < n {
next = Some(waiters[idx].wait);
}
} else {
next = Some(waiters[idx].wait);
}
idx += 1;
}
}
}
if next.is_some() {
self.install_wait_timeout(next.unwrap());
}
}
2018-04-06 03:33:58 +02:00
fn install_wait_timeout(&mut self, time: Instant) {
if let Some(ref mut wait) = self.wait_timeout {
if wait.0 < time {
2018-04-14 01:02:01 +02:00
return;
2018-04-06 03:33:58 +02:00
}
}
2018-05-25 06:03:16 +02:00
let mut timeout = Delay::new(time);
2018-04-06 03:33:58 +02:00
let _ = timeout.poll();
self.wait_timeout = Some((time, timeout));
}
2018-04-14 01:02:01 +02:00
fn wait_for(
2018-04-29 07:55:47 +02:00
&mut self, key: Key, wait: Duration, conn_timeout: Duration,
2018-04-14 01:02:01 +02:00
) -> oneshot::Receiver<Result<Connection, ClientConnectorError>> {
// connection is not available, wait
let (tx, rx) = oneshot::channel();
let wait = Instant::now() + wait;
self.install_wait_timeout(wait);
2018-04-14 01:02:01 +02:00
let waiter = Waiter {
tx,
wait,
conn_timeout,
};
2018-04-29 18:09:08 +02:00
self.waiters
.as_mut()
.unwrap()
2018-04-29 18:09:08 +02:00
.entry(key)
.or_insert_with(VecDeque::new)
.push_back(waiter);
rx
}
}
impl Handler<Pause> for ClientConnector {
type Result = ();
fn handle(&mut self, msg: Pause, _: &mut Self::Context) {
if let Some(time) = msg.time {
let when = Instant::now() + time;
2018-05-25 06:03:16 +02:00
let mut timeout = Delay::new(when);
let _ = timeout.poll();
self.paused = Paused::Timeout(when, timeout);
} else {
self.paused = Paused::Yes;
}
}
}
impl Handler<Resume> for ClientConnector {
type Result = ();
fn handle(&mut self, _: Resume, _: &mut Self::Context) {
self.paused = Paused::No;
}
2018-01-30 20:17:17 +01:00
}
2018-01-30 08:01:20 +01:00
impl Handler<Connect> for ClientConnector {
2018-02-12 21:17:30 +01:00
type Result = ActorResponse<ClientConnector, Connection, ClientConnectorError>;
2018-01-30 08:01:20 +01:00
fn handle(&mut self, msg: Connect, _: &mut Self::Context) -> Self::Result {
2018-03-06 23:26:09 +01:00
let uri = &msg.uri;
let wait_timeout = msg.wait_timeout;
2018-03-07 02:04:48 +01:00
let conn_timeout = msg.conn_timeout;
2018-01-30 08:01:20 +01:00
2018-01-30 20:17:17 +01:00
// host name is required
2018-01-30 08:01:20 +01:00
if uri.host().is_none() {
2018-04-14 01:02:01 +02:00
return ActorResponse::reply(Err(ClientConnectorError::InvalidUrl));
2018-01-30 08:01:20 +01:00
}
2018-01-30 20:17:17 +01:00
// supported protocols
2018-01-30 08:01:20 +01:00
let proto = match uri.scheme_part() {
Some(scheme) => match Protocol::from(scheme.as_str()) {
Some(proto) => proto,
2018-04-14 01:02:01 +02:00
None => {
return ActorResponse::reply(Err(ClientConnectorError::InvalidUrl))
}
2018-01-30 08:01:20 +01:00
},
2018-02-12 21:17:30 +01:00
None => return ActorResponse::reply(Err(ClientConnectorError::InvalidUrl)),
2018-01-30 08:01:20 +01:00
};
2018-01-30 20:17:17 +01:00
// check ssl availability
2018-03-07 02:34:46 +01:00
if proto.is_secure() && !HAS_OPENSSL && !HAS_TLS {
2018-04-14 01:02:01 +02:00
return ActorResponse::reply(Err(ClientConnectorError::SslIsNotSupported));
2018-01-30 20:17:17 +01:00
}
let host = uri.host().unwrap().to_owned();
2018-01-30 08:01:20 +01:00
let port = uri.port().unwrap_or_else(|| proto.port());
2018-04-14 01:02:01 +02:00
let key = Key {
host,
port,
ssl: proto.is_secure(),
};
2018-03-16 20:04:01 +01:00
// check pause state
if self.paused.is_paused() {
let rx = self.wait_for(key, wait_timeout, conn_timeout);
2018-04-12 01:11:11 +02:00
self.stats.waits += 1;
return ActorResponse::async(
rx.map_err(|_| ClientConnectorError::Disconnected)
.into_actor(self)
.and_then(|res, _, _| match res {
Ok(conn) => fut::ok(conn),
Err(err) => fut::err(err),
2018-04-14 01:02:01 +02:00
}),
);
}
2018-04-05 01:39:01 +02:00
// acquire connection
2018-03-16 20:04:01 +01:00
let pool = if proto.is_http() {
2018-04-05 01:39:01 +02:00
match self.acquire(&key) {
Acquire::Acquired(mut conn) => {
// use existing connection
conn.pool = Some(AcquiredConn(key, Some(self.acq_tx.clone())));
2018-04-12 01:11:11 +02:00
self.stats.reused += 1;
2018-04-14 01:02:01 +02:00
return ActorResponse::async(fut::ok(conn));
}
2018-04-05 01:39:01 +02:00
Acquire::NotAvailable => {
// connection is not available, wait
let rx = self.wait_for(key, wait_timeout, conn_timeout);
2018-04-12 01:11:11 +02:00
self.stats.waits += 1;
2018-04-05 01:39:01 +02:00
return ActorResponse::async(
rx.map_err(|_| ClientConnectorError::Disconnected)
.into_actor(self)
.and_then(|res, _, _| match res {
Ok(conn) => fut::ok(conn),
Err(err) => fut::err(err),
2018-04-14 01:02:01 +02:00
}),
);
2018-04-05 01:39:01 +02:00
}
Acquire::Available => Some(self.acq_tx.clone()),
2018-03-16 20:04:01 +01:00
}
} else {
None
};
2018-04-05 01:39:01 +02:00
let conn = AcquiredConn(key, pool);
2018-04-14 01:02:01 +02:00
{
ActorResponse::async(
self.resolver
2018-06-09 16:53:46 +02:00
.as_ref()
.unwrap()
2018-04-14 01:02:01 +02:00
.send(
ResolveConnect::host_and_port(&conn.0.host, port)
.timeout(conn_timeout),
)
.into_actor(self)
.map_err(|_, _, _| ClientConnectorError::Disconnected)
.and_then(move |res, act, _| {
#[cfg(feature = "alpn")]
match res {
Err(err) => {
act.stats.opened += 1;
fut::Either::B(fut::err(err.into()))
}
Ok(stream) => {
act.stats.opened += 1;
if proto.is_secure() {
fut::Either::A(
act.connector
.connect_async(&conn.0.host, stream)
.map_err(ClientConnectorError::SslError)
.map(|stream| {
Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)
})
.into_actor(act),
)
} else {
fut::Either::B(fut::ok(Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)))
}
}
2018-01-30 20:17:17 +01:00
}
2018-04-14 01:02:01 +02:00
#[cfg(all(feature = "tls", not(feature = "alpn")))]
match res {
Err(err) => {
act.stats.opened += 1;
fut::Either::B(fut::err(err.into()))
}
Ok(stream) => {
act.stats.opened += 1;
if proto.is_secure() {
fut::Either::A(
act.connector
.connect_async(&conn.0.host, stream)
.map_err(ClientConnectorError::SslError)
.map(|stream| {
Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)
})
.into_actor(act),
)
} else {
fut::Either::B(fut::ok(Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)))
}
}
2018-03-07 02:34:46 +01:00
}
2018-04-14 01:02:01 +02:00
#[cfg(not(any(feature = "alpn", feature = "tls")))]
match res {
Err(err) => {
act.stats.opened += 1;
fut::err(err.into())
}
Ok(stream) => {
act.stats.opened += 1;
if proto.is_secure() {
fut::err(ClientConnectorError::SslIsNotSupported)
} else {
fut::ok(Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
))
}
}
2018-01-30 20:17:17 +01:00
}
2018-04-14 01:02:01 +02:00
}),
)
}
2018-04-05 01:39:01 +02:00
}
}
2018-06-21 09:34:36 +02:00
impl StreamHandler2<AcquiredConnOperation, ()> for ClientConnector {
2018-06-09 16:53:46 +02:00
fn handle(
&mut self, msg: Result<Option<AcquiredConnOperation>, ()>,
ctx: &mut Context<Self>,
) {
let now = Instant::now();
match msg {
2018-06-09 16:53:46 +02:00
Ok(Some(AcquiredConnOperation::Close(conn))) => {
self.release_key(&conn.key);
self.to_close.push(conn);
self.stats.closed += 1;
}
2018-06-09 16:53:46 +02:00
Ok(Some(AcquiredConnOperation::Release(conn))) => {
self.release_key(&conn.key);
// check connection lifetime and the return to available pool
if (Instant::now() - conn.ts) < self.conn_lifetime {
self.available
.entry(conn.key.clone())
.or_insert_with(VecDeque::new)
.push_back(Conn(Instant::now(), conn));
}
}
2018-06-09 16:53:46 +02:00
Ok(Some(AcquiredConnOperation::ReleaseKey(key))) => {
self.release_key(&key);
}
2018-06-09 16:53:46 +02:00
_ => ctx.stop(),
}
// check keep-alive
for conns in self.available.values_mut() {
while !conns.is_empty() {
if (now > conns[0].0) && (now - conns[0].0) > self.conn_keep_alive
|| (now - conns[0].1.ts) > self.conn_lifetime
{
let conn = conns.pop_front().unwrap().1;
self.to_close.push(conn);
self.stats.closed += 1;
} else {
break;
}
}
}
}
}
2018-04-05 01:39:01 +02:00
struct Maintenance;
2018-04-14 01:02:01 +02:00
impl fut::ActorFuture for Maintenance {
2018-04-05 01:39:01 +02:00
type Item = ();
type Error = ();
type Actor = ClientConnector;
2018-04-14 01:02:01 +02:00
fn poll(
2018-04-29 07:55:47 +02:00
&mut self, act: &mut ClientConnector, ctx: &mut Context<ClientConnector>,
2018-04-14 01:02:01 +02:00
) -> Poll<Self::Item, Self::Error> {
// check pause duration
if let Paused::Timeout(inst, _) = act.paused {
if inst <= Instant::now() {
act.paused = Paused::No;
}
2018-04-06 03:33:58 +02:00
}
// collect wait timers
act.collect_waiters();
2018-04-05 01:39:01 +02:00
// check waiters
let mut waiters = act.waiters.take().unwrap();
2018-04-05 01:39:01 +02:00
for (key, waiters) in &mut waiters {
2018-04-05 01:39:01 +02:00
while let Some(waiter) = waiters.pop_front() {
2018-04-14 01:02:01 +02:00
if waiter.tx.is_canceled() {
continue;
}
2018-04-05 01:39:01 +02:00
match act.acquire(key) {
Acquire::Acquired(mut conn) => {
// use existing connection
2018-04-12 01:11:11 +02:00
act.stats.reused += 1;
2018-04-14 01:02:01 +02:00
conn.pool =
Some(AcquiredConn(key.clone(), Some(act.acq_tx.clone())));
2018-04-05 01:39:01 +02:00
let _ = waiter.tx.send(Ok(conn));
2018-04-14 01:02:01 +02:00
}
2018-04-05 01:39:01 +02:00
Acquire::NotAvailable => {
waiters.push_front(waiter);
2018-04-14 01:02:01 +02:00
break;
}
Acquire::Available => {
let conn = AcquiredConn(key.clone(), Some(act.acq_tx.clone()));
2018-04-14 01:02:01 +02:00
fut::WrapFuture::<ClientConnector>::actfuture(
2018-06-10 19:14:13 +02:00
Resolver::from_registry().send(
2018-04-14 01:02:01 +02:00
ResolveConnect::host_and_port(&conn.0.host, conn.0.port)
.timeout(waiter.conn_timeout),
),
).map_err(|_, _, _| ())
.and_then(move |res, act, _| {
#[cfg(feature = "alpn")]
match res {
Err(err) => {
act.stats.errors += 1;
let _ = waiter.tx.send(Err(err.into()));
fut::Either::B(fut::err(()))
}
Ok(stream) => {
act.stats.opened += 1;
if conn.0.ssl {
fut::Either::A(
act.connector
.connect_async(&key.host, stream)
.then(move |res| {
match res {
Err(e) => {
let _ = waiter.tx.send(
Err(ClientConnectorError::SslError(e)));
}
Ok(stream) => {
let _ = waiter.tx.send(
Ok(Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)),
);
}
}
Ok(())
})
.actfuture(),
)
} else {
let _ = waiter.tx.send(Ok(Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)));
fut::Either::B(fut::ok(()))
}
}
}
#[cfg(all(feature = "tls", not(feature = "alpn")))]
match res {
Err(err) => {
act.stats.errors += 1;
let _ = waiter.tx.send(Err(err.into()));
fut::Either::B(fut::err(()))
}
Ok(stream) => {
act.stats.opened += 1;
if conn.0.ssl {
fut::Either::A(
act.connector
.connect_async(&conn.0.host, stream)
.then(|res| {
match res {
Err(e) => {
let _ = waiter.tx.send(Err(
ClientConnectorError::SslError(e),
));
}
Ok(stream) => {
let _ = waiter.tx.send(
Ok(Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)),
);
}
}
Ok(())
})
.into_actor(act),
)
} else {
let _ = waiter.tx.send(Ok(Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)));
fut::Either::B(fut::ok(()))
}
}
}
#[cfg(not(any(feature = "alpn", feature = "tls")))]
match res {
Err(err) => {
act.stats.errors += 1;
let _ = waiter.tx.send(Err(err.into()));
fut::err(())
}
Ok(stream) => {
act.stats.opened += 1;
if conn.0.ssl {
let _ = waiter.tx.send(Err(
ClientConnectorError::SslIsNotSupported,
));
} else {
let _ = waiter.tx.send(Ok(Connection::new(
conn.0.clone(),
Some(conn),
Box::new(stream),
)));
};
fut::ok(())
}
}
2018-04-14 01:02:01 +02:00
})
.spawn(ctx);
2018-04-05 01:39:01 +02:00
}
}
}
}
act.waiters = Some(waiters);
2018-04-05 01:39:01 +02:00
Ok(Async::NotReady)
2018-01-30 08:01:20 +01:00
}
}
2018-01-30 20:17:17 +01:00
#[derive(PartialEq, Hash, Debug, Clone, Copy)]
2018-01-30 08:01:20 +01:00
enum Protocol {
Http,
Https,
Ws,
Wss,
}
impl Protocol {
fn from(s: &str) -> Option<Protocol> {
match s {
"http" => Some(Protocol::Http),
"https" => Some(Protocol::Https),
"ws" => Some(Protocol::Ws),
"wss" => Some(Protocol::Wss),
_ => None,
}
}
2018-03-16 20:04:01 +01:00
fn is_http(&self) -> bool {
match *self {
Protocol::Https | Protocol::Http => true,
_ => false,
}
}
2018-01-30 20:17:17 +01:00
fn is_secure(&self) -> bool {
match *self {
Protocol::Https | Protocol::Wss => true,
_ => false,
}
}
2018-01-30 08:01:20 +01:00
fn port(&self) -> u16 {
match *self {
Protocol::Http | Protocol::Ws => 80,
2018-04-14 01:02:01 +02:00
Protocol::Https | Protocol::Wss => 443,
2018-01-30 08:01:20 +01:00
}
}
}
2018-03-16 20:04:01 +01:00
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
struct Key {
host: String,
port: u16,
ssl: bool,
}
impl Key {
fn empty() -> Key {
2018-04-14 01:02:01 +02:00
Key {
host: String::new(),
port: 0,
ssl: false,
}
2018-03-16 20:04:01 +01:00
}
}
#[derive(Debug)]
struct Conn(Instant, Connection);
2018-04-05 01:39:01 +02:00
enum Acquire {
Acquired(Connection),
Available,
NotAvailable,
}
enum AcquiredConnOperation {
Close(Connection),
Release(Connection),
ReleaseKey(Key),
}
struct AcquiredConn(Key, Option<mpsc::UnboundedSender<AcquiredConnOperation>>);
2018-04-05 01:39:01 +02:00
impl AcquiredConn {
fn close(&mut self, conn: Connection) {
if let Some(tx) = self.1.take() {
let _ = tx.unbounded_send(AcquiredConnOperation::Close(conn));
2018-04-05 01:39:01 +02:00
}
}
fn release(&mut self, conn: Connection) {
if let Some(tx) = self.1.take() {
let _ = tx.unbounded_send(AcquiredConnOperation::Release(conn));
2018-04-05 01:39:01 +02:00
}
}
}
impl Drop for AcquiredConn {
fn drop(&mut self) {
if let Some(tx) = self.1.take() {
let _ = tx.unbounded_send(AcquiredConnOperation::ReleaseKey(self.0.clone()));
2018-03-16 20:04:01 +01:00
}
}
}
/// HTTP client connection
2018-01-30 08:01:20 +01:00
pub struct Connection {
2018-03-16 20:04:01 +01:00
key: Key,
stream: Box<IoStream + Send>,
2018-04-05 01:39:01 +02:00
pool: Option<AcquiredConn>,
2018-03-16 20:04:01 +01:00
ts: Instant,
}
impl fmt::Debug for Connection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Connection {}:{}", self.key.host, self.key.port)
}
2018-01-30 08:01:20 +01:00
}
impl Connection {
fn new(key: Key, pool: Option<AcquiredConn>, stream: Box<IoStream + Send>) -> Self {
2018-04-14 01:02:01 +02:00
Connection {
key,
stream,
pool,
ts: Instant::now(),
}
2018-03-16 20:04:01 +01:00
}
/// Raw IO stream
2018-01-30 08:01:20 +01:00
pub fn stream(&mut self) -> &mut IoStream {
&mut *self.stream
}
/// Create a new connection from an IO Stream
pub fn from_stream<T: IoStream + Send>(io: T) -> Connection {
2018-03-16 20:04:01 +01:00
Connection::new(Key::empty(), None, Box::new(io))
}
/// Close connection pool
2018-04-05 01:39:01 +02:00
pub fn close(mut self) {
if let Some(mut pool) = self.pool.take() {
pool.close(self)
}
}
/// Release this connection from the connection pool
2018-03-16 20:04:01 +01:00
pub fn release(mut self) {
2018-04-05 01:39:01 +02:00
if let Some(mut pool) = self.pool.take() {
2018-03-16 20:04:01 +01:00
pool.release(self)
}
}
2018-01-30 08:01:20 +01:00
}
impl IoStream for Connection {
fn shutdown(&mut self, how: Shutdown) -> io::Result<()> {
IoStream::shutdown(&mut *self.stream, how)
}
#[inline]
fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> {
IoStream::set_nodelay(&mut *self.stream, nodelay)
}
#[inline]
fn set_linger(&mut self, dur: Option<time::Duration>) -> io::Result<()> {
IoStream::set_linger(&mut *self.stream, dur)
}
}
impl io::Read for Connection {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.stream.read(buf)
}
}
impl AsyncRead for Connection {}
impl io::Write for Connection {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.stream.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.stream.flush()
}
}
impl AsyncWrite for Connection {
fn shutdown(&mut self) -> Poll<(), io::Error> {
self.stream.shutdown()
}
}