1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-12-19 14:49:01 +00:00

refactor TransferEncoding; allow to use client api with threaded tokio runtime

This commit is contained in:
Nikolay Kim 2018-05-29 16:32:39 -07:00
parent 844be8d9dd
commit a64205e502
18 changed files with 344 additions and 290 deletions

View file

@ -1,6 +1,5 @@
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::Stream; use futures::Stream;
use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, mem}; use std::{fmt, mem};
@ -35,10 +34,8 @@ pub enum Binary {
/// Static slice /// Static slice
Slice(&'static [u8]), Slice(&'static [u8]),
/// Shared string body /// Shared string body
SharedString(Rc<String>),
/// Shared string body
#[doc(hidden)] #[doc(hidden)]
ArcSharedString(Arc<String>), SharedString(Arc<String>),
/// Shared vec body /// Shared vec body
SharedVec(Arc<Vec<u8>>), SharedVec(Arc<Vec<u8>>),
} }
@ -140,7 +137,6 @@ impl Binary {
Binary::Bytes(ref bytes) => bytes.len(), Binary::Bytes(ref bytes) => bytes.len(),
Binary::Slice(slice) => slice.len(), Binary::Slice(slice) => slice.len(),
Binary::SharedString(ref s) => s.len(), Binary::SharedString(ref s) => s.len(),
Binary::ArcSharedString(ref s) => s.len(),
Binary::SharedVec(ref s) => s.len(), Binary::SharedVec(ref s) => s.len(),
} }
} }
@ -162,7 +158,6 @@ impl Clone for Binary {
Binary::Bytes(ref bytes) => Binary::Bytes(bytes.clone()), Binary::Bytes(ref bytes) => Binary::Bytes(bytes.clone()),
Binary::Slice(slice) => Binary::Bytes(Bytes::from(slice)), Binary::Slice(slice) => Binary::Bytes(Bytes::from(slice)),
Binary::SharedString(ref s) => Binary::SharedString(s.clone()), Binary::SharedString(ref s) => Binary::SharedString(s.clone()),
Binary::ArcSharedString(ref s) => Binary::ArcSharedString(s.clone()),
Binary::SharedVec(ref s) => Binary::SharedVec(s.clone()), Binary::SharedVec(ref s) => Binary::SharedVec(s.clone()),
} }
} }
@ -174,7 +169,6 @@ impl Into<Bytes> for Binary {
Binary::Bytes(bytes) => bytes, Binary::Bytes(bytes) => bytes,
Binary::Slice(slice) => Bytes::from(slice), Binary::Slice(slice) => Bytes::from(slice),
Binary::SharedString(s) => Bytes::from(s.as_str()), Binary::SharedString(s) => Bytes::from(s.as_str()),
Binary::ArcSharedString(s) => Bytes::from(s.as_str()),
Binary::SharedVec(s) => Bytes::from(AsRef::<[u8]>::as_ref(s.as_ref())), Binary::SharedVec(s) => Bytes::from(AsRef::<[u8]>::as_ref(s.as_ref())),
} }
} }
@ -222,27 +216,15 @@ impl From<BytesMut> for Binary {
} }
} }
impl From<Rc<String>> for Binary {
fn from(body: Rc<String>) -> Binary {
Binary::SharedString(body)
}
}
impl<'a> From<&'a Rc<String>> for Binary {
fn from(body: &'a Rc<String>) -> Binary {
Binary::SharedString(Rc::clone(body))
}
}
impl From<Arc<String>> for Binary { impl From<Arc<String>> for Binary {
fn from(body: Arc<String>) -> Binary { fn from(body: Arc<String>) -> Binary {
Binary::ArcSharedString(body) Binary::SharedString(body)
} }
} }
impl<'a> From<&'a Arc<String>> for Binary { impl<'a> From<&'a Arc<String>> for Binary {
fn from(body: &'a Arc<String>) -> Binary { fn from(body: &'a Arc<String>) -> Binary {
Binary::ArcSharedString(Arc::clone(body)) Binary::SharedString(Arc::clone(body))
} }
} }
@ -265,7 +247,6 @@ impl AsRef<[u8]> for Binary {
Binary::Bytes(ref bytes) => bytes.as_ref(), Binary::Bytes(ref bytes) => bytes.as_ref(),
Binary::Slice(slice) => slice, Binary::Slice(slice) => slice,
Binary::SharedString(ref s) => s.as_bytes(), Binary::SharedString(ref s) => s.as_bytes(),
Binary::ArcSharedString(ref s) => s.as_bytes(),
Binary::SharedVec(ref s) => s.as_ref().as_ref(), Binary::SharedVec(ref s) => s.as_ref().as_ref(),
} }
} }
@ -324,22 +305,6 @@ mod tests {
assert_eq!(Binary::from(Bytes::from("test")).as_ref(), b"test"); assert_eq!(Binary::from(Bytes::from("test")).as_ref(), b"test");
} }
#[test]
fn test_ref_string() {
let b = Rc::new("test".to_owned());
assert_eq!(Binary::from(&b).len(), 4);
assert_eq!(Binary::from(&b).as_ref(), b"test");
}
#[test]
fn test_rc_string() {
let b = Rc::new("test".to_owned());
assert_eq!(Binary::from(b.clone()).len(), 4);
assert_eq!(Binary::from(b.clone()).as_ref(), b"test");
assert_eq!(Binary::from(&b).len(), 4);
assert_eq!(Binary::from(&b).as_ref(), b"test");
}
#[test] #[test]
fn test_arc_string() { fn test_arc_string() {
let b = Arc::new("test".to_owned()); let b = Arc::new("test".to_owned());

94
src/client/body.rs Normal file
View file

@ -0,0 +1,94 @@
use std::fmt;
use bytes::Bytes;
use futures::Stream;
use body::Binary;
use context::ActorHttpContext;
use error::Error;
/// Type represent streaming body
pub type ClientBodyStream = Box<Stream<Item = Bytes, Error = Error> + Send>;
/// Represents various types of http message body.
pub enum ClientBody {
/// Empty response. `Content-Length` header is set to `0`
Empty,
/// Specific response body.
Binary(Binary),
/// Unspecified streaming response. Developer is responsible for setting
/// right `Content-Length` or `Transfer-Encoding` headers.
Streaming(ClientBodyStream),
/// Special body type for actor response.
Actor(Box<ActorHttpContext + Send>),
}
impl ClientBody {
/// Does this body streaming.
#[inline]
pub fn is_streaming(&self) -> bool {
match *self {
ClientBody::Streaming(_) | ClientBody::Actor(_) => true,
_ => false,
}
}
/// Is this binary body.
#[inline]
pub fn is_binary(&self) -> bool {
match *self {
ClientBody::Binary(_) => true,
_ => false,
}
}
/// Is this binary empy.
#[inline]
pub fn is_empty(&self) -> bool {
match *self {
ClientBody::Empty => true,
_ => false,
}
}
/// Create body from slice (copy)
pub fn from_slice(s: &[u8]) -> ClientBody {
ClientBody::Binary(Binary::Bytes(Bytes::from(s)))
}
}
impl PartialEq for ClientBody {
fn eq(&self, other: &ClientBody) -> bool {
match *self {
ClientBody::Empty => match *other {
ClientBody::Empty => true,
_ => false,
},
ClientBody::Binary(ref b) => match *other {
ClientBody::Binary(ref b2) => b == b2,
_ => false,
},
ClientBody::Streaming(_) | ClientBody::Actor(_) => false,
}
}
}
impl fmt::Debug for ClientBody {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ClientBody::Empty => write!(f, "ClientBody::Empty"),
ClientBody::Binary(ref b) => write!(f, "ClientBody::Binary({:?})", b),
ClientBody::Streaming(_) => write!(f, "ClientBody::Streaming(_)"),
ClientBody::Actor(_) => write!(f, "ClientBody::Actor(_)"),
}
}
}
impl<T> From<T> for ClientBody
where
T: Into<Binary>,
{
fn from(b: T) -> ClientBody {
ClientBody::Binary(b.into())
}
}

