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:
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()),
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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"));
|
||||
|
@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user