2018-02-19 12:11:11 +01:00
|
|
|
use std::{io, mem};
|
|
|
|
use bytes::{Bytes, BytesMut};
|
|
|
|
use futures::{Async, Future, Poll};
|
2018-02-20 07:48:27 +01:00
|
|
|
use futures::unsync::oneshot;
|
2018-02-19 12:11:11 +01:00
|
|
|
|
|
|
|
use actix::prelude::*;
|
|
|
|
|
2018-02-20 07:48:27 +01:00
|
|
|
use error::Error;
|
|
|
|
use body::{Body, BodyStream};
|
|
|
|
use context::{Frame, ActorHttpContext};
|
2018-02-19 12:11:11 +01:00
|
|
|
use error::PayloadError;
|
2018-02-20 07:48:27 +01:00
|
|
|
use server::WriterState;
|
2018-02-19 12:11:11 +01:00
|
|
|
use server::shared::SharedBytes;
|
|
|
|
use super::{ClientRequest, ClientResponse};
|
|
|
|
use super::{Connect, Connection, ClientConnector, ClientConnectorError};
|
|
|
|
use super::HttpClientWriter;
|
|
|
|
use super::{HttpResponseParser, HttpResponseParserError};
|
|
|
|
|
2018-02-19 22:18:18 +01:00
|
|
|
/// A set of errors that can occur during sending request and reading response
|
|
|
|
#[derive(Fail, Debug)]
|
2018-02-19 12:11:11 +01:00
|
|
|
pub enum SendRequestError {
|
2018-02-19 22:18:18 +01:00
|
|
|
/// Failed to connect to host
|
|
|
|
#[fail(display="Failed to connect to host: {}", _0)]
|
|
|
|
Connector(#[cause] ClientConnectorError),
|
|
|
|
/// Error parsing response
|
|
|
|
#[fail(display="{}", _0)]
|
|
|
|
ParseError(#[cause] HttpResponseParserError),
|
|
|
|
/// Error reading response payload
|
|
|
|
#[fail(display="Error reading response payload: {}", _0)]
|
|
|
|
Io(#[cause] io::Error),
|
2018-02-19 12:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<io::Error> for SendRequestError {
|
|
|
|
fn from(err: io::Error) -> SendRequestError {
|
|
|
|
SendRequestError::Io(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum State {
|
|
|
|
New,
|
|
|
|
Connect(actix::dev::Request<Unsync, ClientConnector, Connect>),
|
|
|
|
Send(Box<Pipeline>),
|
|
|
|
None,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `SendRequest` is a `Future` which represents asynchronous request sending process.
|
2018-02-20 07:48:27 +01:00
|
|
|
#[must_use = "SendRequest does nothing unless polled"]
|
2018-02-19 12:11:11 +01:00
|
|
|
pub struct SendRequest {
|
|
|
|
req: ClientRequest,
|
|
|
|
state: State,
|
2018-02-19 22:41:21 +01:00
|
|
|
conn: Addr<Unsync, ClientConnector>,
|
2018-02-19 12:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SendRequest {
|
|
|
|
pub(crate) fn new(req: ClientRequest) -> SendRequest {
|
2018-02-19 22:41:21 +01:00
|
|
|
SendRequest::with_connector(req, ClientConnector::from_registry())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn with_connector(req: ClientRequest, conn: Addr<Unsync, ClientConnector>)
|
|
|
|
-> SendRequest
|
|
|
|
{
|
2018-02-19 12:11:11 +01:00
|
|
|
SendRequest{
|
|
|
|
req: req,
|
|
|
|
state: State::New,
|
2018-02-19 22:41:21 +01:00
|
|
|
conn: conn}
|
2018-02-19 12:11:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Future for SendRequest {
|
|
|
|
type Item = ClientResponse;
|
|
|
|
type Error = SendRequestError;
|
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
|
|
|
loop {
|
|
|
|
let state = mem::replace(&mut self.state, State::None);
|
|
|
|
|
|
|
|
match state {
|
|
|
|
State::New =>
|
2018-02-19 22:41:21 +01:00
|
|
|
self.state = State::Connect(self.conn.send(Connect(self.req.uri().clone()))),
|
2018-02-19 12:11:11 +01:00
|
|
|
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) => {
|
2018-02-20 07:48:27 +01:00
|
|
|
let mut writer = HttpClientWriter::new(SharedBytes::default());
|
|
|
|
writer.start(&mut self.req)?;
|
|
|
|
|
|
|
|
let body = match self.req.replace_body(Body::Empty) {
|
|
|
|
Body::Streaming(stream) => IoBody::Payload(stream),
|
|
|
|
Body::Actor(ctx) => IoBody::Actor(ctx),
|
|
|
|
_ => IoBody::Done,
|
|
|
|
};
|
|
|
|
|
2018-02-19 12:11:11 +01:00
|
|
|
let mut pl = Box::new(Pipeline {
|
2018-02-20 07:48:27 +01:00
|
|
|
body: body,
|
2018-02-19 12:11:11 +01:00
|
|
|
conn: stream,
|
2018-02-20 07:48:27 +01:00
|
|
|
writer: writer,
|
2018-02-19 12:11:11 +01:00
|
|
|
parser: HttpResponseParser::default(),
|
|
|
|
parser_buf: BytesMut::new(),
|
2018-02-20 07:48:27 +01:00
|
|
|
disconnected: false,
|
|
|
|
running: RunningState::Running,
|
|
|
|
drain: None,
|
2018-02-19 12:11:11 +01:00
|
|
|
});
|
|
|
|
self.state = State::Send(pl);
|
|
|
|
},
|
|
|
|
Err(err) => return Err(SendRequestError::Connector(err)),
|
|
|
|
},
|
|
|
|
Err(_) =>
|
|
|
|
return Err(SendRequestError::Connector(ClientConnectorError::Disconnected))
|
|
|
|
},
|
|
|
|
State::Send(mut pl) => {
|
2018-02-20 07:48:27 +01:00
|
|
|
pl.poll_write()
|
|
|
|
.map_err(|e| io::Error::new(
|
|
|
|
io::ErrorKind::Other, format!("{}", e).as_str()))?;
|
|
|
|
|
2018-02-19 12:11:11 +01:00
|
|
|
match pl.parse() {
|
|
|
|
Ok(Async::Ready(mut resp)) => {
|
|
|
|
resp.set_pipeline(pl);
|
|
|
|
return Ok(Async::Ready(resp))
|
|
|
|
},
|
|
|
|
Ok(Async::NotReady) => {
|
|
|
|
self.state = State::Send(pl);
|
|
|
|
return Ok(Async::NotReady)
|
|
|
|
},
|
|
|
|
Err(err) => return Err(SendRequestError::ParseError(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
State::None => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) struct Pipeline {
|
2018-02-20 07:48:27 +01:00
|
|
|
body: IoBody,
|
2018-02-19 12:11:11 +01:00
|
|
|
conn: Connection,
|
|
|
|
writer: HttpClientWriter,
|
|
|
|
parser: HttpResponseParser,
|
|
|
|
parser_buf: BytesMut,
|
2018-02-20 07:48:27 +01:00
|
|
|
disconnected: bool,
|
|
|
|
running: RunningState,
|
|
|
|
drain: Option<oneshot::Sender<()>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum IoBody {
|
|
|
|
Payload(BodyStream),
|
|
|
|
Actor(Box<ActorHttpContext>),
|
|
|
|
Done,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq)]
|
|
|
|
enum RunningState {
|
|
|
|
Running,
|
|
|
|
Paused,
|
|
|
|
Done,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RunningState {
|
|
|
|
#[inline]
|
|
|
|
fn pause(&mut self) {
|
|
|
|
if *self != RunningState::Done {
|
|
|
|
*self = RunningState::Paused
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[inline]
|
|
|
|
fn resume(&mut self) {
|
|
|
|
if *self != RunningState::Done {
|
|
|
|
*self = RunningState::Running
|
|
|
|
}
|
|
|
|
}
|
2018-02-19 12:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Pipeline {
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn parse(&mut self) -> Poll<ClientResponse, HttpResponseParserError> {
|
|
|
|
self.parser.parse(&mut self.conn, &mut self.parser_buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn poll(&mut self) -> Poll<Option<Bytes>, PayloadError> {
|
2018-02-20 07:48:27 +01:00
|
|
|
self.poll_write()
|
|
|
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e).as_str()))?;
|
|
|
|
Ok(self.parser.parse_payload(&mut self.conn, &mut self.parser_buf)?)
|
2018-02-19 12:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2018-02-20 07:48:27 +01:00
|
|
|
pub fn poll_write(&mut self) -> Poll<(), Error> {
|
|
|
|
if self.running == RunningState::Done {
|
|
|
|
return Ok(Async::Ready(()))
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut done = false;
|
|
|
|
|
|
|
|
if self.drain.is_none() && self.running != 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::Actor(mut ctx) => {
|
|
|
|
if self.disconnected {
|
|
|
|
ctx.disconnected();
|
|
|
|
}
|
|
|
|
match ctx.poll()? {
|
|
|
|
Async::Ready(Some(vec)) => {
|
|
|
|
if vec.is_empty() {
|
|
|
|
self.body = IoBody::Actor(ctx);
|
|
|
|
break
|
|
|
|
}
|
|
|
|
let mut res = None;
|
|
|
|
for frame in vec {
|
|
|
|
match frame {
|
|
|
|
Frame::Chunk(None) => {
|
|
|
|
// info.context = Some(ctx);
|
|
|
|
self.writer.write_eof()?;
|
|
|
|
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.running.resume();
|
|
|
|
break
|
|
|
|
}
|
|
|
|
res.unwrap()
|
|
|
|
},
|
|
|
|
Async::Ready(None) => {
|
|
|
|
done = true;
|
|
|
|
break
|
|
|
|
}
|
|
|
|
Async::NotReady => {
|
|
|
|
done = true;
|
|
|
|
self.body = IoBody::Actor(ctx);
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
IoBody::Done => {
|
|
|
|
done = true;
|
|
|
|
break
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
match result {
|
|
|
|
WriterState::Pause => {
|
|
|
|
self.running.pause();
|
|
|
|
break
|
|
|
|
}
|
|
|
|
WriterState::Done => {
|
|
|
|
self.running.resume()
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// flush io but only if we need to
|
|
|
|
match self.writer.poll_completed(&mut self.conn, false) {
|
|
|
|
Ok(Async::Ready(_)) => {
|
|
|
|
self.running.resume();
|
|
|
|
|
|
|
|
// resolve drain futures
|
|
|
|
if let Some(tx) = self.drain.take() {
|
|
|
|
let _ = tx.send(());
|
|
|
|
}
|
|
|
|
// restart io processing
|
|
|
|
if !done {
|
|
|
|
self.poll_write()
|
|
|
|
} else {
|
|
|
|
Ok(Async::NotReady)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Ok(Async::NotReady) => Ok(Async::NotReady),
|
|
|
|
Err(err) => Err(err.into()),
|
|
|
|
}
|
2018-02-19 12:11:11 +01:00
|
|
|
}
|
|
|
|
}
|