mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-30 18:44:35 +01:00
refactor TransferEncoding; allow to use client api with threaded tokio runtime
This commit is contained in:
parent
844be8d9dd
commit
a64205e502
41
src/body.rs
41
src/body.rs
@ -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
94
src/client/body.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
@ -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(())
|
||||||
/// }));
|
/// }));
|
||||||
/// }
|
/// }
|
||||||
|
@ -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;
|
||||||
|
@ -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`
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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())
|
||||||
|
@ -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) {
|
||||||
|
10
src/test.rs
10
src/test.rs
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}))));
|
}))));
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user