View file

@ -1,11 +1,12 @@
//! Http client api //! Http client api
//! //!
//! ```rust,ignore //! ```rust
//! # extern crate actix; //! # extern crate actix;
//! # extern crate actix_web; //! # extern crate actix_web;
//! # extern crate futures; //! # extern crate futures;
//! # extern crate tokio; //! # extern crate tokio;
//! # use futures::Future; //! # use futures::Future;
//! # use std::process;
//! use actix_web::client; //! use actix_web::client;
//! //!
//! fn main() { //! fn main() {
@ -17,11 +18,14 @@
//! .map_err(|_| ()) //! .map_err(|_| ())
//! .and_then(|response| { // <- server http response //! .and_then(|response| { // <- server http response
//! println!("Response: {:?}", response); //! println!("Response: {:?}", response);
//! # process::exit(0);
//! Ok(()) //! Ok(())
//! }) //! })
//! }); //! });
//! } //! }
//! ``` //! ```
mod body;
mod connector; mod connector;
mod parser; mod parser;
mod pipeline; mod pipeline;
@ -29,6 +33,7 @@ mod request;
mod response; mod response;
mod writer; mod writer;
pub use self::body::{ClientBody, ClientBodyStream};
pub use self::connector::{ pub use self::connector::{
ClientConnector, ClientConnectorError, ClientConnectorStats, Connect, Connection, ClientConnector, ClientConnectorError, ClientConnectorStats, Connect, Connection,
Pause, Resume, Pause, Resume,
@ -56,11 +61,15 @@ impl ResponseError for SendRequestError {
/// Create request builder for `GET` requests /// Create request builder for `GET` requests
/// ///
/// ```rust,ignore ///
/// ```rust
/// # extern crate actix; /// # extern crate actix;
/// # extern crate actix_web; /// # extern crate actix_web;
/// # extern crate futures; /// # extern crate futures;
/// # use futures::{future, Future}; /// # extern crate tokio;
/// # extern crate env_logger;
/// # use futures::Future;
/// # use std::process;
/// use actix_web::client; /// use actix_web::client;
/// ///
/// fn main() { /// fn main() {
@ -72,6 +81,7 @@ impl ResponseError for SendRequestError {
/// .map_err(|_| ()) /// .map_err(|_| ())
/// .and_then(|response| { // <- server http response /// .and_then(|response| { // <- server http response
/// println!("Response: {:?}", response); /// println!("Response: {:?}", response);
/// # process::exit(0);
/// Ok(()) /// Ok(())
/// })); /// }));
/// } /// }

View file

@ -1,5 +1,5 @@
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::unsync::oneshot; use futures::sync::oneshot;
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use http::header::CONTENT_ENCODING; use http::header::CONTENT_ENCODING;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -8,18 +8,16 @@ use tokio_timer::Delay;
use actix::prelude::*; use actix::prelude::*;
use super::HttpClientWriter; use super::{
use super::{ClientConnector, ClientConnectorError, Connect, Connection}; ClientBody, ClientBodyStream, ClientConnector, ClientConnectorError, ClientRequest,
use super::{ClientRequest, ClientResponse}; ClientResponse, Connect, Connection, HttpClientWriter, HttpResponseParser,
use super::{HttpResponseParser, HttpResponseParserError}; HttpResponseParserError,
use body::{Body, BodyStream}; };
use context::{ActorHttpContext, Frame};
use error::Error; use error::Error;
use error::PayloadError; use error::PayloadError;
use header::ContentEncoding; use header::ContentEncoding;
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use server::encoding::PayloadStream; use server::encoding::PayloadStream;
use server::shared::SharedBytes;
use server::WriterState; use server::WriterState;
/// A set of errors that can occur during request sending and response reading /// A set of errors that can occur during request sending and response reading
@ -68,7 +66,7 @@ enum State {
pub struct SendRequest { pub struct SendRequest {
req: ClientRequest, req: ClientRequest,
state: State, state: State,
conn: Addr<ClientConnector>, conn: Option<Addr<ClientConnector>>,
conn_timeout: Duration, conn_timeout: Duration,
wait_timeout: Duration, wait_timeout: Duration,
timeout: Option<Delay>, timeout: Option<Delay>,
@ -76,7 +74,14 @@ pub struct SendRequest {
impl SendRequest { impl SendRequest {
pub(crate) fn new(req: ClientRequest) -> SendRequest { pub(crate) fn new(req: ClientRequest) -> SendRequest {
SendRequest::with_connector(req, ClientConnector::from_registry()) SendRequest {
req,
conn: None,
state: State::New,
timeout: None,
wait_timeout: Duration::from_secs(5),
conn_timeout: Duration::from_secs(1),
}
} }
pub(crate) fn with_connector( pub(crate) fn with_connector(
@ -84,7 +89,7 @@ impl SendRequest {
) -> SendRequest { ) -> SendRequest {
SendRequest { SendRequest {
req, req,
conn, conn: Some(conn),
state: State::New, state: State::New,
timeout: None, timeout: None,
wait_timeout: Duration::from_secs(5), wait_timeout: Duration::from_secs(5),
@ -96,7 +101,7 @@ impl SendRequest {
SendRequest { SendRequest {
req, req,
state: State::Connection(conn), state: State::Connection(conn),
conn: ClientConnector::from_registry(), conn: None,
timeout: None, timeout: None,
wait_timeout: Duration::from_secs(5), wait_timeout: Duration::from_secs(5),
conn_timeout: Duration::from_secs(1), conn_timeout: Duration::from_secs(1),
@ -142,7 +147,12 @@ impl Future for SendRequest {
match state { match state {
State::New => { State::New => {
self.state = State::Connect(self.conn.send(Connect { let conn = if let Some(conn) = self.conn.take() {
conn
} else {
ClientConnector::from_registry()
};
self.state = State::Connect(conn.send(Connect {
uri: self.req.uri().clone(), uri: self.req.uri().clone(),
wait_timeout: self.wait_timeout, wait_timeout: self.wait_timeout,
conn_timeout: self.conn_timeout, conn_timeout: self.conn_timeout,
@ -160,16 +170,16 @@ impl Future for SendRequest {
Err(_) => { Err(_) => {
return Err(SendRequestError::Connector( return Err(SendRequestError::Connector(
ClientConnectorError::Disconnected, ClientConnectorError::Disconnected,
)) ));
} }
}, },
State::Connection(conn) => { State::Connection(conn) => {
let mut writer = HttpClientWriter::new(SharedBytes::default()); let mut writer = HttpClientWriter::new();
writer.start(&mut self.req)?; writer.start(&mut self.req)?;
let body = match self.req.replace_body(Body::Empty) { let body = match self.req.replace_body(ClientBody::Empty) {
Body::Streaming(stream) => IoBody::Payload(stream), ClientBody::Streaming(stream) => IoBody::Payload(stream),
Body::Actor(ctx) => IoBody::Actor(ctx), ClientBody::Actor(_) => panic!("Client actor is not supported"),
_ => IoBody::Done, _ => IoBody::Done,
}; };
@ -208,7 +218,9 @@ impl Future for SendRequest {
self.state = State::Send(pl); self.state = State::Send(pl);
return Ok(Async::NotReady); return Ok(Async::NotReady);
} }
Err(err) => return Err(SendRequestError::ParseError(err)), Err(err) => {
return Err(SendRequestError::ParseError(err));
}
} }
} }
State::None => unreachable!(), State::None => unreachable!(),
@ -233,8 +245,7 @@ pub(crate) struct Pipeline {
} }
enum IoBody { enum IoBody {
Payload(BodyStream), Payload(ClientBodyStream),
Actor(Box<ActorHttpContext>),
Done, Done,
} }
@ -380,10 +391,7 @@ impl Pipeline {
match self.timeout.as_mut().unwrap().poll() { match self.timeout.as_mut().unwrap().poll() {
Ok(Async::Ready(())) => return Err(SendRequestError::Timeout), Ok(Async::Ready(())) => return Err(SendRequestError::Timeout),
Ok(Async::NotReady) => (), Ok(Async::NotReady) => (),
Err(e) => { Err(_) => return Err(SendRequestError::Timeout),
println!("err: {:?}", e);
return Err(SendRequestError::Timeout);
}
} }
} }
Ok(()) Ok(())
@ -397,66 +405,24 @@ impl Pipeline {
let mut done = false; let mut done = false;
if self.drain.is_none() && self.write_state != RunningState::Paused { if self.drain.is_none() && self.write_state != RunningState::Paused {
'outter: loop { loop {
let result = match mem::replace(&mut self.body, IoBody::Done) { let result = match mem::replace(&mut self.body, IoBody::Done) {
IoBody::Payload(mut body) => match body.poll()? { IoBody::Payload(mut stream) => match stream.poll()? {
Async::Ready(None) => { Async::Ready(None) => {
self.writer.write_eof()?; self.writer.write_eof()?;
self.body_completed = true; self.body_completed = true;
break; break;
} }
Async::Ready(Some(chunk)) => { Async::Ready(Some(chunk)) => {
self.body = IoBody::Payload(body); self.body = IoBody::Payload(stream);
self.writer.write(chunk.into())? self.writer.write(chunk.as_ref())?
} }
Async::NotReady => { Async::NotReady => {
done = true; done = true;
self.body = IoBody::Payload(body); self.body = IoBody::Payload(stream);
break; 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) => {
self.body_completed = true;
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.write_state.resume();
break;
}
res.unwrap()
}
Async::Ready(None) => {
done = true;
break;
}
Async::NotReady => {
done = true;
self.body = IoBody::Actor(ctx);
break;
}
}
}
IoBody::Done => { IoBody::Done => {
self.body_completed = true; self.body_completed = true;
done = true; done = true;

View file

@ -12,9 +12,9 @@ use serde::Serialize;
use serde_json; use serde_json;
use url::Url; use url::Url;
use super::body::ClientBody;
use super::connector::{ClientConnector, Connection}; use super::connector::{ClientConnector, Connection};
use super::pipeline::SendRequest; use super::pipeline::SendRequest;
use body::Body;
use error::Error; use error::Error;
use header::{ContentEncoding, Header, IntoHeaderValue}; use header::{ContentEncoding, Header, IntoHeaderValue};
use http::header::{self, HeaderName, HeaderValue}; use http::header::{self, HeaderName, HeaderValue};
@ -24,11 +24,13 @@ use httprequest::HttpRequest;
/// An HTTP Client Request /// An HTTP Client Request
/// ///
/// ```rust,ignore /// ```rust
/// # extern crate actix; /// # extern crate actix;
/// # extern crate actix_web; /// # extern crate actix_web;
/// # extern crate futures; /// # extern crate futures;
/// # extern crate tokio;
/// # use futures::Future; /// # use futures::Future;
/// # use std::process;
/// use actix_web::client::ClientRequest; /// use actix_web::client::ClientRequest;
/// ///
/// fn main() { /// fn main() {
@ -40,6 +42,7 @@ use httprequest::HttpRequest;
/// .map_err(|_| ()) /// .map_err(|_| ())
/// .and_then(|response| { // <- server http response /// .and_then(|response| { // <- server http response
/// println!("Response: {:?}", response); /// println!("Response: {:?}", response);
/// # process::exit(0);
/// Ok(()) /// Ok(())
/// }) /// })
/// ); /// );
@ -50,7 +53,7 @@ pub struct ClientRequest {
method: Method, method: Method,
version: Version, version: Version,
headers: HeaderMap, headers: HeaderMap,
body: Body, body: ClientBody,
chunked: bool, chunked: bool,
upgrade: bool, upgrade: bool,
timeout: Option<Duration>, timeout: Option<Duration>,
@ -73,7 +76,7 @@ impl Default for ClientRequest {
method: Method::default(), method: Method::default(),
version: Version::HTTP_11, version: Version::HTTP_11,
headers: HeaderMap::with_capacity(16), headers: HeaderMap::with_capacity(16),
body: Body::Empty, body: ClientBody::Empty,
chunked: false, chunked: false,
upgrade: false, upgrade: false,
timeout: None, timeout: None,
@ -217,17 +220,17 @@ impl ClientRequest {
/// Get body of this response /// Get body of this response
#[inline] #[inline]
pub fn body(&self) -> &Body { pub fn body(&self) -> &ClientBody {
&self.body &self.body
} }
/// Set a body /// Set a body
pub fn set_body<B: Into<Body>>(&mut self, body: B) { pub fn set_body<B: Into<ClientBody>>(&mut self, body: B) {
self.body = body.into(); self.body = body.into();
} }
/// Extract body, replace it with `Empty` /// Extract body, replace it with `Empty`
pub(crate) fn replace_body(&mut self, body: Body) -> Body { pub(crate) fn replace_body(&mut self, body: ClientBody) -> ClientBody {
mem::replace(&mut self.body, body) mem::replace(&mut self.body, body)
} }
@ -578,7 +581,9 @@ impl ClientRequestBuilder {
/// Set a body and generate `ClientRequest`. /// Set a body and generate `ClientRequest`.
/// ///
/// `ClientRequestBuilder` can not be used after this call. /// `ClientRequestBuilder` can not be used after this call.
pub fn body<B: Into<Body>>(&mut self, body: B) -> Result<ClientRequest, Error> { pub fn body<B: Into<ClientBody>>(
&mut self, body: B,
) -> Result<ClientRequest, Error> {
if let Some(e) = self.err.take() { if let Some(e) = self.err.take() {
return Err(e.into()); return Err(e.into());
} }
@ -644,17 +649,19 @@ impl ClientRequestBuilder {
/// `ClientRequestBuilder` can not be used after this call. /// `ClientRequestBuilder` can not be used after this call.
pub fn streaming<S, E>(&mut self, stream: S) -> Result<ClientRequest, Error> pub fn streaming<S, E>(&mut self, stream: S) -> Result<ClientRequest, Error>
where where
S: Stream<Item = Bytes, Error = E> + 'static, S: Stream<Item = Bytes, Error = E> + Send + 'static,
E: Into<Error>, E: Into<Error>,
{ {
self.body(Body::Streaming(Box::new(stream.map_err(|e| e.into())))) self.body(ClientBody::Streaming(Box::new(
stream.map_err(|e| e.into()),
)))
} }
/// Set an empty body and generate `ClientRequest` /// Set an empty body and generate `ClientRequest`
/// ///
/// `ClientRequestBuilder` can not be used after this call. /// `ClientRequestBuilder` can not be used after this call.
pub fn finish(&mut self) -> Result<ClientRequest, Error> { pub fn finish(&mut self) -> Result<ClientRequest, Error> {
self.body(Body::Empty) self.body(ClientBody::Empty)
} }
/// This method construct new `ClientRequestBuilder` /// This method construct new `ClientRequestBuilder`

View file

@ -19,13 +19,12 @@ use http::{HttpTryFrom, Version};
use time::{self, Duration}; use time::{self, Duration};
use tokio_io::AsyncWrite; use tokio_io::AsyncWrite;
use body::{Binary, Body}; use body::Binary;
use header::ContentEncoding; use header::ContentEncoding;
use server::encoding::{ContentEncoder, TransferEncoding}; use server::encoding::{ContentEncoder, TransferEncoding};
use server::shared::SharedBytes;
use server::WriterState; use server::WriterState;
use client::ClientRequest; use client::{ClientBody, ClientRequest};
const AVERAGE_HEADER_SIZE: usize = 30; const AVERAGE_HEADER_SIZE: usize = 30;
@ -42,20 +41,20 @@ pub(crate) struct HttpClientWriter {
flags: Flags, flags: Flags,
written: u64, written: u64,
headers_size: u32, headers_size: u32,
buffer: SharedBytes, buffer: Box<BytesMut>,
buffer_capacity: usize, buffer_capacity: usize,
encoder: ContentEncoder, encoder: ContentEncoder,
} }
impl HttpClientWriter { impl HttpClientWriter {
pub fn new(buffer: SharedBytes) -> HttpClientWriter { pub fn new() -> HttpClientWriter {
let encoder = ContentEncoder::Identity(TransferEncoding::eof(buffer.clone())); let encoder = ContentEncoder::Identity(TransferEncoding::eof());
HttpClientWriter { HttpClientWriter {
flags: Flags::empty(), flags: Flags::empty(),
written: 0, written: 0,
headers_size: 0, headers_size: 0,
buffer_capacity: 0, buffer_capacity: 0,
buffer, buffer: Box::new(BytesMut::new()),
encoder, encoder,
} }
} }
@ -98,12 +97,23 @@ impl HttpClientWriter {
} }
} }
pub struct Writer<'a>(pub &'a mut BytesMut);
impl<'a> io::Write for Writer<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl HttpClientWriter { impl HttpClientWriter {
pub fn start(&mut self, msg: &mut ClientRequest) -> io::Result<()> { pub fn start(&mut self, msg: &mut ClientRequest) -> io::Result<()> {
// prepare task // prepare task
self.flags.insert(Flags::STARTED); self.flags.insert(Flags::STARTED);
self.encoder = content_encoder(self.buffer.clone(), msg); self.encoder = content_encoder(self.buffer.as_mut(), msg);
if msg.upgrade() { if msg.upgrade() {
self.flags.insert(Flags::UPGRADE); self.flags.insert(Flags::UPGRADE);
} }
@ -112,7 +122,7 @@ impl HttpClientWriter {
{ {
// status line // status line
writeln!( writeln!(
self.buffer, Writer(&mut self.buffer),
"{} {} {:?}\r", "{} {} {:?}\r",
msg.method(), msg.method(),
msg.uri() msg.uri()
@ -120,40 +130,41 @@ impl HttpClientWriter {
.map(|u| u.as_str()) .map(|u| u.as_str())
.unwrap_or("/"), .unwrap_or("/"),
msg.version() msg.version()
)?; ).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
// write headers // write headers
let mut buffer = self.buffer.get_mut(); if let ClientBody::Binary(ref bytes) = *msg.body() {
if let Body::Binary(ref bytes) = *msg.body() { self.buffer
buffer.reserve(msg.headers().len() * AVERAGE_HEADER_SIZE + bytes.len()); .reserve(msg.headers().len() * AVERAGE_HEADER_SIZE + bytes.len());
} else { } else {
buffer.reserve(msg.headers().len() * AVERAGE_HEADER_SIZE); self.buffer
.reserve(msg.headers().len() * AVERAGE_HEADER_SIZE);
} }
for (key, value) in msg.headers() { for (key, value) in msg.headers() {
let v = value.as_ref(); let v = value.as_ref();
let k = key.as_str().as_bytes(); let k = key.as_str().as_bytes();
buffer.reserve(k.len() + v.len() + 4); self.buffer.reserve(k.len() + v.len() + 4);
buffer.put_slice(k); self.buffer.put_slice(k);
buffer.put_slice(b": "); self.buffer.put_slice(b": ");
buffer.put_slice(v); self.buffer.put_slice(v);
buffer.put_slice(b"\r\n"); self.buffer.put_slice(b"\r\n");
} }
// set date header // set date header
if !msg.headers().contains_key(DATE) { if !msg.headers().contains_key(DATE) {
buffer.extend_from_slice(b"date: "); self.buffer.extend_from_slice(b"date: ");
set_date(&mut buffer); set_date(&mut self.buffer);
buffer.extend_from_slice(b"\r\n\r\n"); self.buffer.extend_from_slice(b"\r\n\r\n");
} else { } else {
buffer.extend_from_slice(b"\r\n"); self.buffer.extend_from_slice(b"\r\n");
} }
self.headers_size = buffer.len() as u32; self.headers_size = self.buffer.len() as u32;
if msg.body().is_binary() { if msg.body().is_binary() {
if let Body::Binary(bytes) = msg.replace_body(Body::Empty) { if let ClientBody::Binary(bytes) = msg.replace_body(ClientBody::Empty) {
self.written += bytes.len() as u64; self.written += bytes.len() as u64;
self.encoder.write(bytes)?; self.encoder.write(bytes.as_ref())?;
} }
} else { } else {
self.buffer_capacity = msg.write_buffer_capacity(); self.buffer_capacity = msg.write_buffer_capacity();
@ -162,7 +173,7 @@ impl HttpClientWriter {
Ok(()) Ok(())
} }
pub fn write(&mut self, payload: Binary) -> io::Result<WriterState> { pub fn write(&mut self, payload: &[u8]) -> io::Result<WriterState> {
self.written += payload.len() as u64; self.written += payload.len() as u64;
if !self.flags.contains(Flags::DISCONNECTED) { if !self.flags.contains(Flags::DISCONNECTED) {
if self.flags.contains(Flags::UPGRADE) { if self.flags.contains(Flags::UPGRADE) {
@ -210,20 +221,21 @@ impl HttpClientWriter {
} }
} }
fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder { fn content_encoder(buf: &mut BytesMut, req: &mut ClientRequest) -> ContentEncoder {
let version = req.version(); let version = req.version();
let mut body = req.replace_body(Body::Empty); let mut body = req.replace_body(ClientBody::Empty);
let mut encoding = req.content_encoding(); let mut encoding = req.content_encoding();
let transfer = match body { let mut transfer = match body {
Body::Empty => { ClientBody::Empty => {
req.headers_mut().remove(CONTENT_LENGTH); req.headers_mut().remove(CONTENT_LENGTH);
TransferEncoding::length(0, buf) TransferEncoding::length(0)
} }
Body::Binary(ref mut bytes) => { ClientBody::Binary(ref mut bytes) => {
if encoding.is_compression() { if encoding.is_compression() {
let tmp = SharedBytes::default(); let mut tmp = BytesMut::new();
let transfer = TransferEncoding::eof(tmp.clone()); let mut transfer = TransferEncoding::eof();
transfer.set_buffer(&mut tmp);
let mut enc = match encoding { let mut enc = match encoding {
#[cfg(feature = "flate2")] #[cfg(feature = "flate2")]
ContentEncoding::Deflate => ContentEncoder::Deflate( ContentEncoding::Deflate => ContentEncoder::Deflate(
@ -242,7 +254,7 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
ContentEncoding::Auto => unreachable!(), ContentEncoding::Auto => unreachable!(),
}; };
// TODO return error! // TODO return error!
let _ = enc.write(bytes.clone()); let _ = enc.write(bytes.as_ref());
let _ = enc.write_eof(); let _ = enc.write_eof();
*bytes = Binary::from(tmp.take()); *bytes = Binary::from(tmp.take());
@ -256,9 +268,9 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
let _ = write!(b, "{}", bytes.len()); let _ = write!(b, "{}", bytes.len());
req.headers_mut() req.headers_mut()
.insert(CONTENT_LENGTH, HeaderValue::try_from(b.freeze()).unwrap()); .insert(CONTENT_LENGTH, HeaderValue::try_from(b.freeze()).unwrap());
TransferEncoding::eof(buf) TransferEncoding::eof()
} }
Body::Streaming(_) | Body::Actor(_) => { ClientBody::Streaming(_) | ClientBody::Actor(_) => {
if req.upgrade() { if req.upgrade() {
if version == Version::HTTP_2 { if version == Version::HTTP_2 {
error!("Connection upgrade is forbidden for HTTP/2"); error!("Connection upgrade is forbidden for HTTP/2");
@ -270,9 +282,9 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
encoding = ContentEncoding::Identity; encoding = ContentEncoding::Identity;
req.headers_mut().remove(CONTENT_ENCODING); req.headers_mut().remove(CONTENT_ENCODING);
} }
TransferEncoding::eof(buf) TransferEncoding::eof()
} else { } else {
streaming_encoding(buf, version, req) streaming_encoding(version, req)
} }
} }
}; };
@ -283,6 +295,7 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
HeaderValue::from_static(encoding.as_str()), HeaderValue::from_static(encoding.as_str()),
); );
} }
transfer.set_buffer(buf);
req.replace_body(body); req.replace_body(body);
match encoding { match encoding {
@ -303,19 +316,17 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
} }
} }
fn streaming_encoding( fn streaming_encoding(version: Version, req: &mut ClientRequest) -> TransferEncoding {
buf: SharedBytes, version: Version, req: &mut ClientRequest,
) -> TransferEncoding {
if req.chunked() { if req.chunked() {
// Enable transfer encoding // Enable transfer encoding
req.headers_mut().remove(CONTENT_LENGTH); req.headers_mut().remove(CONTENT_LENGTH);
if version == Version::HTTP_2 { if version == Version::HTTP_2 {
req.headers_mut().remove(TRANSFER_ENCODING); req.headers_mut().remove(TRANSFER_ENCODING);
TransferEncoding::eof(buf) TransferEncoding::eof()
} else { } else {
req.headers_mut() req.headers_mut()
.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked")); .insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
TransferEncoding::chunked(buf) TransferEncoding::chunked()
} }
} else { } else {
// if Content-Length is specified, then use it as length hint // if Content-Length is specified, then use it as length hint
@ -338,9 +349,9 @@ fn streaming_encoding(
if !chunked { if !chunked {
if let Some(len) = len { if let Some(len) = len {
TransferEncoding::length(len, buf) TransferEncoding::length(len)
} else { } else {
TransferEncoding::eof(buf) TransferEncoding::eof()
} }
} else { } else {
// Enable transfer encoding // Enable transfer encoding
@ -348,11 +359,11 @@ fn streaming_encoding(
Version::HTTP_11 => { Version::HTTP_11 => {
req.headers_mut() req.headers_mut()
.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked")); .insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
TransferEncoding::chunked(buf) TransferEncoding::chunked()
} }
_ => { _ => {
req.headers_mut().remove(TRANSFER_ENCODING); req.headers_mut().remove(TRANSFER_ENCODING);
TransferEncoding::eof(buf) TransferEncoding::eof()
} }
} }
} }

View file

@ -1,5 +1,5 @@
use futures::sync::oneshot;
use futures::sync::oneshot::Sender; use futures::sync::oneshot::Sender;
use futures::unsync::oneshot;
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::marker::PhantomData; use std::marker::PhantomData;

View file

@ -801,7 +801,6 @@ mod tests {
.finish() .finish()
.unwrap(); .unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
// Invalid range header // Invalid range header

View file

@ -3,7 +3,7 @@ use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use std::{io, mem}; use std::{io, mem};
use futures::unsync::oneshot; use futures::sync::oneshot;
use futures::{Async, Future, Poll, Stream}; use futures::{Async, Future, Poll, Stream};
use log::Level::Debug; use log::Level::Debug;

View file

@ -1,7 +1,7 @@
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::str::FromStr; use std::str::FromStr;
use std::{cmp, io, mem}; use std::{cmp, io, mem, ptr};
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
use brotli2::write::{BrotliDecoder, BrotliEncoder}; use brotli2::write::{BrotliDecoder, BrotliEncoder};
@ -25,8 +25,6 @@ use httprequest::HttpInnerMessage;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use payload::{PayloadSender, PayloadStatus, PayloadWriter}; use payload::{PayloadSender, PayloadStatus, PayloadWriter};
use super::shared::SharedBytes;
pub(crate) enum PayloadType { pub(crate) enum PayloadType {
Sender(PayloadSender), Sender(PayloadSender),
Encoding(Box<EncodedPayload>), Encoding(Box<EncodedPayload>),
@ -381,12 +379,12 @@ pub(crate) enum ContentEncoder {
} }
impl ContentEncoder { impl ContentEncoder {
pub fn empty(bytes: SharedBytes) -> ContentEncoder { pub fn empty() -> ContentEncoder {
ContentEncoder::Identity(TransferEncoding::eof(bytes)) ContentEncoder::Identity(TransferEncoding::eof())
} }
pub fn for_server( pub fn for_server(
buf: SharedBytes, req: &HttpInnerMessage, resp: &mut HttpResponse, buf: &mut BytesMut, req: &HttpInnerMessage, resp: &mut HttpResponse,
response_encoding: ContentEncoding, response_encoding: ContentEncoding,
) -> ContentEncoder { ) -> ContentEncoder {
let version = resp.version().unwrap_or_else(|| req.version); let version = resp.version().unwrap_or_else(|| req.version);
@ -441,7 +439,7 @@ impl ContentEncoder {
if req.method != Method::HEAD { if req.method != Method::HEAD {
resp.headers_mut().remove(CONTENT_LENGTH); resp.headers_mut().remove(CONTENT_LENGTH);
} }
TransferEncoding::length(0, buf) TransferEncoding::length(0)
} }
&Body::Binary(_) => { &Body::Binary(_) => {
#[cfg(any(feature = "brotli", feature = "flate2"))] #[cfg(any(feature = "brotli", feature = "flate2"))]
@ -449,8 +447,9 @@ impl ContentEncoder {
if !(encoding == ContentEncoding::Identity if !(encoding == ContentEncoding::Identity
|| encoding == ContentEncoding::Auto) || encoding == ContentEncoding::Auto)
{ {
let tmp = SharedBytes::default(); let mut tmp = BytesMut::default();
let transfer = TransferEncoding::eof(tmp.clone()); let mut transfer = TransferEncoding::eof();
transfer.set_buffer(&mut tmp);
let mut enc = match encoding { let mut enc = match encoding {
#[cfg(feature = "flate2")] #[cfg(feature = "flate2")]
ContentEncoding::Deflate => ContentEncoder::Deflate( ContentEncoding::Deflate => ContentEncoder::Deflate(
@ -472,7 +471,7 @@ impl ContentEncoder {
let bin = resp.replace_body(Body::Empty).binary(); let bin = resp.replace_body(Body::Empty).binary();
// TODO return error! // TODO return error!
let _ = enc.write(bin); let _ = enc.write(bin.as_ref());
let _ = enc.write_eof(); let _ = enc.write_eof();
let body = tmp.take(); let body = tmp.take();
len = body.len(); len = body.len();
@ -492,7 +491,7 @@ impl ContentEncoder {
} else { } else {
// resp.headers_mut().remove(CONTENT_LENGTH); // resp.headers_mut().remove(CONTENT_LENGTH);
} }
TransferEncoding::eof(buf) TransferEncoding::eof()
} }
&Body::Streaming(_) | &Body::Actor(_) => { &Body::Streaming(_) | &Body::Actor(_) => {
if resp.upgrade() { if resp.upgrade() {
@ -503,14 +502,14 @@ impl ContentEncoder {
encoding = ContentEncoding::Identity; encoding = ContentEncoding::Identity;
resp.headers_mut().remove(CONTENT_ENCODING); resp.headers_mut().remove(CONTENT_ENCODING);
} }
TransferEncoding::eof(buf) TransferEncoding::eof()
} else { } else {
if !(encoding == ContentEncoding::Identity if !(encoding == ContentEncoding::Identity
|| encoding == ContentEncoding::Auto) || encoding == ContentEncoding::Auto)
{ {
resp.headers_mut().remove(CONTENT_LENGTH); resp.headers_mut().remove(CONTENT_LENGTH);
} }
ContentEncoder::streaming_encoding(buf, version, resp) ContentEncoder::streaming_encoding(version, resp)
} }
} }
}; };
@ -519,6 +518,7 @@ impl ContentEncoder {
resp.set_body(Body::Empty); resp.set_body(Body::Empty);
transfer.kind = TransferEncodingKind::Length(0); transfer.kind = TransferEncodingKind::Length(0);
} }
transfer.set_buffer(buf);
match encoding { match encoding {
#[cfg(feature = "flate2")] #[cfg(feature = "flate2")]
@ -539,7 +539,7 @@ impl ContentEncoder {
} }
fn streaming_encoding( fn streaming_encoding(
buf: SharedBytes, version: Version, resp: &mut HttpResponse, version: Version, resp: &mut HttpResponse,
) -> TransferEncoding { ) -> TransferEncoding {
match resp.chunked() { match resp.chunked() {
Some(true) => { Some(true) => {
@ -547,14 +547,14 @@ impl ContentEncoder {
resp.headers_mut().remove(CONTENT_LENGTH); resp.headers_mut().remove(CONTENT_LENGTH);
if version == Version::HTTP_2 { if version == Version::HTTP_2 {
resp.headers_mut().remove(TRANSFER_ENCODING); resp.headers_mut().remove(TRANSFER_ENCODING);
TransferEncoding::eof(buf) TransferEncoding::eof()
} else { } else {
resp.headers_mut() resp.headers_mut()
.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked")); .insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
TransferEncoding::chunked(buf) TransferEncoding::chunked()
} }
} }
Some(false) => TransferEncoding::eof(buf), Some(false) => TransferEncoding::eof(),
None => { None => {
// if Content-Length is specified, then use it as length hint // if Content-Length is specified, then use it as length hint
let (len, chunked) = let (len, chunked) =
@ -577,9 +577,9 @@ impl ContentEncoder {
if !chunked { if !chunked {
if let Some(len) = len { if let Some(len) = len {
TransferEncoding::length(len, buf) TransferEncoding::length(len)
} else { } else {
TransferEncoding::eof(buf) TransferEncoding::eof()
} }
} else { } else {
// Enable transfer encoding // Enable transfer encoding
@ -589,11 +589,11 @@ impl ContentEncoder {
TRANSFER_ENCODING, TRANSFER_ENCODING,
HeaderValue::from_static("chunked"), HeaderValue::from_static("chunked"),
); );
TransferEncoding::chunked(buf) TransferEncoding::chunked()
} }
_ => { _ => {
resp.headers_mut().remove(TRANSFER_ENCODING); resp.headers_mut().remove(TRANSFER_ENCODING);
TransferEncoding::eof(buf) TransferEncoding::eof()
} }
} }
} }
@ -619,10 +619,8 @@ impl ContentEncoder {
#[cfg_attr(feature = "cargo-clippy", allow(inline_always))] #[cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#[inline(always)] #[inline(always)]
pub fn write_eof(&mut self) -> Result<bool, io::Error> { pub fn write_eof(&mut self) -> Result<bool, io::Error> {
let encoder = mem::replace( let encoder =
self, mem::replace(self, ContentEncoder::Identity(TransferEncoding::eof()));
ContentEncoder::Identity(TransferEncoding::eof(SharedBytes::empty())),
);
match encoder { match encoder {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
@ -662,38 +660,32 @@ impl ContentEncoder {
#[cfg_attr(feature = "cargo-clippy", allow(inline_always))] #[cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#[inline(always)] #[inline(always)]
pub fn write(&mut self, data: Binary) -> Result<(), io::Error> { pub fn write(&mut self, data: &[u8]) -> Result<(), io::Error> {
match *self { match *self {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
ContentEncoder::Br(ref mut encoder) => { ContentEncoder::Br(ref mut encoder) => match encoder.write_all(data) {
match encoder.write_all(data.as_ref()) { Ok(_) => Ok(()),
Ok(_) => Ok(()), Err(err) => {
Err(err) => { trace!("Error decoding br encoding: {}", err);
trace!("Error decoding br encoding: {}", err); Err(err)
Err(err)
}
} }
} },
#[cfg(feature = "flate2")] #[cfg(feature = "flate2")]
ContentEncoder::Gzip(ref mut encoder) => { ContentEncoder::Gzip(ref mut encoder) => match encoder.write_all(data) {
match encoder.write_all(data.as_ref()) { Ok(_) => Ok(()),
Ok(_) => Ok(()), Err(err) => {
Err(err) => { trace!("Error decoding gzip encoding: {}", err);
trace!("Error decoding gzip encoding: {}", err); Err(err)
Err(err)
}
} }
} },
#[cfg(feature = "flate2")] #[cfg(feature = "flate2")]
ContentEncoder::Deflate(ref mut encoder) => { ContentEncoder::Deflate(ref mut encoder) => match encoder.write_all(data) {
match encoder.write_all(data.as_ref()) { Ok(_) => Ok(()),
Ok(_) => Ok(()), Err(err) => {
Err(err) => { trace!("Error decoding deflate encoding: {}", err);
trace!("Error decoding deflate encoding: {}", err); Err(err)
Err(err)
}
} }
} },
ContentEncoder::Identity(ref mut encoder) => { ContentEncoder::Identity(ref mut encoder) => {
encoder.encode(data)?; encoder.encode(data)?;
Ok(()) Ok(())
@ -705,10 +697,12 @@ impl ContentEncoder {
/// Encoders to handle different Transfer-Encodings. /// Encoders to handle different Transfer-Encodings.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct TransferEncoding { pub(crate) struct TransferEncoding {
buf: *mut BytesMut,
kind: TransferEncodingKind, kind: TransferEncodingKind,
buffer: SharedBytes,
} }
unsafe impl Send for TransferEncoding {}
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
enum TransferEncodingKind { enum TransferEncodingKind {
/// An Encoder for when Transfer-Encoding includes `chunked`. /// An Encoder for when Transfer-Encoding includes `chunked`.
@ -724,27 +718,31 @@ enum TransferEncodingKind {
} }
impl TransferEncoding { impl TransferEncoding {
pub(crate) fn set_buffer(&mut self, buf: *mut BytesMut) {
self.buf = buf;
}
#[inline] #[inline]
pub fn eof(bytes: SharedBytes) -> TransferEncoding { pub fn eof() -> TransferEncoding {
TransferEncoding { TransferEncoding {
kind: TransferEncodingKind::Eof, kind: TransferEncodingKind::Eof,
buffer: bytes, buf: ptr::null_mut(),
} }
} }
#[inline] #[inline]
pub fn chunked(bytes: SharedBytes) -> TransferEncoding { pub fn chunked() -> TransferEncoding {
TransferEncoding { TransferEncoding {
kind: TransferEncodingKind::Chunked(false), kind: TransferEncodingKind::Chunked(false),
buffer: bytes, buf: ptr::null_mut(),
} }
} }
#[inline] #[inline]
pub fn length(len: u64, bytes: SharedBytes) -> TransferEncoding { pub fn length(len: u64) -> TransferEncoding {
TransferEncoding { TransferEncoding {
kind: TransferEncodingKind::Length(len), kind: TransferEncodingKind::Length(len),
buffer: bytes, buf: ptr::null_mut(),
} }
} }
@ -759,11 +757,13 @@ impl TransferEncoding {
/// Encode message. Return `EOF` state of encoder /// Encode message. Return `EOF` state of encoder
#[inline] #[inline]
pub fn encode(&mut self, mut msg: Binary) -> io::Result<bool> { pub fn encode(&mut self, msg: &[u8]) -> io::Result<bool> {
match self.kind { match self.kind {
TransferEncodingKind::Eof => { TransferEncodingKind::Eof => {
let eof = msg.is_empty(); let eof = msg.is_empty();
self.buffer.extend(msg); debug_assert!(!self.buf.is_null());
let buf = unsafe { &mut *self.buf };
buf.extend(msg);
Ok(eof) Ok(eof)
} }
TransferEncodingKind::Chunked(ref mut eof) => { TransferEncodingKind::Chunked(ref mut eof) => {
@ -773,15 +773,20 @@ impl TransferEncoding {
if msg.is_empty() { if msg.is_empty() {
*eof = true; *eof = true;
self.buffer.extend_from_slice(b"0\r\n\r\n"); debug_assert!(!self.buf.is_null());
let buf = unsafe { &mut *self.buf };
buf.extend_from_slice(b"0\r\n\r\n");
} else { } else {
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();
writeln!(&mut buf, "{:X}\r", msg.len()) writeln!(&mut buf, "{:X}\r", msg.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
self.buffer.reserve(buf.len() + msg.len() + 2);
self.buffer.extend(buf.into()); debug_assert!(!self.buf.is_null());
self.buffer.extend(msg); let b = unsafe { &mut *self.buf };
self.buffer.extend_from_slice(b"\r\n"); b.reserve(buf.len() + msg.len() + 2);
b.extend_from_slice(buf.as_ref());
b.extend_from_slice(msg);
b.extend_from_slice(b"\r\n");
} }
Ok(*eof) Ok(*eof)
} }
@ -791,7 +796,9 @@ impl TransferEncoding {
return Ok(*remaining == 0); return Ok(*remaining == 0);
} }
let len = cmp::min(*remaining, msg.len() as u64); let len = cmp::min(*remaining, msg.len() as u64);
self.buffer.extend(msg.take().split_to(len as usize).into());
debug_assert!(!self.buf.is_null());
unsafe { &mut *self.buf }.extend(&msg[..len as usize]);
*remaining -= len as u64; *remaining -= len as u64;
Ok(*remaining == 0) Ok(*remaining == 0)
@ -811,7 +818,10 @@ impl TransferEncoding {
TransferEncodingKind::Chunked(ref mut eof) => { TransferEncodingKind::Chunked(ref mut eof) => {
if !*eof { if !*eof {
*eof = true; *eof = true;
self.buffer.extend_from_slice(b"0\r\n\r\n");
debug_assert!(!self.buf.is_null());
let buf = unsafe { &mut *self.buf };
buf.extend_from_slice(b"0\r\n\r\n");
} }
true true
} }
@ -822,7 +832,7 @@ impl TransferEncoding {
impl io::Write for TransferEncoding { impl io::Write for TransferEncoding {
#[inline] #[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.encode(Binary::from_slice(buf))?; self.encode(buf)?;
Ok(buf.len()) Ok(buf.len())
} }
@ -904,12 +914,15 @@ mod tests {
#[test] #[test]
fn test_chunked_te() { fn test_chunked_te() {
let bytes = SharedBytes::default(); let mut bytes = BytesMut::new();
let mut enc = TransferEncoding::chunked(bytes.clone()); let mut enc = TransferEncoding::chunked();
assert!(!enc.encode(Binary::from(b"test".as_ref())).ok().unwrap()); {
assert!(enc.encode(Binary::from(b"".as_ref())).ok().unwrap()); enc.set_buffer(&mut bytes);
assert!(!enc.encode(b"test").ok().unwrap());
assert!(enc.encode(b"").ok().unwrap());
}
assert_eq!( assert_eq!(
bytes.get_mut().take().freeze(), bytes.take().freeze(),
Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n") Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n")
); );
} }

View file

@ -46,7 +46,7 @@ impl<T: AsyncWrite, H: 'static> H1Writer<T, H> {
) -> H1Writer<T, H> { ) -> H1Writer<T, H> {
H1Writer { H1Writer {
flags: Flags::KEEPALIVE, flags: Flags::KEEPALIVE,
encoder: ContentEncoder::empty(buf.clone()), encoder: ContentEncoder::empty(),
written: 0, written: 0,
headers_size: 0, headers_size: 0,
buffer: buf, buffer: buf,
@ -116,7 +116,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
) -> io::Result<WriterState> { ) -> io::Result<WriterState> {
// prepare task // prepare task
self.encoder = self.encoder =
ContentEncoder::for_server(self.buffer.clone(), req, msg, encoding); ContentEncoder::for_server(self.buffer.get_mut(), req, msg, encoding);
if msg.keep_alive().unwrap_or_else(|| req.keep_alive()) { if msg.keep_alive().unwrap_or_else(|| req.keep_alive()) {
self.flags = Flags::STARTED | Flags::KEEPALIVE; self.flags = Flags::STARTED | Flags::KEEPALIVE;
} else { } else {
@ -223,7 +223,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
if let Body::Binary(bytes) = body { if let Body::Binary(bytes) = body {
self.written = bytes.len() as u64; self.written = bytes.len() as u64;
self.encoder.write(bytes)?; self.encoder.write(bytes.as_ref())?;
} else { } else {
// capacity, makes sense only for streaming or actor // capacity, makes sense only for streaming or actor
self.buffer_capacity = msg.write_buffer_capacity(); self.buffer_capacity = msg.write_buffer_capacity();
@ -251,7 +251,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
} }
} else { } else {
// TODO: add warning, write after EOF // TODO: add warning, write after EOF
self.encoder.write(payload)?; self.encoder.write(payload.as_ref())?;
} }
} else { } else {
// could be response to EXCEPT header // could be response to EXCEPT header

View file

@ -51,7 +51,7 @@ impl<H: 'static> H2Writer<H> {
respond, respond,
settings, settings,
stream: None, stream: None,
encoder: ContentEncoder::empty(buf.clone()), encoder: ContentEncoder::empty(),
flags: Flags::empty(), flags: Flags::empty(),
written: 0, written: 0,
buffer: buf, buffer: buf,
@ -88,7 +88,7 @@ impl<H: 'static> Writer for H2Writer<H> {
// prepare response // prepare response
self.flags.insert(Flags::STARTED); self.flags.insert(Flags::STARTED);
self.encoder = self.encoder =
ContentEncoder::for_server(self.buffer.clone(), req, msg, encoding); ContentEncoder::for_server(self.buffer.get_mut(), req, msg, encoding);
if let Body::Empty = *msg.body() { if let Body::Empty = *msg.body() {
self.flags.insert(Flags::EOF); self.flags.insert(Flags::EOF);
} }
@ -143,7 +143,7 @@ impl<H: 'static> Writer for H2Writer<H> {
if let Body::Binary(bytes) = body { if let Body::Binary(bytes) = body {
self.flags.insert(Flags::EOF); self.flags.insert(Flags::EOF);
self.written = bytes.len() as u64; self.written = bytes.len() as u64;
self.encoder.write(bytes)?; self.encoder.write(bytes.as_ref())?;
if let Some(ref mut stream) = self.stream { if let Some(ref mut stream) = self.stream {
self.flags.insert(Flags::RESERVED); self.flags.insert(Flags::RESERVED);
stream.reserve_capacity(cmp::min(self.buffer.len(), CHUNK_SIZE)); stream.reserve_capacity(cmp::min(self.buffer.len(), CHUNK_SIZE));
@ -162,7 +162,7 @@ impl<H: 'static> Writer for H2Writer<H> {
if !self.flags.contains(Flags::DISCONNECTED) { if !self.flags.contains(Flags::DISCONNECTED) {
if self.flags.contains(Flags::STARTED) { if self.flags.contains(Flags::STARTED) {
// TODO: add warning, write after EOF // TODO: add warning, write after EOF
self.encoder.write(payload)?; self.encoder.write(payload.as_ref())?;
} else { } else {
// might be response for EXCEPT // might be response for EXCEPT
self.buffer.extend_from_slice(payload.as_ref()) self.buffer.extend_from_slice(payload.as_ref())

View file

@ -48,10 +48,6 @@ impl Drop for SharedBytes {
} }
impl SharedBytes { impl SharedBytes {
pub fn empty() -> Self {
SharedBytes(None, None)
}
pub fn new(bytes: Rc<BytesMut>, pool: Rc<SharedBytesPool>) -> SharedBytes { pub fn new(bytes: Rc<BytesMut>, pool: Rc<SharedBytesPool>) -> SharedBytes {
SharedBytes(Some(bytes), Some(pool)) SharedBytes(Some(bytes), Some(pool))
} }
@ -87,11 +83,6 @@ impl SharedBytes {
self.get_mut().take() self.get_mut().take()
} }
#[inline]
pub fn reserve(&self, cnt: usize) {
self.get_mut().reserve(cnt)
}
#[inline] #[inline]
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn extend(&self, data: Binary) { pub fn extend(&self, data: Binary) {

View file

@ -60,7 +60,6 @@ use ws;
/// ``` /// ```
pub struct TestServer { pub struct TestServer {
addr: net::SocketAddr, addr: net::SocketAddr,
thread: Option<thread::JoinHandle<()>>,
server_sys: Addr<System>, server_sys: Addr<System>,
ssl: bool, ssl: bool,
conn: Addr<ClientConnector>, conn: Addr<ClientConnector>,
@ -107,7 +106,7 @@ impl TestServer {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
// run server in separate thread // run server in separate thread
let join = thread::spawn(move || { thread::spawn(move || {
let sys = System::new("actix-test-server"); let sys = System::new("actix-test-server");
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = tcp.local_addr().unwrap(); let local_addr = tcp.local_addr().unwrap();
@ -134,7 +133,6 @@ impl TestServer {
server_sys, server_sys,
conn, conn,
ssl: false, ssl: false,
thread: Some(join),
rt: Runtime::new().unwrap(), rt: Runtime::new().unwrap(),
} }
} }
@ -190,10 +188,7 @@ impl TestServer {
/// Stop http server /// Stop http server
fn stop(&mut self) { fn stop(&mut self) {
if let Some(handle) = self.thread.take() { self.server_sys.do_send(msgs::SystemExit(0));
self.server_sys.do_send(msgs::SystemExit(0));
let _ = handle.join();
}
} }
/// Execute future on current core /// Execute future on current core
@ -287,7 +282,7 @@ impl<S: 'static> TestServerBuilder<S> {
let ssl = false; let ssl = false;
// run server in separate thread // run server in separate thread
let join = thread::spawn(move || { thread::spawn(move || {
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = tcp.local_addr().unwrap(); let local_addr = tcp.local_addr().unwrap();
@ -332,7 +327,6 @@ impl<S: 'static> TestServerBuilder<S> {
ssl, ssl,
conn, conn,
server_sys, server_sys,
thread: Some(join),
rt: Runtime::new().unwrap(), rt: Runtime::new().unwrap(),
} }
} }

View file

@ -7,7 +7,7 @@ use std::{fmt, io, str};
use base64; use base64;
use bytes::Bytes; use bytes::Bytes;
use cookie::Cookie; use cookie::Cookie;
use futures::unsync::mpsc::{unbounded, UnboundedSender}; use futures::sync::mpsc::{unbounded, UnboundedSender};
use futures::{Async, Future, Poll, Stream}; use futures::{Async, Future, Poll, Stream};
use http::header::{self, HeaderName, HeaderValue}; use http::header::{self, HeaderName, HeaderValue};
use http::{Error as HttpError, HttpTryFrom, StatusCode}; use http::{Error as HttpError, HttpTryFrom, StatusCode};
@ -16,14 +16,14 @@ use sha1::Sha1;
use actix::prelude::*; use actix::prelude::*;
use body::{Binary, Body}; use body::Binary;
use error::{Error, UrlParseError}; use error::{Error, UrlParseError};
use header::IntoHeaderValue; use header::IntoHeaderValue;
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use payload::PayloadHelper; use payload::PayloadHelper;
use client::{ use client::{
ClientConnector, ClientRequest, ClientRequestBuilder, ClientResponse, ClientBody, ClientConnector, ClientRequest, ClientRequestBuilder, ClientResponse,
HttpResponseParserError, SendRequest, SendRequestError, HttpResponseParserError, SendRequest, SendRequestError,
}; };
@ -283,7 +283,7 @@ impl ClientHandshake {
); );
let (tx, rx) = unbounded(); let (tx, rx) = unbounded();
request.set_body(Body::Streaming(Box::new(rx.map_err(|_| { request.set_body(ClientBody::Streaming(Box::new(rx.map_err(|_| {
io::Error::new(io::ErrorKind::Other, "disconnected").into() io::Error::new(io::ErrorKind::Other, "disconnected").into()
})))); }))));

View file

@ -1,5 +1,4 @@
use futures::sync::oneshot::Sender; use futures::sync::oneshot::{self, Sender};
use futures::unsync::oneshot;
use futures::{Async, Poll}; use futures::{Async, Poll};
use smallvec::SmallVec; use smallvec::SmallVec;

View file

@ -343,7 +343,10 @@ fn test_client_streaming_explicit() {
let body = once(Ok(Bytes::from_static(STR.as_ref()))); let body = once(Ok(Bytes::from_static(STR.as_ref())));
let request = srv.get().body(Body::Streaming(Box::new(body))).unwrap(); let request = srv
.get()
.body(client::ClientBody::Streaming(Box::new(body)))
.unwrap();
let response = srv.execute(request.send()).unwrap(); let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());

View file

@ -32,7 +32,7 @@ use tokio::executor::current_thread;
use tokio::runtime::current_thread::Runtime; use tokio::runtime::current_thread::Runtime;
use tokio_tcp::TcpStream; use tokio_tcp::TcpStream;
use actix::System; use actix::{msgs::SystemExit, Arbiter, System};
use actix_web::*; use actix_web::*;
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
@ -74,12 +74,11 @@ fn test_start() {
let srv = srv.bind("127.0.0.1:0").unwrap(); let srv = srv.bind("127.0.0.1:0").unwrap();
let addr = srv.addrs()[0]; let addr = srv.addrs()[0];
let srv_addr = srv.start(); let srv_addr = srv.start();
let _ = tx.send((addr, srv_addr)); let _ = tx.send((addr, srv_addr, Arbiter::system()));
}); });
}); });
let (addr, srv_addr) = rx.recv().unwrap(); let (addr, srv_addr, sys) = rx.recv().unwrap();
let _sys = System::new("test-server");
let mut rt = Runtime::new().unwrap(); let mut rt = Runtime::new().unwrap();
{ {
let req = client::ClientRequest::get(format!("http://{}/", addr).as_str()) let req = client::ClientRequest::get(format!("http://{}/", addr).as_str())
@ -102,7 +101,7 @@ fn test_start() {
// resume // resume
let _ = srv_addr.send(server::ResumeServer).wait(); let _ = srv_addr.send(server::ResumeServer).wait();
thread::sleep(time::Duration::from_millis(400)); thread::sleep(time::Duration::from_millis(200));
{ {
let req = client::ClientRequest::get(format!("http://{}/", addr).as_str()) let req = client::ClientRequest::get(format!("http://{}/", addr).as_str())
.finish() .finish()
@ -110,6 +109,8 @@ fn test_start() {
let response = rt.block_on(req.send()).unwrap(); let response = rt.block_on(req.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
} }
let _ = sys.send(SystemExit(0)).wait();
} }
#[test] #[test]
@ -129,12 +130,11 @@ fn test_shutdown() {
let srv = srv.bind("127.0.0.1:0").unwrap(); let srv = srv.bind("127.0.0.1:0").unwrap();
let addr = srv.addrs()[0]; let addr = srv.addrs()[0];
let srv_addr = srv.shutdown_timeout(1).start(); let srv_addr = srv.shutdown_timeout(1).start();
let _ = tx.send((addr, srv_addr)); let _ = tx.send((addr, srv_addr, Arbiter::system()));
}); });
}); });
let (addr, srv_addr) = rx.recv().unwrap(); let (addr, srv_addr, sys) = rx.recv().unwrap();
let _sys = System::new("test-server");
let mut rt = Runtime::new().unwrap(); let mut rt = Runtime::new().unwrap();
{ {
let req = client::ClientRequest::get(format!("http://{}/", addr).as_str()) let req = client::ClientRequest::get(format!("http://{}/", addr).as_str())
@ -147,6 +147,8 @@ fn test_shutdown() {
thread::sleep(time::Duration::from_millis(1000)); thread::sleep(time::Duration::from_millis(1000));
assert!(net::TcpStream::connect(addr).is_err()); assert!(net::TcpStream::connect(addr).is_err());
let _ = sys.send(SystemExit(0)).wait();
} }
#[test] #[test]