1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 07:53:00 +01:00

refactor te encoding

This commit is contained in:
Nikolay Kim 2018-10-06 22:36:57 -07:00
parent dda5b399ca
commit b0ca6220f0
4 changed files with 83 additions and 100 deletions

View File

@ -374,7 +374,7 @@ impl ResponseError for cookie::ParseError {
#[derive(Debug)]
/// A set of errors that can occur during dispatching http requests
pub enum DispatchError<E: fmt::Debug + fmt::Display> {
pub enum DispatchError<E> {
/// Service error
// #[fail(display = "Application specific error: {}", _0)]
Service(E),
@ -413,13 +413,13 @@ pub enum DispatchError<E: fmt::Debug + fmt::Display> {
Unknown,
}
impl<E: fmt::Debug + fmt::Display> From<ParseError> for DispatchError<E> {
impl<E> From<ParseError> for DispatchError<E> {
fn from(err: ParseError) -> Self {
DispatchError::Parse(err)
}
}
impl<E: fmt::Debug + fmt::Display> From<io::Error> for DispatchError<E> {
impl<E> From<io::Error> for DispatchError<E> {
fn from(err: io::Error) -> Self {
DispatchError::Io(err)
}

View File

@ -54,7 +54,6 @@ pub struct Codec {
// encoder part
flags: Flags,
written: u64,
headers_size: u32,
te: ResponseEncoder,
}
@ -82,31 +81,30 @@ impl Codec {
version: Version::HTTP_11,
flags,
written: 0,
headers_size: 0,
te: ResponseEncoder::default(),
}
}
fn written(&self) -> u64 {
self.written
}
/// Check if request is upgrade
pub fn upgrade(&self) -> bool {
self.flags.contains(Flags::UPGRADE)
}
/// Check if last response is keep-alive
pub fn keepalive(&self) -> bool {
self.flags.contains(Flags::KEEPALIVE)
}
/// prepare transfer encoding
pub fn prepare_te(&mut self, res: &mut Response) {
self.te
.update(res, self.flags.contains(Flags::HEAD), self.version);
}
fn encode_response(
&mut self, mut msg: Response, buffer: &mut BytesMut,
) -> io::Result<()> {
// prepare transfer encoding
self.te
.update(&mut msg, self.flags.contains(Flags::HEAD), self.version);
let ka = self.flags.contains(Flags::KEEPALIVE_ENABLED) && msg
.keep_alive()
.unwrap_or_else(|| self.flags.contains(Flags::KEEPALIVE));
@ -131,12 +129,11 @@ impl Codec {
msg.headers_mut()
.insert(CONNECTION, HeaderValue::from_static("close"));
}
let body = msg.replace_body(Body::Empty);
// render message
{
let reason = msg.reason().as_bytes();
if let Body::Binary(ref bytes) = body {
if let Body::Binary(ref bytes) = msg.body() {
buffer.reserve(
256 + msg.headers().len() * AVERAGE_HEADER_SIZE
+ bytes.len()
@ -229,16 +226,6 @@ impl Codec {
self.headers_size = buffer.len() as u32;
}
if let Body::Binary(bytes) = body {
self.written = bytes.len() as u64;
// buffer.write(bytes.as_ref())?;
buffer.extend_from_slice(bytes.as_ref());
} else {
// capacity, makes sense only for streaming or actor
// self.buffer_capacity = msg.write_buffer_capacity();
msg.replace_body(body);
}
Ok(())
}
}
@ -282,7 +269,6 @@ impl Encoder for Codec {
) -> Result<(), Self::Error> {
match item {
OutMessage::Response(res) => {
self.written = 0;
self.encode_response(res, dst)?;
}
OutMessage::Payload(bytes) => {

View File

@ -1,15 +1,15 @@
use std::collections::VecDeque;
use std::fmt::{Debug, Display};
use std::time::Instant;
use actix_net::codec::Framed;
use actix_net::service::Service;
use futures::{Async, AsyncSink, Future, Poll, Sink, Stream};
use log::Level::Debug;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_timer::Delay;
use error::{ParseError, PayloadError};
use error::{Error, ParseError, PayloadError};
use payload::{Payload, PayloadSender, PayloadStatus, PayloadWriter};
use body::Body;
@ -38,7 +38,7 @@ bitflags! {
/// Dispatcher for HTTP/1.1 protocol
pub struct Dispatcher<T, S: Service>
where
S::Error: Debug + Display,
S::Error: Into<Error>,
{
service: S,
flags: Flags,
@ -81,7 +81,7 @@ impl<T, S> Dispatcher<T, S>
where
T: AsyncRead + AsyncWrite,
S: Service<Request = Request, Response = Response>,
S::Error: Debug + Display,
S::Error: Into<Error>,
{
/// Create http/1 dispatcher.
pub fn new(stream: T, config: ServiceConfig, service: S) -> Self {
@ -177,52 +177,34 @@ where
State::None => loop {
break if let Some(msg) = self.messages.pop_front() {
match msg {
Message::Item(msg) => {
let mut task = self.service.call(msg);
match task.poll() {
Ok(Async::Ready(res)) => {
if res.body().is_streaming() {
unimplemented!()
} else {
Some(Ok(State::SendResponse(Some(
OutMessage::Response(res),
))))
}
}
Ok(Async::NotReady) => {
Some(Ok(State::Response(task)))
}
Err(err) => Some(Err(DispatchError::Service(err))),
}
}
Message::Error(res) => Some(Ok(State::SendResponse(Some(
Message::Item(req) => Some(self.handle_request(req)),
Message::Error(res) => Some(State::SendResponse(Some(
OutMessage::Response(res),
)))),
))),
}
} else {
None
};
},
State::Payload(ref mut _body) => unimplemented!(),
State::Response(ref mut fut) => {
match fut.poll() {
Ok(Async::Ready(res)) => {
if res.body().is_streaming() {
unimplemented!()
} else {
Some(Ok(State::SendResponse(Some(
OutMessage::Response(res),
))))
}
}
Ok(Async::NotReady) => None,
Err(err) => {
// it is not possible to recover from error
// during pipe handling, so just drop connection
Some(Err(DispatchError::Service(err)))
State::Response(ref mut fut) => match fut.poll() {
Ok(Async::Ready(mut res)) => {
self.framed.get_codec_mut().prepare_te(&mut res);
if res.body().is_streaming() {
unimplemented!()
} else {
Some(State::SendResponse(Some(OutMessage::Response(res))))
}
}
}
Ok(Async::NotReady) => None,
Err(err) => {
let err = err.into();
if log_enabled!(Debug) {
debug!("{:?}", err);
}
Some(State::SendResponse(Some(OutMessage::Response(err.into()))))
}
},
State::SendResponse(ref mut item) => {
let msg = item.take().expect("SendResponse is empty");
match self.framed.start_send(msg) {
@ -232,13 +214,19 @@ where
self.framed.get_codec().keepalive(),
);
self.flags.remove(Flags::FLUSHED);
Some(Ok(State::None))
Some(State::None)
}
Ok(AsyncSink::NotReady(msg)) => {
*item = Some(msg);
return Ok(());
}
Err(err) => Some(Err(DispatchError::Io(err))),
Err(err) => {
self.flags.insert(Flags::READ_DISCONNECTED);
if let Some(mut payload) = self.payload.take() {
payload.set_error(PayloadError::Incomplete);
}
return Err(DispatchError::Io(err));
}
}
}
State::SendResponseWithPayload(ref mut item) => {
@ -251,23 +239,25 @@ where
self.framed.get_codec().keepalive(),
);
self.flags.remove(Flags::FLUSHED);
Some(Ok(State::Payload(body)))
Some(State::Payload(body))
}
Ok(AsyncSink::NotReady(msg)) => {
*item = Some((msg, body));
return Ok(());
}
Err(err) => Some(Err(DispatchError::Io(err))),
Err(err) => {
self.flags.insert(Flags::READ_DISCONNECTED);
if let Some(mut payload) = self.payload.take() {
payload.set_error(PayloadError::Incomplete);
}
return Err(DispatchError::Io(err));
}
}
}
};
match state {
Some(Ok(state)) => self.state = state,
Some(Err(err)) => {
self.client_disconnected();
return Err(err);
}
Some(state) => self.state = state,
None => {
// if read-backpressure is enabled and we consumed some data.
// we may read more dataand retry
@ -283,6 +273,28 @@ where
Ok(())
}
fn handle_request(&mut self, req: Request) -> State<S> {
let mut task = self.service.call(req);
match task.poll() {
Ok(Async::Ready(mut res)) => {
self.framed.get_codec_mut().prepare_te(&mut res);
if res.body().is_streaming() {
unimplemented!()
} else {
State::SendResponse(Some(OutMessage::Response(res)))
}
}
Ok(Async::NotReady) => State::Response(task),
Err(err) => {
let err = err.into();
if log_enabled!(Debug) {
debug!("{:?}", err);
}
State::SendResponse(Some(OutMessage::Response(err.into())))
}
}
}
fn one_message(&mut self, msg: InMessage) -> Result<(), DispatchError<S::Error>> {
self.flags.insert(Flags::STARTED);
@ -290,23 +302,7 @@ where
InMessage::Message(msg) => {
// handle request early
if self.state.is_empty() {
let mut task = self.service.call(msg);
match task.poll() {
Ok(Async::Ready(res)) => {
if res.body().is_streaming() {
unimplemented!()
} else {
self.state =
State::SendResponse(Some(OutMessage::Response(res)));
}
}
Ok(Async::NotReady) => self.state = State::Response(task),
Err(err) => {
error!("Unhandled application error: {}", err);
self.client_disconnected();
return Err(DispatchError::Service(err));
}
}
self.state = self.handle_request(msg);
} else {
self.messages.push_back(Message::Item(msg));
}
@ -449,7 +445,7 @@ impl<T, S> Future for Dispatcher<T, S>
where
T: AsyncRead + AsyncWrite,
S: Service<Request = Request, Response = Response>,
S::Error: Debug + Display,
S::Error: Into<Error>,
{
type Item = ();
type Error = DispatchError<S::Error>;

View File

@ -1,4 +1,3 @@
use std::fmt::{Debug, Display};
use std::marker::PhantomData;
use actix_net::codec::Framed;
@ -7,7 +6,7 @@ use futures::{future, Async, Future, Poll, Stream};
use tokio_io::{AsyncRead, AsyncWrite};
use config::ServiceConfig;
use error::{DispatchError, ParseError};
use error::{DispatchError, Error, ParseError};
use request::Request;
use response::Response;
@ -24,6 +23,8 @@ pub struct H1Service<T, S> {
impl<T, S> H1Service<T, S>
where
S: NewService,
S::Service: Clone,
S::Error: Into<Error>,
{
/// Create new `HttpService` instance.
pub fn new<F: IntoNewService<S>>(cfg: ServiceConfig, service: F) -> Self {
@ -40,7 +41,7 @@ where
T: AsyncRead + AsyncWrite,
S: NewService<Request = Request, Response = Response> + Clone,
S::Service: Clone,
S::Error: Debug + Display,
S::Error: Into<Error>,
{
type Request = T;
type Response = ();
@ -69,7 +70,7 @@ where
T: AsyncRead + AsyncWrite,
S: NewService<Request = Request, Response = Response>,
S::Service: Clone,
S::Error: Debug + Display,
S::Error: Into<Error>,
{
type Item = H1ServiceHandler<T, S::Service>;
type Error = S::InitError;
@ -93,7 +94,7 @@ pub struct H1ServiceHandler<T, S> {
impl<T, S> H1ServiceHandler<T, S>
where
S: Service<Request = Request, Response = Response> + Clone,
S::Error: Debug + Display,
S::Error: Into<Error>,
{
fn new(cfg: ServiceConfig, srv: S) -> H1ServiceHandler<T, S> {
H1ServiceHandler {
@ -108,7 +109,7 @@ impl<T, S> Service for H1ServiceHandler<T, S>
where
T: AsyncRead + AsyncWrite,
S: Service<Request = Request, Response = Response> + Clone,
S::Error: Debug + Display,
S::Error: Into<Error>,
{
type Request = T;
type Response = ();