1
0
mirror of https://github.com/actix/actix-extras.git synced 2025-06-26 02:19:22 +02:00

add rustfmt config

This commit is contained in:
Nikolay Kim
2018-04-13 16:02:01 -07:00
parent 95f6277007
commit 113f5ad1a8
91 changed files with 8057 additions and 5509 deletions

View File

@ -1,36 +1,35 @@
use std::{fmt, mem, io, time};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::net::Shutdown;
use std::time::{Duration, Instant};
use std::collections::{HashMap, VecDeque};
use std::net::Shutdown;
use std::rc::Rc;
use std::time::{Duration, Instant};
use std::{fmt, io, mem, time};
use actix::{fut, Actor, ActorFuture, Arbiter, Context, AsyncContext,
Recipient, Syn, Handler, Message, ActorResponse,
Supervised, ContextFutureSpawner};
use actix::registry::ArbiterService;
use actix::actors::{Connect as ResolveConnect, Connector, ConnectorError};
use actix::fut::WrapFuture;
use actix::actors::{Connector, ConnectorError, Connect as ResolveConnect};
use actix::registry::ArbiterService;
use actix::{fut, Actor, ActorFuture, ActorResponse, Arbiter, AsyncContext, Context,
ContextFutureSpawner, Handler, Message, Recipient, Supervised, Syn};
use http::{Uri, HttpTryFrom, Error as HttpError};
use futures::{Async, Future, Poll};
use futures::task::{Task, current as current_task};
use futures::task::{current as current_task, Task};
use futures::unsync::oneshot;
use tokio_io::{AsyncRead, AsyncWrite};
use futures::{Async, Future, Poll};
use http::{Error as HttpError, HttpTryFrom, Uri};
use tokio_core::reactor::Timeout;
use tokio_io::{AsyncRead, AsyncWrite};
#[cfg(feature="alpn")]
use openssl::ssl::{SslMethod, SslConnector, Error as OpensslError};
#[cfg(feature="alpn")]
#[cfg(feature = "alpn")]
use openssl::ssl::{Error as OpensslError, SslConnector, SslMethod};
#[cfg(feature = "alpn")]
use tokio_openssl::SslConnectorExt;
#[cfg(all(feature="tls", not(feature="alpn")))]
use native_tls::{TlsConnector, Error as TlsError};
#[cfg(all(feature="tls", not(feature="alpn")))]
#[cfg(all(feature = "tls", not(feature = "alpn")))]
use native_tls::{Error as TlsError, TlsConnector};
#[cfg(all(feature = "tls", not(feature = "alpn")))]
use tokio_tls::TlsConnectorExt;
use {HAS_OPENSSL, HAS_TLS};
use server::IoStream;
use {HAS_OPENSSL, HAS_TLS};
/// Client connector usage stats
#[derive(Default, Message)]
@ -54,7 +53,10 @@ pub struct Connect {
impl Connect {
/// Create `Connect` message for specified `Uri`
pub fn new<U>(uri: U) -> Result<Connect, HttpError> where Uri: HttpTryFrom<U> {
pub fn new<U>(uri: U) -> Result<Connect, HttpError>
where
Uri: HttpTryFrom<U>,
{
Ok(Connect {
uri: Uri::try_from(uri).map_err(|e| e.into())?,
wait_timeout: Duration::from_secs(5),
@ -92,13 +94,13 @@ pub struct Pause {
impl Pause {
/// Create message with pause duration parameter
pub fn new(time: Duration) -> Pause {
Pause{time: Some(time)}
Pause { time: Some(time) }
}
}
impl Default for Pause {
fn default() -> Pause {
Pause{time: None}
Pause { time: None }
}
}
@ -114,21 +116,21 @@ pub struct Resume;
#[derive(Fail, Debug)]
pub enum ClientConnectorError {
/// Invalid URL
#[fail(display="Invalid URL")]
#[fail(display = "Invalid URL")]
InvalidUrl,
/// SSL feature is not enabled
#[fail(display="SSL is not supported")]
#[fail(display = "SSL is not supported")]
SslIsNotSupported,
/// SSL error
#[cfg(feature="alpn")]
#[fail(display="{}", _0)]
#[cfg(feature = "alpn")]
#[fail(display = "{}", _0)]
SslError(#[cause] OpensslError),
/// SSL error
#[cfg(all(feature="tls", not(feature="alpn")))]
#[fail(display="{}", _0)]
#[cfg(all(feature = "tls", not(feature = "alpn")))]
#[fail(display = "{}", _0)]
SslError(#[cause] TlsError),
/// Connection error
@ -152,7 +154,7 @@ impl From<ConnectorError> for ClientConnectorError {
fn from(err: ConnectorError) -> ClientConnectorError {
match err {
ConnectorError::Timeout => ClientConnectorError::Timeout,
_ => ClientConnectorError::Connector(err)
_ => ClientConnectorError::Connector(err),
}
}
}
@ -166,9 +168,9 @@ struct Waiter {
/// `ClientConnector` type is responsible for transport layer of a
/// client connection.
pub struct ClientConnector {
#[cfg(all(feature="alpn"))]
#[cfg(all(feature = "alpn"))]
connector: SslConnector,
#[cfg(all(feature="tls", not(feature="alpn")))]
#[cfg(all(feature = "tls", not(feature = "alpn")))]
connector: TlsConnector,
stats: ClientConnectorStats,
@ -207,12 +209,12 @@ impl Default for ClientConnector {
fn default() -> ClientConnector {
let _modified = Rc::new(Cell::new(false));
#[cfg(all(feature="alpn"))]
#[cfg(all(feature = "alpn"))]
{
let builder = SslConnector::builder(SslMethod::tls()).unwrap();
ClientConnector::with_connector(builder.build())
}
#[cfg(all(feature="tls", not(feature="alpn")))]
#[cfg(all(feature = "tls", not(feature = "alpn")))]
{
let builder = TlsConnector::builder().unwrap();
ClientConnector {
@ -235,29 +237,29 @@ impl Default for ClientConnector {
}
}
#[cfg(not(any(feature="alpn", feature="tls")))]
ClientConnector {stats: ClientConnectorStats::default(),
subscriber: None,
pool: Rc::new(Pool::new(Rc::clone(&_modified))),
pool_modified: _modified,
conn_lifetime: Duration::from_secs(15),
conn_keep_alive: Duration::from_secs(75),
limit: 100,
limit_per_host: 0,
acquired: 0,
acquired_per_host: HashMap::new(),
available: HashMap::new(),
to_close: Vec::new(),
waiters: HashMap::new(),
wait_timeout: None,
paused: None,
#[cfg(not(any(feature = "alpn", feature = "tls")))]
ClientConnector {
stats: ClientConnectorStats::default(),
subscriber: None,
pool: Rc::new(Pool::new(Rc::clone(&_modified))),
pool_modified: _modified,
conn_lifetime: Duration::from_secs(15),
conn_keep_alive: Duration::from_secs(75),
limit: 100,
limit_per_host: 0,
acquired: 0,
acquired_per_host: HashMap::new(),
available: HashMap::new(),
to_close: Vec::new(),
waiters: HashMap::new(),
wait_timeout: None,
paused: None,
}
}
}
impl ClientConnector {
#[cfg(feature="alpn")]
#[cfg(feature = "alpn")]
/// Create `ClientConnector` actor with custom `SslConnector` instance.
///
/// By default `ClientConnector` uses very a simple SSL configuration.
@ -369,20 +371,19 @@ impl ClientConnector {
// check limits
if self.limit > 0 {
if self.acquired >= self.limit {
return Acquire::NotAvailable
return Acquire::NotAvailable;
}
if self.limit_per_host > 0 {
if let Some(per_host) = self.acquired_per_host.get(key) {
if self.limit_per_host >= *per_host {
return Acquire::NotAvailable
return Acquire::NotAvailable;
}
}
}
}
else if self.limit_per_host > 0 {
} else if self.limit_per_host > 0 {
if let Some(per_host) = self.acquired_per_host.get(key) {
if self.limit_per_host >= *per_host {
return Acquire::NotAvailable
return Acquire::NotAvailable;
}
}
}
@ -408,11 +409,11 @@ impl ClientConnector {
Ok(n) if n > 0 => {
self.stats.closed += 1;
self.to_close.push(conn);
continue
},
continue;
}
Ok(_) | Err(_) => continue,
}
return Acquire::Acquired(conn)
return Acquire::Acquired(conn);
}
}
}
@ -421,25 +422,25 @@ impl ClientConnector {
fn reserve(&mut self, key: &Key) {
self.acquired += 1;
let per_host =
if let Some(per_host) = self.acquired_per_host.get(key) {
*per_host
} else {
0
};
self.acquired_per_host.insert(key.clone(), per_host + 1);
let per_host = if let Some(per_host) = self.acquired_per_host.get(key) {
*per_host
} else {
0
};
self.acquired_per_host
.insert(key.clone(), per_host + 1);
}
fn release_key(&mut self, key: &Key) {
self.acquired -= 1;
let per_host =
if let Some(per_host) = self.acquired_per_host.get(key) {
*per_host
} else {
return
};
let per_host = if let Some(per_host) = self.acquired_per_host.get(key) {
*per_host
} else {
return;
};
if per_host > 1 {
self.acquired_per_host.insert(key.clone(), per_host - 1);
self.acquired_per_host
.insert(key.clone(), per_host - 1);
} else {
self.acquired_per_host.remove(key);
}
@ -472,7 +473,8 @@ impl ClientConnector {
// check connection lifetime and the return to available pool
if (now - conn.ts) < self.conn_lifetime {
self.available.entry(conn.key.clone())
self.available
.entry(conn.key.clone())
.or_insert_with(VecDeque::new)
.push_back(Conn(Instant::now(), conn));
}
@ -490,7 +492,7 @@ impl ClientConnector {
self.to_close.push(conn);
self.stats.closed += 1;
} else {
break
break;
}
}
}
@ -503,7 +505,7 @@ impl ClientConnector {
Ok(Async::NotReady) => idx += 1,
_ => {
self.to_close.swap_remove(idx);
},
}
}
}
}
@ -514,7 +516,9 @@ impl ClientConnector {
fn collect_periodic(&mut self, ctx: &mut Context<Self>) {
self.collect(true);
// re-schedule next collect period
ctx.run_later(Duration::from_secs(1), |act, ctx| act.collect_periodic(ctx));
ctx.run_later(Duration::from_secs(1), |act, ctx| {
act.collect_periodic(ctx)
});
// send stats
let stats = mem::replace(&mut self.stats, ClientConnectorStats::default());
@ -555,27 +559,34 @@ impl ClientConnector {
fn install_wait_timeout(&mut self, time: Instant) {
if let Some(ref mut wait) = self.wait_timeout {
if wait.0 < time {
return
return;
}
}
let mut timeout = Timeout::new(time-Instant::now(), Arbiter::handle()).unwrap();
let mut timeout =
Timeout::new(time - Instant::now(), Arbiter::handle()).unwrap();
let _ = timeout.poll();
self.wait_timeout = Some((time, timeout));
}
fn wait_for(&mut self, key: Key,
wait: Duration, conn_timeout: Duration)
-> oneshot::Receiver<Result<Connection, ClientConnectorError>>
{
fn wait_for(
&mut self, key: Key, wait: Duration, conn_timeout: Duration
) -> 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);
let waiter = Waiter{ tx, wait, conn_timeout };
self.waiters.entry(key).or_insert_with(VecDeque::new).push_back(waiter);
let waiter = Waiter {
tx,
wait,
conn_timeout,
};
self.waiters
.entry(key)
.or_insert_with(VecDeque::new)
.push_back(waiter);
rx
}
}
@ -617,21 +628,23 @@ impl Handler<Connect> for ClientConnector {
// host name is required
if uri.host().is_none() {
return ActorResponse::reply(Err(ClientConnectorError::InvalidUrl))
return ActorResponse::reply(Err(ClientConnectorError::InvalidUrl));
}
// supported protocols
let proto = match uri.scheme_part() {
Some(scheme) => match Protocol::from(scheme.as_str()) {
Some(proto) => proto,
None => return ActorResponse::reply(Err(ClientConnectorError::InvalidUrl)),
None => {
return ActorResponse::reply(Err(ClientConnectorError::InvalidUrl))
}
},
None => return ActorResponse::reply(Err(ClientConnectorError::InvalidUrl)),
};
// check ssl availability
if proto.is_secure() && !HAS_OPENSSL && !HAS_TLS {
return ActorResponse::reply(Err(ClientConnectorError::SslIsNotSupported))
return ActorResponse::reply(Err(ClientConnectorError::SslIsNotSupported));
}
// check if pool has task reference
@ -641,7 +654,11 @@ impl Handler<Connect> for ClientConnector {
let host = uri.host().unwrap().to_owned();
let port = uri.port().unwrap_or_else(|| proto.port());
let key = Key {host, port, ssl: proto.is_secure()};
let key = Key {
host,
port,
ssl: proto.is_secure(),
};
// check pause state
if self.paused.is_some() {
@ -653,7 +670,8 @@ impl Handler<Connect> for ClientConnector {
.and_then(|res, _, _| match res {
Ok(conn) => fut::ok(conn),
Err(err) => fut::err(err),
}));
}),
);
}
// acquire connection
@ -663,8 +681,8 @@ impl Handler<Connect> for ClientConnector {
// use existing connection
conn.pool = Some(AcquiredConn(key, Some(Rc::clone(&self.pool))));
self.stats.reused += 1;
return ActorResponse::async(fut::ok(conn))
},
return ActorResponse::async(fut::ok(conn));
}
Acquire::NotAvailable => {
// connection is not available, wait
let rx = self.wait_for(key, wait_timeout, conn_timeout);
@ -675,106 +693,131 @@ impl Handler<Connect> for ClientConnector {
.and_then(|res, _, _| match res {
Ok(conn) => fut::ok(conn),
Err(err) => fut::err(err),
}));
}),
);
}
Acquire::Available => {
Some(Rc::clone(&self.pool))
},
Acquire::Available => Some(Rc::clone(&self.pool)),
}
} else {
None
};
let conn = AcquiredConn(key, pool);
{
ActorResponse::async(
Connector::from_registry()
.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))))
{
ActorResponse::async(
Connector::from_registry()
.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),
)))
}
}
}
}
}
#[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))))
#[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),
)))
}
}
}
}
}
#[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)))
#[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),
))
}
}
}
}
}
}))
}
}),
)
}
}
}
struct Maintenance;
impl fut::ActorFuture for Maintenance
{
impl fut::ActorFuture for Maintenance {
type Item = ();
type Error = ();
type Actor = ClientConnector;
fn poll(&mut self, act: &mut ClientConnector, ctx: &mut Context<ClientConnector>)
-> Poll<Self::Item, Self::Error>
{
fn poll(
&mut self, act: &mut ClientConnector, ctx: &mut Context<ClientConnector>
) -> Poll<Self::Item, Self::Error> {
// check pause duration
let done = if let Some(Some(ref pause)) = act.paused {
pause.0 <= Instant::now() } else { false };
pause.0 <= Instant::now()
} else {
false
};
if done {
act.paused.take();
}
@ -788,128 +831,151 @@ impl fut::ActorFuture for Maintenance
act.collect_waiters();
// check waiters
let tmp: &mut ClientConnector = unsafe{mem::transmute(act as &mut _)};
let tmp: &mut ClientConnector = unsafe { mem::transmute(act as &mut _) };
for (key, waiters) in &mut tmp.waiters {
while let Some(waiter) = waiters.pop_front() {
if waiter.tx.is_canceled() { continue }
if waiter.tx.is_canceled() {
continue;
}
match act.acquire(key) {
Acquire::Acquired(mut conn) => {
// use existing connection
act.stats.reused += 1;
conn.pool = Some(
AcquiredConn(key.clone(), Some(Rc::clone(&act.pool))));
conn.pool =
Some(AcquiredConn(key.clone(), Some(Rc::clone(&act.pool))));
let _ = waiter.tx.send(Ok(conn));
},
}
Acquire::NotAvailable => {
waiters.push_front(waiter);
break
break;
}
Acquire::Available =>
{
let conn = AcquiredConn(key.clone(), Some(Rc::clone(&act.pool)));
Acquire::Available => {
let conn = AcquiredConn(key.clone(), Some(Rc::clone(&act.pool)));
fut::WrapFuture::<ClientConnector>::actfuture(
Connector::from_registry()
.send(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(()))
}
}
}
fut::WrapFuture::<ClientConnector>::actfuture(
Connector::from_registry().send(
ResolveConnect::host_and_port(&conn.0.host, conn.0.port)
.timeout(waiter.conn_timeout),
),
).map_err(|_, _, _| ())
.and_then(move |res, act, _| {
#[cfg_attr(rustfmt, rustfmt_skip)]
#[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_attr(rustfmt, rustfmt_skip)]
#[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(())
},
}
})
.spawn(ctx);
}
#[cfg_attr(rustfmt, rustfmt_skip)]
#[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(())
}
}
})
.spawn(ctx);
}
}
}
}
@ -954,7 +1020,7 @@ impl Protocol {
fn port(&self) -> u16 {
match *self {
Protocol::Http | Protocol::Ws => 80,
Protocol::Https | Protocol::Wss => 443
Protocol::Https | Protocol::Wss => 443,
}
}
}
@ -968,7 +1034,11 @@ struct Key {
impl Key {
fn empty() -> Key {
Key{host: String::new(), port: 0, ssl: false}
Key {
host: String::new(),
port: 0,
ssl: false,
}
}
}
@ -1035,7 +1105,10 @@ impl Pool {
if self.to_close.borrow().is_empty() {
None
} else {
Some(mem::replace(&mut *self.to_close.borrow_mut(), Vec::new()))
Some(mem::replace(
&mut *self.to_close.borrow_mut(),
Vec::new(),
))
}
}
@ -1043,7 +1116,10 @@ impl Pool {
if self.to_release.borrow().is_empty() {
None
} else {
Some(mem::replace(&mut *self.to_release.borrow_mut(), Vec::new()))
Some(mem::replace(
&mut *self.to_release.borrow_mut(),
Vec::new(),
))
}
}
@ -1072,7 +1148,6 @@ impl Pool {
}
}
pub struct Connection {
key: Key,
stream: Box<IoStream>,
@ -1088,7 +1163,12 @@ impl fmt::Debug for Connection {
impl Connection {
fn new(key: Key, pool: Option<AcquiredConn>, stream: Box<IoStream>) -> Self {
Connection {key, stream, pool, ts: Instant::now()}
Connection {
key,
stream,
pool,
ts: Instant::now(),
}
}
pub fn stream(&mut self) -> &mut IoStream {

View File

@ -28,33 +28,30 @@
//! ```
mod connector;
mod parser;
mod pipeline;
mod request;
mod response;
mod pipeline;
mod writer;
pub use self::connector::{ClientConnector, ClientConnectorError, ClientConnectorStats,
Connect, Connection, Pause, Resume};
pub(crate) use self::parser::{HttpResponseParser, HttpResponseParserError};
pub use self::pipeline::{SendRequest, SendRequestError};
pub use self::request::{ClientRequest, ClientRequestBuilder};
pub use self::response::ClientResponse;
pub use self::connector::{
Connect, Pause, Resume,
Connection, ClientConnector, ClientConnectorError, ClientConnectorStats};
pub(crate) use self::writer::HttpClientWriter;
pub(crate) use self::parser::{HttpResponseParser, HttpResponseParserError};
use error::ResponseError;
use http::Method;
use httpresponse::HttpResponse;
/// Convert `SendRequestError` to a `HttpResponse`
impl ResponseError for SendRequestError {
fn error_response(&self) -> HttpResponse {
match *self {
SendRequestError::Connector(_) => HttpResponse::BadGateway(),
_ => HttpResponse::InternalServerError(),
}
.into()
}.into()
}
}

View File

@ -1,14 +1,14 @@
use std::mem;
use httparse;
use http::{Version, HttpTryFrom, HeaderMap, StatusCode};
use http::header::{self, HeaderName, HeaderValue};
use bytes::{Bytes, BytesMut};
use futures::{Poll, Async};
use futures::{Async, Poll};
use http::header::{self, HeaderName, HeaderValue};
use http::{HeaderMap, HttpTryFrom, StatusCode, Version};
use httparse;
use std::mem;
use error::{ParseError, PayloadError};
use server::h1::{chunked, Decoder};
use server::{utils, IoStream};
use server::h1::{Decoder, chunked};
use super::ClientResponse;
use super::response::ClientMessage;
@ -24,28 +24,26 @@ pub struct HttpResponseParser {
#[derive(Debug, Fail)]
pub enum HttpResponseParserError {
/// Server disconnected
#[fail(display="Server disconnected")]
#[fail(display = "Server disconnected")]
Disconnect,
#[fail(display="{}", _0)]
#[fail(display = "{}", _0)]
Error(#[cause] ParseError),
}
impl HttpResponseParser {
pub fn parse<T>(&mut self, io: &mut T, buf: &mut BytesMut)
-> Poll<ClientResponse, HttpResponseParserError>
where T: IoStream
pub fn parse<T>(
&mut self, io: &mut T, buf: &mut BytesMut
) -> Poll<ClientResponse, HttpResponseParserError>
where
T: IoStream,
{
// if buf is empty parse_message will always return NotReady, let's avoid that
if buf.is_empty() {
match utils::read_from_io(io, buf) {
Ok(Async::Ready(0)) =>
return Err(HttpResponseParserError::Disconnect),
Ok(Async::Ready(0)) => return Err(HttpResponseParserError::Disconnect),
Ok(Async::Ready(_)) => (),
Ok(Async::NotReady) =>
return Ok(Async::NotReady),
Err(err) =>
return Err(HttpResponseParserError::Error(err.into()))
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(err) => return Err(HttpResponseParserError::Error(err.into())),
}
}
@ -56,27 +54,31 @@ impl HttpResponseParser {
Async::Ready((msg, decoder)) => {
self.decoder = decoder;
return Ok(Async::Ready(msg));
},
}
Async::NotReady => {
if buf.capacity() >= MAX_BUFFER_SIZE {
return Err(HttpResponseParserError::Error(ParseError::TooLarge));
}
match utils::read_from_io(io, buf) {
Ok(Async::Ready(0)) =>
return Err(HttpResponseParserError::Disconnect),
Ok(Async::Ready(0)) => {
return Err(HttpResponseParserError::Disconnect)
}
Ok(Async::Ready(_)) => (),
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(err) =>
return Err(HttpResponseParserError::Error(err.into())),
Err(err) => {
return Err(HttpResponseParserError::Error(err.into()))
}
}
},
}
}
}
}
pub fn parse_payload<T>(&mut self, io: &mut T, buf: &mut BytesMut)
-> Poll<Option<Bytes>, PayloadError>
where T: IoStream
pub fn parse_payload<T>(
&mut self, io: &mut T, buf: &mut BytesMut
) -> Poll<Option<Bytes>, PayloadError>
where
T: IoStream,
{
if self.decoder.is_some() {
loop {
@ -89,18 +91,17 @@ impl HttpResponseParser {
};
match self.decoder.as_mut().unwrap().decode(buf) {
Ok(Async::Ready(Some(b))) =>
return Ok(Async::Ready(Some(b))),
Ok(Async::Ready(Some(b))) => return Ok(Async::Ready(Some(b))),
Ok(Async::Ready(None)) => {
self.decoder.take();
return Ok(Async::Ready(None))
return Ok(Async::Ready(None));
}
Ok(Async::NotReady) => {
if not_ready {
return Ok(Async::NotReady)
return Ok(Async::NotReady);
}
if stream_finished {
return Err(PayloadError::Incomplete)
return Err(PayloadError::Incomplete);
}
}
Err(err) => return Err(err.into()),
@ -111,16 +112,19 @@ impl HttpResponseParser {
}
}
fn parse_message(buf: &mut BytesMut)
-> Poll<(ClientResponse, Option<Decoder>), ParseError>
{
fn parse_message(
buf: &mut BytesMut
) -> Poll<(ClientResponse, Option<Decoder>), ParseError> {
// Parse http message
let bytes_ptr = buf.as_ref().as_ptr() as usize;
let mut headers: [httparse::Header; MAX_HEADERS] =
unsafe{mem::uninitialized()};
unsafe { mem::uninitialized() };
let (len, version, status, headers_len) = {
let b = unsafe{ let b: &[u8] = buf; mem::transmute(b) };
let b = unsafe {
let b: &[u8] = buf;
mem::transmute(b)
};
let mut resp = httparse::Response::new(&mut headers);
match resp.parse(b)? {
httparse::Status::Complete(len) => {
@ -147,10 +151,11 @@ impl HttpResponseParser {
let v_start = header.value.as_ptr() as usize - bytes_ptr;
let v_end = v_start + header.value.len();
let value = unsafe {
HeaderValue::from_shared_unchecked(slice.slice(v_start, v_end)) };
HeaderValue::from_shared_unchecked(slice.slice(v_start, v_end))
};
hdrs.append(name, value);
} else {
return Err(ParseError::Header)
return Err(ParseError::Header);
}
}
@ -163,11 +168,11 @@ impl HttpResponseParser {
Some(Decoder::length(len))
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header)
return Err(ParseError::Header);
}
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header)
return Err(ParseError::Header);
}
} else if chunked(&hdrs)? {
// Chunked encoding
@ -177,15 +182,25 @@ impl HttpResponseParser {
};
if let Some(decoder) = decoder {
Ok(Async::Ready(
(ClientResponse::new(
ClientMessage{status, version,
headers: hdrs, cookies: None}), Some(decoder))))
Ok(Async::Ready((
ClientResponse::new(ClientMessage {
status,
version,
headers: hdrs,
cookies: None,
}),
Some(decoder),
)))
} else {
Ok(Async::Ready(
(ClientResponse::new(
ClientMessage{status, version,
headers: hdrs, cookies: None}), None)))
Ok(Async::Ready((
ClientResponse::new(ClientMessage {
status,
version,
headers: hdrs,
cookies: None,
}),
None,
)))
}
}
}

View File

@ -1,26 +1,26 @@
use std::{io, mem};
use std::time::Duration;
use bytes::{Bytes, BytesMut};
use http::header::CONTENT_ENCODING;
use futures::{Async, Future, Poll};
use futures::unsync::oneshot;
use futures::{Async, Future, Poll};
use http::header::CONTENT_ENCODING;
use std::time::Duration;
use std::{io, mem};
use tokio_core::reactor::Timeout;
use actix::prelude::*;
use error::Error;
use super::HttpClientWriter;
use super::{ClientConnector, ClientConnectorError, Connect, Connection};
use super::{ClientRequest, ClientResponse};
use super::{HttpResponseParser, HttpResponseParserError};
use body::{Body, BodyStream};
use context::{Frame, ActorHttpContext};
use context::{ActorHttpContext, Frame};
use error::Error;
use error::PayloadError;
use header::ContentEncoding;
use httpmessage::HttpMessage;
use error::PayloadError;
use server::WriterState;
use server::shared::SharedBytes;
use server::encoding::PayloadStream;
use super::{ClientRequest, ClientResponse};
use super::{Connect, Connection, ClientConnector, ClientConnectorError};
use super::HttpClientWriter;
use super::{HttpResponseParser, HttpResponseParserError};
use server::shared::SharedBytes;
/// A set of errors that can occur during request sending and response reading
#[derive(Fail, Debug)]
@ -29,13 +29,13 @@ pub enum SendRequestError {
#[fail(display = "Timeout while waiting for response")]
Timeout,
/// Failed to connect to host
#[fail(display="Failed to connect to host: {}", _0)]
#[fail(display = "Failed to connect to host: {}", _0)]
Connector(#[cause] ClientConnectorError),
/// Error parsing response
#[fail(display="{}", _0)]
#[fail(display = "{}", _0)]
ParseError(#[cause] HttpResponseParserError),
/// Error reading response payload
#[fail(display="Error reading response payload: {}", _0)]
#[fail(display = "Error reading response payload: {}", _0)]
Io(#[cause] io::Error),
}
@ -79,25 +79,27 @@ impl SendRequest {
SendRequest::with_connector(req, ClientConnector::from_registry())
}
pub(crate) fn with_connector(req: ClientRequest, conn: Addr<Unsync, ClientConnector>)
-> SendRequest
{
SendRequest{req, conn,
state: State::New,
timeout: None,
wait_timeout: Duration::from_secs(5),
conn_timeout: Duration::from_secs(1),
pub(crate) fn with_connector(
req: ClientRequest, conn: Addr<Unsync, ClientConnector>
) -> SendRequest {
SendRequest {
req,
conn,
state: State::New,
timeout: None,
wait_timeout: Duration::from_secs(5),
conn_timeout: Duration::from_secs(1),
}
}
pub(crate) fn with_connection(req: ClientRequest, conn: Connection) -> SendRequest
{
SendRequest{req,
state: State::Connection(conn),
conn: ClientConnector::from_registry(),
timeout: None,
wait_timeout: Duration::from_secs(5),
conn_timeout: Duration::from_secs(1),
pub(crate) fn with_connection(req: ClientRequest, conn: Connection) -> SendRequest {
SendRequest {
req,
state: State::Connection(conn),
conn: ClientConnector::from_registry(),
timeout: None,
wait_timeout: Duration::from_secs(5),
conn_timeout: Duration::from_secs(1),
}
}
@ -139,25 +141,27 @@ impl Future for SendRequest {
let state = mem::replace(&mut self.state, State::None);
match state {
State::New =>
State::New => {
self.state = State::Connect(self.conn.send(Connect {
uri: self.req.uri().clone(),
wait_timeout: self.wait_timeout,
conn_timeout: self.conn_timeout,
})),
}))
}
State::Connect(mut conn) => match conn.poll() {
Ok(Async::NotReady) => {
self.state = State::Connect(conn);
return Ok(Async::NotReady);
},
}
Ok(Async::Ready(result)) => match result {
Ok(stream) => {
self.state = State::Connection(stream)
},
Ok(stream) => self.state = State::Connection(stream),
Err(err) => return Err(err.into()),
},
Err(_) => return Err(SendRequestError::Connector(
ClientConnectorError::Disconnected))
Err(_) => {
return Err(SendRequestError::Connector(
ClientConnectorError::Disconnected,
))
}
},
State::Connection(conn) => {
let mut writer = HttpClientWriter::new(SharedBytes::default());
@ -169,12 +173,13 @@ impl Future for SendRequest {
_ => IoBody::Done,
};
let timeout = self.timeout.take().unwrap_or_else(||
Timeout::new(
Duration::from_secs(5), Arbiter::handle()).unwrap());
let timeout = self.timeout.take().unwrap_or_else(|| {
Timeout::new(Duration::from_secs(5), Arbiter::handle()).unwrap()
});
let pl = Box::new(Pipeline {
body, writer,
body,
writer,
conn: Some(conn),
parser: Some(HttpResponseParser::default()),
parser_buf: BytesMut::new(),
@ -186,22 +191,22 @@ impl Future for SendRequest {
timeout: Some(timeout),
});
self.state = State::Send(pl);
},
}
State::Send(mut pl) => {
pl.poll_write()
.map_err(|e| io::Error::new(
io::ErrorKind::Other, format!("{}", e).as_str()))?;
pl.poll_write().map_err(|e| {
io::Error::new(io::ErrorKind::Other, format!("{}", e).as_str())
})?;
match pl.parse() {
Ok(Async::Ready(mut resp)) => {
resp.set_pipeline(pl);
return Ok(Async::Ready(resp))
},
return Ok(Async::Ready(resp));
}
Ok(Async::NotReady) => {
self.state = State::Send(pl);
return Ok(Async::NotReady)
},
Err(err) => return Err(SendRequestError::ParseError(err))
return Ok(Async::NotReady);
}
Err(err) => return Err(SendRequestError::ParseError(err)),
}
}
State::None => unreachable!(),
@ -210,7 +215,6 @@ impl Future for SendRequest {
}
}
pub(crate) struct Pipeline {
body: IoBody,
conn: Option<Connection>,
@ -254,7 +258,6 @@ impl RunningState {
}
impl Pipeline {
fn release_conn(&mut self) {
if let Some(conn) = self.conn.take() {
conn.release()
@ -264,15 +267,22 @@ impl Pipeline {
#[inline]
fn parse(&mut self) -> Poll<ClientResponse, HttpResponseParserError> {
if let Some(ref mut conn) = self.conn {
match self.parser.as_mut().unwrap().parse(conn, &mut self.parser_buf) {
match self.parser
.as_mut()
.unwrap()
.parse(conn, &mut self.parser_buf)
{
Ok(Async::Ready(resp)) => {
// check content-encoding
if self.should_decompress {
if let Some(enc) = resp.headers().get(CONTENT_ENCODING) {
if let Ok(enc) = enc.to_str() {
match ContentEncoding::from(enc) {
ContentEncoding::Auto | ContentEncoding::Identity => (),
enc => self.decompress = Some(PayloadStream::new(enc)),
ContentEncoding::Auto
| ContentEncoding::Identity => (),
enc => {
self.decompress = Some(PayloadStream::new(enc))
}
}
}
}
@ -290,9 +300,10 @@ impl Pipeline {
#[inline]
pub fn poll(&mut self) -> Poll<Option<Bytes>, PayloadError> {
if self.conn.is_none() {
return Ok(Async::Ready(None))
return Ok(Async::Ready(None));
}
let conn: &mut Connection = unsafe{ mem::transmute(self.conn.as_mut().unwrap())};
let conn: &mut Connection =
unsafe { mem::transmute(self.conn.as_mut().unwrap()) };
let mut need_run = false;
@ -302,15 +313,18 @@ impl Pipeline {
{
Async::NotReady => need_run = true,
Async::Ready(_) => {
let _ = self.poll_timeout()
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e)))?;
let _ = self.poll_timeout().map_err(|e| {
io::Error::new(io::ErrorKind::Other, format!("{}", e))
})?;
}
}
// need read?
if self.parser.is_some() {
loop {
match self.parser.as_mut().unwrap()
match self.parser
.as_mut()
.unwrap()
.parse_payload(conn, &mut self.parser_buf)?
{
Async::Ready(Some(b)) => {
@ -318,17 +332,20 @@ impl Pipeline {
match decompress.feed_data(b) {
Ok(Some(b)) => return Ok(Async::Ready(Some(b))),
Ok(None) => return Ok(Async::NotReady),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock =>
continue,
Err(ref err)
if err.kind() == io::ErrorKind::WouldBlock =>
{
continue
}
Err(err) => return Err(err.into()),
}
} else {
return Ok(Async::Ready(Some(b)))
return Ok(Async::Ready(Some(b)));
}
},
}
Async::Ready(None) => {
let _ = self.parser.take();
break
break;
}
Async::NotReady => return Ok(Async::NotReady),
}
@ -340,7 +357,7 @@ impl Pipeline {
let res = decompress.feed_eof();
if let Some(b) = res? {
self.release_conn();
return Ok(Async::Ready(Some(b)))
return Ok(Async::Ready(Some(b)));
}
}
@ -357,7 +374,7 @@ impl Pipeline {
match self.timeout.as_mut().unwrap().poll() {
Ok(Async::Ready(())) => Err(SendRequestError::Timeout),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(_) => unreachable!()
Err(_) => unreachable!(),
}
} else {
Ok(Async::NotReady)
@ -367,29 +384,27 @@ impl Pipeline {
#[inline]
fn poll_write(&mut self) -> Poll<(), Error> {
if self.write_state == RunningState::Done || self.conn.is_none() {
return Ok(Async::Ready(()))
return Ok(Async::Ready(()));
}
let mut done = false;
if self.drain.is_none() && self.write_state != RunningState::Paused {
'outter: loop {
let result = match mem::replace(&mut self.body, IoBody::Done) {
IoBody::Payload(mut body) => {
match body.poll()? {
Async::Ready(None) => {
self.writer.write_eof()?;
self.disconnected = true;
break
},
Async::Ready(Some(chunk)) => {
self.body = IoBody::Payload(body);
self.writer.write(chunk.into())?
}
Async::NotReady => {
done = true;
self.body = IoBody::Payload(body);
break
},
IoBody::Payload(mut body) => match body.poll()? {
Async::Ready(None) => {
self.writer.write_eof()?;
self.disconnected = true;
break;
}
Async::Ready(Some(chunk)) => {
self.body = IoBody::Payload(body);
self.writer.write(chunk.into())?
}
Async::NotReady => {
done = true;
self.body = IoBody::Payload(body);
break;
}
},
IoBody::Actor(mut ctx) => {
@ -400,7 +415,7 @@ impl Pipeline {
Async::Ready(Some(vec)) => {
if vec.is_empty() {
self.body = IoBody::Actor(ctx);
break
break;
}
let mut res = None;
for frame in vec {
@ -409,52 +424,53 @@ impl Pipeline {
// info.context = Some(ctx);
self.disconnected = true;
self.writer.write_eof()?;
break 'outter
},
Frame::Chunk(Some(chunk)) =>
res = Some(self.writer.write(chunk)?),
break 'outter;
}
Frame::Chunk(Some(chunk)) => {
res = Some(self.writer.write(chunk)?)
}
Frame::Drain(fut) => self.drain = Some(fut),
}
}
self.body = IoBody::Actor(ctx);
if self.drain.is_some() {
self.write_state.resume();
break
break;
}
res.unwrap()
},
}
Async::Ready(None) => {
done = true;
break
break;
}
Async::NotReady => {
done = true;
self.body = IoBody::Actor(ctx);
break
break;
}
}
},
}
IoBody::Done => {
self.disconnected = true;
done = true;
break
break;
}
};
match result {
WriterState::Pause => {
self.write_state.pause();
break
break;
}
WriterState::Done => {
self.write_state.resume()
},
WriterState::Done => self.write_state.resume(),
}
}
}
// flush io but only if we need to
match self.writer.poll_completed(self.conn.as_mut().unwrap(), false) {
match self.writer
.poll_completed(self.conn.as_mut().unwrap(), false)
{
Ok(Async::Ready(_)) => {
if self.disconnected {
self.write_state = RunningState::Done;
@ -472,7 +488,7 @@ impl Pipeline {
} else {
Ok(Async::NotReady)
}
},
}
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(err) => Err(err.into()),
}

View File

@ -1,26 +1,26 @@
use std::{fmt, mem};
use std::fmt::Write as FmtWrite;
use std::io::Write;
use std::time::Duration;
use std::{fmt, mem};
use actix::{Addr, Unsync};
use bytes::{BufMut, Bytes, BytesMut};
use cookie::{Cookie, CookieJar};
use bytes::{Bytes, BytesMut, BufMut};
use futures::Stream;
use serde_json;
use percent_encoding::{percent_encode, USERINFO_ENCODE_SET};
use serde::Serialize;
use serde_json;
use url::Url;
use percent_encoding::{USERINFO_ENCODE_SET, percent_encode};
use super::connector::{ClientConnector, Connection};
use super::pipeline::SendRequest;
use body::Body;
use error::Error;
use header::{ContentEncoding, Header, IntoHeaderValue};
use http::header::{self, HeaderName, HeaderValue};
use http::{uri, Error as HttpError, HeaderMap, HttpTryFrom, Method, Uri, Version};
use httpmessage::HttpMessage;
use httprequest::HttpRequest;
use http::{uri, HeaderMap, Method, Version, Uri, HttpTryFrom, Error as HttpError};
use http::header::{self, HeaderName, HeaderValue};
use super::pipeline::SendRequest;
use super::connector::{Connection, ClientConnector};
/// An HTTP Client Request
///
@ -72,7 +72,6 @@ enum ConnectionType {
}
impl Default for ClientRequest {
fn default() -> ClientRequest {
ClientRequest {
uri: Uri::default(),
@ -92,7 +91,6 @@ impl Default for ClientRequest {
}
impl ClientRequest {
/// Create request builder for `GET` request
pub fn get<U: AsRef<str>>(uri: U) -> ClientRequestBuilder {
let mut builder = ClientRequest::build();
@ -130,14 +128,13 @@ impl ClientRequest {
}
impl ClientRequest {
/// Create client request builder
pub fn build() -> ClientRequestBuilder {
ClientRequestBuilder {
request: Some(ClientRequest::default()),
err: None,
cookies: None,
default_headers: true
default_headers: true,
}
}
@ -259,8 +256,11 @@ impl ClientRequest {
impl fmt::Debug for ClientRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let res = writeln!(f, "\nClientRequest {:?} {}:{}",
self.version, self.method, self.uri);
let res = writeln!(
f,
"\nClientRequest {:?} {}:{}",
self.version, self.method, self.uri
);
let _ = writeln!(f, " headers:");
for (key, val) in self.headers.iter() {
let _ = writeln!(f, " {:?}: {:?}", key, val);
@ -277,7 +277,7 @@ pub struct ClientRequestBuilder {
request: Option<ClientRequest>,
err: Option<HttpError>,
cookies: Option<CookieJar>,
default_headers: bool
default_headers: bool,
}
impl ClientRequestBuilder {
@ -300,8 +300,8 @@ impl ClientRequestBuilder {
if let Some(parts) = parts(&mut self.request, &self.err) {
parts.uri = uri;
}
},
Err(e) => self.err = Some(e.into(),),
}
Err(e) => self.err = Some(e.into()),
}
self
}
@ -318,8 +318,8 @@ impl ClientRequestBuilder {
/// Set HTTP method of this request.
#[inline]
pub fn get_method(&mut self) -> &Method {
let parts = parts(&mut self.request, &self.err)
.expect("cannot reuse request builder");
let parts =
parts(&mut self.request, &self.err).expect("cannot reuse request builder");
&parts.method
}
@ -351,11 +351,12 @@ impl ClientRequestBuilder {
/// }
/// ```
#[doc(hidden)]
pub fn set<H: Header>(&mut self, hdr: H) -> &mut Self
{
pub fn set<H: Header>(&mut self, hdr: H) -> &mut Self {
if let Some(parts) = parts(&mut self.request, &self.err) {
match hdr.try_into() {
Ok(value) => { parts.headers.insert(H::name(), value); }
Ok(value) => {
parts.headers.insert(H::name(), value);
}
Err(e) => self.err = Some(e.into()),
}
}
@ -382,15 +383,17 @@ impl ClientRequestBuilder {
/// }
/// ```
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
where HeaderName: HttpTryFrom<K>, V: IntoHeaderValue
where
HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue,
{
if let Some(parts) = parts(&mut self.request, &self.err) {
match HeaderName::try_from(key) {
Ok(key) => {
match value.try_into() {
Ok(value) => { parts.headers.append(key, value); }
Err(e) => self.err = Some(e.into()),
Ok(key) => match value.try_into() {
Ok(value) => {
parts.headers.append(key, value);
}
Err(e) => self.err = Some(e.into()),
},
Err(e) => self.err = Some(e.into()),
};
@ -400,15 +403,17 @@ impl ClientRequestBuilder {
/// Set a header.
pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
where HeaderName: HttpTryFrom<K>, V: IntoHeaderValue
where
HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue,
{
if let Some(parts) = parts(&mut self.request, &self.err) {
match HeaderName::try_from(key) {
Ok(key) => {
match value.try_into() {
Ok(value) => { parts.headers.insert(key, value); }
Err(e) => self.err = Some(e.into()),
Ok(key) => match value.try_into() {
Ok(value) => {
parts.headers.insert(key, value);
}
Err(e) => self.err = Some(e.into()),
},
Err(e) => self.err = Some(e.into()),
};
@ -448,11 +453,14 @@ impl ClientRequestBuilder {
/// Set request's content type
#[inline]
pub fn content_type<V>(&mut self, value: V) -> &mut Self
where HeaderValue: HttpTryFrom<V>
where
HeaderValue: HttpTryFrom<V>,
{
if let Some(parts) = parts(&mut self.request, &self.err) {
match HeaderValue::try_from(value) {
Ok(value) => { parts.headers.insert(header::CONTENT_TYPE, value); },
Ok(value) => {
parts.headers.insert(header::CONTENT_TYPE, value);
}
Err(e) => self.err = Some(e.into()),
};
}
@ -491,7 +499,10 @@ impl ClientRequestBuilder {
jar.add(cookie.into_owned());
self.cookies = Some(jar)
} else {
self.cookies.as_mut().unwrap().add(cookie.into_owned());
self.cookies
.as_mut()
.unwrap()
.add(cookie.into_owned());
}
self
}
@ -551,7 +562,8 @@ impl ClientRequestBuilder {
/// This method calls provided closure with builder reference if
/// value is `true`.
pub fn if_true<F>(&mut self, value: bool, f: F) -> &mut Self
where F: FnOnce(&mut ClientRequestBuilder)
where
F: FnOnce(&mut ClientRequestBuilder),
{
if value {
f(self);
@ -562,7 +574,8 @@ impl ClientRequestBuilder {
/// This method calls provided closure with builder reference if
/// value is `Some`.
pub fn if_some<T, F>(&mut self, value: Option<T>, f: F) -> &mut Self
where F: FnOnce(T, &mut ClientRequestBuilder)
where
F: FnOnce(T, &mut ClientRequestBuilder),
{
if let Some(val) = value {
f(val, self);
@ -575,18 +588,20 @@ impl ClientRequestBuilder {
/// `ClientRequestBuilder` can not be used after this call.
pub fn body<B: Into<Body>>(&mut self, body: B) -> Result<ClientRequest, Error> {
if let Some(e) = self.err.take() {
return Err(e.into())
return Err(e.into());
}
if self.default_headers {
// enable br only for https
let https =
if let Some(parts) = parts(&mut self.request, &self.err) {
parts.uri.scheme_part()
.map(|s| s == &uri::Scheme::HTTPS).unwrap_or(true)
} else {
true
};
let https = if let Some(parts) = parts(&mut self.request, &self.err) {
parts
.uri
.scheme_part()
.map(|s| s == &uri::Scheme::HTTPS)
.unwrap_or(true)
} else {
true
};
if https {
self.header(header::ACCEPT_ENCODING, "br, gzip, deflate");
@ -595,7 +610,9 @@ impl ClientRequestBuilder {
}
}
let mut request = self.request.take().expect("cannot reuse request builder");
let mut request = self.request
.take()
.expect("cannot reuse request builder");
// set cookies
if let Some(ref mut jar) = self.cookies {
@ -606,7 +623,9 @@ impl ClientRequestBuilder {
let _ = write!(&mut cookie, "; {}={}", name, value);
}
request.headers.insert(
header::COOKIE, HeaderValue::from_str(&cookie.as_str()[2..]).unwrap());
header::COOKIE,
HeaderValue::from_str(&cookie.as_str()[2..]).unwrap(),
);
}
request.body = body.into();
Ok(request)
@ -634,10 +653,13 @@ impl ClientRequestBuilder {
///
/// `ClientRequestBuilder` can not be used after this call.
pub fn streaming<S, E>(&mut self, stream: S) -> Result<ClientRequest, Error>
where S: Stream<Item=Bytes, Error=E> + 'static,
E: Into<Error>,
where
S: Stream<Item = Bytes, Error = E> + 'static,
E: Into<Error>,
{
self.body(Body::Streaming(Box::new(stream.map_err(|e| e.into()))))
self.body(Body::Streaming(Box::new(
stream.map_err(|e| e.into()),
)))
}
/// Set an empty body and generate `ClientRequest`
@ -653,17 +675,17 @@ impl ClientRequestBuilder {
request: self.request.take(),
err: self.err.take(),
cookies: self.cookies.take(),
default_headers: self.default_headers
default_headers: self.default_headers,
}
}
}
#[inline]
fn parts<'a>(parts: &'a mut Option<ClientRequest>, err: &Option<HttpError>)
-> Option<&'a mut ClientRequest>
{
fn parts<'a>(
parts: &'a mut Option<ClientRequest>, err: &Option<HttpError>
) -> Option<&'a mut ClientRequest> {
if err.is_some() {
return None
return None;
}
parts.as_mut()
}
@ -671,8 +693,11 @@ fn parts<'a>(parts: &'a mut Option<ClientRequest>, err: &Option<HttpError>)
impl fmt::Debug for ClientRequestBuilder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref parts) = self.request {
let res = writeln!(f, "\nClientRequestBuilder {:?} {}:{}",
parts.version, parts.method, parts.uri);
let res = writeln!(
f,
"\nClientRequestBuilder {:?} {}:{}",
parts.version, parts.method, parts.uri
);
let _ = writeln!(f, " headers:");
for (key, val) in parts.headers.iter() {
let _ = writeln!(f, " {:?}: {:?}", key, val);

View File

@ -1,19 +1,18 @@
use std::{fmt, str};
use std::rc::Rc;
use std::cell::UnsafeCell;
use std::rc::Rc;
use std::{fmt, str};
use bytes::Bytes;
use cookie::Cookie;
use futures::{Async, Poll, Stream};
use http::{HeaderMap, StatusCode, Version};
use http::header::{self, HeaderValue};
use http::{HeaderMap, StatusCode, Version};
use httpmessage::HttpMessage;
use error::{CookieParseError, PayloadError};
use httpmessage::HttpMessage;
use super::pipeline::Pipeline;
pub(crate) struct ClientMessage {
pub status: StatusCode,
pub version: Version,
@ -22,7 +21,6 @@ pub(crate) struct ClientMessage {
}
impl Default for ClientMessage {
fn default() -> ClientMessage {
ClientMessage {
status: StatusCode::OK,
@ -45,7 +43,6 @@ impl HttpMessage for ClientResponse {
}
impl ClientResponse {
pub(crate) fn new(msg: ClientMessage) -> ClientResponse {
ClientResponse(Rc::new(UnsafeCell::new(msg)), None)
}
@ -56,13 +53,13 @@ impl ClientResponse {
#[inline]
fn as_ref(&self) -> &ClientMessage {
unsafe{ &*self.0.get() }
unsafe { &*self.0.get() }
}
#[inline]
#[cfg_attr(feature = "cargo-clippy", allow(mut_from_ref))]
fn as_mut(&self) -> &mut ClientMessage {
unsafe{ &mut *self.0.get() }
unsafe { &mut *self.0.get() }
}
/// Get the HTTP version of this response.
@ -96,7 +93,7 @@ impl ClientResponse {
if let Ok(cookies) = self.cookies() {
for cookie in cookies {
if cookie.name() == name {
return Some(cookie)
return Some(cookie);
}
}
}
@ -107,7 +104,11 @@ impl ClientResponse {
impl fmt::Debug for ClientResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let res = writeln!(
f, "\nClientResponse {:?} {}", self.version(), self.status());
f,
"\nClientResponse {:?} {}",
self.version(),
self.status()
);
let _ = writeln!(f, " headers:");
for (key, val) in self.headers().iter() {
let _ = writeln!(f, " {:?}: {:?}", key, val);
@ -138,9 +139,13 @@ mod tests {
fn test_debug() {
let resp = ClientResponse::new(ClientMessage::default());
resp.as_mut().headers.insert(
header::COOKIE, HeaderValue::from_static("cookie1=value1"));
header::COOKIE,
HeaderValue::from_static("cookie1=value1"),
);
resp.as_mut().headers.insert(
header::COOKIE, HeaderValue::from_static("cookie2=value2"));
header::COOKIE,
HeaderValue::from_static("cookie2=value2"),
);
let dbg = format!("{:?}", resp);
assert!(dbg.contains("ClientResponse"));

View File

@ -1,30 +1,29 @@
#![cfg_attr(feature = "cargo-clippy", allow(redundant_field_names))]
use std::io::{self, Write};
use std::cell::RefCell;
use std::fmt::Write as FmtWrite;
use std::io::{self, Write};
use time::{self, Duration};
use bytes::{BytesMut, BufMut};
use futures::{Async, Poll};
use tokio_io::AsyncWrite;
use http::{Version, HttpTryFrom};
use http::header::{HeaderValue, DATE,
CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING};
use flate2::Compression;
use flate2::write::{GzEncoder, DeflateEncoder};
#[cfg(feature="brotli")]
#[cfg(feature = "brotli")]
use brotli2::write::BrotliEncoder;
use bytes::{BufMut, BytesMut};
use flate2::Compression;
use flate2::write::{DeflateEncoder, GzEncoder};
use futures::{Async, Poll};
use http::header::{HeaderValue, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, DATE,
TRANSFER_ENCODING};
use http::{HttpTryFrom, Version};
use time::{self, Duration};
use tokio_io::AsyncWrite;
use body::{Body, Binary};
use body::{Binary, Body};
use header::ContentEncoding;
use server::WriterState;
use server::shared::SharedBytes;
use server::encoding::{ContentEncoder, TransferEncoding};
use server::shared::SharedBytes;
use client::ClientRequest;
const AVERAGE_HEADER_SIZE: usize = 30;
bitflags! {
@ -46,7 +45,6 @@ pub(crate) struct HttpClientWriter {
}
impl HttpClientWriter {
pub fn new(buffer: SharedBytes) -> HttpClientWriter {
let encoder = ContentEncoder::Identity(TransferEncoding::eof(buffer.clone()));
HttpClientWriter {
@ -64,24 +62,26 @@ impl HttpClientWriter {
}
// pub fn keepalive(&self) -> bool {
// self.flags.contains(Flags::KEEPALIVE) && !self.flags.contains(Flags::UPGRADE)
// }
// self.flags.contains(Flags::KEEPALIVE) &&
// !self.flags.contains(Flags::UPGRADE) }
fn write_to_stream<T: AsyncWrite>(&mut self, stream: &mut T) -> io::Result<WriterState> {
fn write_to_stream<T: AsyncWrite>(
&mut self, stream: &mut T
) -> io::Result<WriterState> {
while !self.buffer.is_empty() {
match stream.write(self.buffer.as_ref()) {
Ok(0) => {
self.disconnected();
return Ok(WriterState::Done);
},
}
Ok(n) => {
let _ = self.buffer.split_to(n);
},
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
if self.buffer.len() > self.buffer_capacity {
return Ok(WriterState::Pause)
return Ok(WriterState::Pause);
} else {
return Ok(WriterState::Done)
return Ok(WriterState::Done);
}
}
Err(err) => return Err(err),
@ -92,7 +92,6 @@ impl HttpClientWriter {
}
impl HttpClientWriter {
pub fn start(&mut self, msg: &mut ClientRequest) -> io::Result<()> {
// prepare task
self.flags.insert(Flags::STARTED);
@ -105,10 +104,16 @@ impl HttpClientWriter {
// render message
{
// status line
writeln!(self.buffer, "{} {} {:?}\r",
msg.method(),
msg.uri().path_and_query().map(|u| u.as_str()).unwrap_or("/"),
msg.version())?;
writeln!(
self.buffer,
"{} {} {:?}\r",
msg.method(),
msg.uri()
.path_and_query()
.map(|u| u.as_str())
.unwrap_or("/"),
msg.version()
)?;
// write headers
let mut buffer = self.buffer.get_mut();
@ -173,15 +178,17 @@ impl HttpClientWriter {
if self.encoder.is_eof() {
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::Other,
"Last payload item, but eof is not reached"))
Err(io::Error::new(
io::ErrorKind::Other,
"Last payload item, but eof is not reached",
))
}
}
#[inline]
pub fn poll_completed<T: AsyncWrite>(&mut self, stream: &mut T, shutdown: bool)
-> Poll<(), io::Error>
{
pub fn poll_completed<T: AsyncWrite>(
&mut self, stream: &mut T, shutdown: bool
) -> Poll<(), io::Error> {
match self.write_to_stream(stream) {
Ok(WriterState::Done) => {
if shutdown {
@ -189,14 +196,13 @@ impl HttpClientWriter {
} else {
Ok(Async::Ready(()))
}
},
}
Ok(WriterState::Pause) => Ok(Async::NotReady),
Err(err) => Err(err)
Err(err) => Err(err),
}
}
}
fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder {
let version = req.version();
let mut body = req.replace_body(Body::Empty);
@ -206,21 +212,25 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
Body::Empty => {
req.headers_mut().remove(CONTENT_LENGTH);
TransferEncoding::length(0, buf)
},
}
Body::Binary(ref mut bytes) => {
if encoding.is_compression() {
let tmp = SharedBytes::default();
let transfer = TransferEncoding::eof(tmp.clone());
let mut enc = match encoding {
ContentEncoding::Deflate => ContentEncoder::Deflate(
DeflateEncoder::new(transfer, Compression::default())),
ContentEncoding::Gzip => ContentEncoder::Gzip(
GzEncoder::new(transfer, Compression::default())),
#[cfg(feature="brotli")]
ContentEncoding::Br => ContentEncoder::Br(
BrotliEncoder::new(transfer, 5)),
DeflateEncoder::new(transfer, Compression::default()),
),
ContentEncoding::Gzip => ContentEncoder::Gzip(GzEncoder::new(
transfer,
Compression::default(),
)),
#[cfg(feature = "brotli")]
ContentEncoding::Br => {
ContentEncoder::Br(BrotliEncoder::new(transfer, 5))
}
ContentEncoding::Identity => ContentEncoder::Identity(transfer),
ContentEncoding::Auto => unreachable!()
ContentEncoding::Auto => unreachable!(),
};
// TODO return error!
let _ = enc.write(bytes.clone());
@ -228,21 +238,26 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
*bytes = Binary::from(tmp.take());
req.headers_mut().insert(
CONTENT_ENCODING, HeaderValue::from_static(encoding.as_str()));
CONTENT_ENCODING,
HeaderValue::from_static(encoding.as_str()),
);
encoding = ContentEncoding::Identity;
}
let mut b = BytesMut::new();
let _ = write!(b, "{}", bytes.len());
req.headers_mut().insert(
CONTENT_LENGTH, HeaderValue::try_from(b.freeze()).unwrap());
CONTENT_LENGTH,
HeaderValue::try_from(b.freeze()).unwrap(),
);
TransferEncoding::eof(buf)
},
}
Body::Streaming(_) | Body::Actor(_) => {
if req.upgrade() {
if version == Version::HTTP_2 {
error!("Connection upgrade is forbidden for HTTP/2");
} else {
req.headers_mut().insert(CONNECTION, HeaderValue::from_static("upgrade"));
req.headers_mut()
.insert(CONNECTION, HeaderValue::from_static("upgrade"));
}
if encoding != ContentEncoding::Identity {
encoding = ContentEncoding::Identity;
@ -257,24 +272,31 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
if encoding.is_compression() {
req.headers_mut().insert(
CONTENT_ENCODING, HeaderValue::from_static(encoding.as_str()));
CONTENT_ENCODING,
HeaderValue::from_static(encoding.as_str()),
);
}
req.replace_body(body);
match encoding {
ContentEncoding::Deflate => ContentEncoder::Deflate(
DeflateEncoder::new(transfer, Compression::default())),
ContentEncoding::Gzip => ContentEncoder::Gzip(
GzEncoder::new(transfer, Compression::default())),
#[cfg(feature="brotli")]
ContentEncoding::Br => ContentEncoder::Br(
BrotliEncoder::new(transfer, 5)),
ContentEncoding::Identity | ContentEncoding::Auto => ContentEncoder::Identity(transfer),
ContentEncoding::Deflate => ContentEncoder::Deflate(DeflateEncoder::new(
transfer,
Compression::default(),
)),
ContentEncoding::Gzip => {
ContentEncoder::Gzip(GzEncoder::new(transfer, Compression::default()))
}
#[cfg(feature = "brotli")]
ContentEncoding::Br => ContentEncoder::Br(BrotliEncoder::new(transfer, 5)),
ContentEncoding::Identity | ContentEncoding::Auto => {
ContentEncoder::Identity(transfer)
}
}
}
fn streaming_encoding(buf: SharedBytes, version: Version, req: &mut ClientRequest)
-> TransferEncoding {
fn streaming_encoding(
buf: SharedBytes, version: Version, req: &mut ClientRequest
) -> TransferEncoding {
if req.chunked() {
// Enable transfer encoding
req.headers_mut().remove(CONTENT_LENGTH);
@ -282,29 +304,28 @@ fn streaming_encoding(buf: SharedBytes, version: Version, req: &mut ClientReques
req.headers_mut().remove(TRANSFER_ENCODING);
TransferEncoding::eof(buf)
} else {
req.headers_mut().insert(
TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
req.headers_mut()
.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
TransferEncoding::chunked(buf)
}
} else {
// if Content-Length is specified, then use it as length hint
let (len, chunked) =
if let Some(len) = req.headers().get(CONTENT_LENGTH) {
// Content-Length
if let Ok(s) = len.to_str() {
if let Ok(len) = s.parse::<u64>() {
(Some(len), false)
} else {
error!("illegal Content-Length: {:?}", len);
(None, false)
}
let (len, chunked) = if let Some(len) = req.headers().get(CONTENT_LENGTH) {
// Content-Length
if let Ok(s) = len.to_str() {
if let Ok(len) = s.parse::<u64>() {
(Some(len), false)
} else {
error!("illegal Content-Length: {:?}", len);
(None, false)
}
} else {
(None, true)
};
error!("illegal Content-Length: {:?}", len);
(None, false)
}
} else {
(None, true)
};
if !chunked {
if let Some(len) = len {
@ -316,10 +337,10 @@ fn streaming_encoding(buf: SharedBytes, version: Version, req: &mut ClientReques
// Enable transfer encoding
match version {
Version::HTTP_11 => {
req.headers_mut().insert(
TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
req.headers_mut()
.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
TransferEncoding::chunked(buf)
},
}
_ => {
req.headers_mut().remove(TRANSFER_ENCODING);
TransferEncoding::eof(buf)
@ -329,7 +350,6 @@ fn streaming_encoding(buf: SharedBytes, version: Version, req: &mut ClientReques
}
}
// "Sun, 06 Nov 1994 08:49:37 GMT".len()
pub const DATE_VALUE_LENGTH: usize = 29;