mirror of
https://github.com/actix/actix-web.git
synced 2024-12-19 06:36:36 +00:00
simplify Frame::Message; impl Try for Reply
This commit is contained in:
parent
c0e73c7275
commit
0447c66de1
13 changed files with 226 additions and 141 deletions
|
@ -21,10 +21,17 @@ path = "src/lib.rs"
|
|||
name = "test"
|
||||
path = "src/main.rs"
|
||||
|
||||
[features]
|
||||
default = ["nightly"]
|
||||
|
||||
# Enable nightly features
|
||||
nightly = []
|
||||
|
||||
[dependencies]
|
||||
time = "0.1"
|
||||
http = "0.1"
|
||||
httparse = "0.1"
|
||||
cookie = { version="0.10", features=["percent-encode"] }
|
||||
slab = "0.4"
|
||||
sha1 = "0.2"
|
||||
url = "1.5"
|
||||
|
|
|
@ -9,7 +9,7 @@ use actix::fut::ActorFuture;
|
|||
use actix::dev::{AsyncContextApi, ActorAddressCell, ActorItemsCell, SpawnHandle};
|
||||
|
||||
use route::{Route, Frame};
|
||||
use httpmessage::{HttpRequest, HttpResponse};
|
||||
use httpmessage::HttpResponse;
|
||||
|
||||
|
||||
/// Actor execution context
|
||||
|
@ -102,8 +102,8 @@ impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
|
|||
}
|
||||
|
||||
/// Start response processing
|
||||
pub fn start<R: Into<HttpResponse>>(&mut self, request: HttpRequest, response: R) {
|
||||
self.stream.push_back(Frame::Message(request, response.into()))
|
||||
pub fn start<R: Into<HttpResponse>>(&mut self, response: R) {
|
||||
self.stream.push_back(Frame::Message(response.into()))
|
||||
}
|
||||
|
||||
/// Write payload
|
||||
|
|
110
src/error.rs
110
src/error.rs
|
@ -5,27 +5,15 @@ use std::io::Error as IoError;
|
|||
use std::str::Utf8Error;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use cookie;
|
||||
use httparse;
|
||||
use http::{StatusCode, Error as HttpError};
|
||||
|
||||
use self::Error::{
|
||||
Method,
|
||||
Uri,
|
||||
Version,
|
||||
Header,
|
||||
Status,
|
||||
Timeout,
|
||||
Io,
|
||||
TooLarge,
|
||||
Incomplete,
|
||||
Utf8
|
||||
};
|
||||
|
||||
/// Result type often returned from methods that can have error.
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
use httpmessage::{Body, HttpResponse};
|
||||
|
||||
/// A set of errors that can occur parsing HTTP streams.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
pub enum ParseError {
|
||||
/// An invalid `Method`, such as `GE,T`.
|
||||
Method,
|
||||
/// An invalid `Uri`, such as `exam ple.domain`.
|
||||
|
@ -43,79 +31,107 @@ pub enum Error {
|
|||
/// A timeout occurred waiting for an IO event.
|
||||
#[allow(dead_code)]
|
||||
Timeout,
|
||||
/// Unexpected EOF during parsing
|
||||
Eof,
|
||||
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
||||
Io(IoError),
|
||||
/// Parsing a field as string failed
|
||||
Utf8(Utf8Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Io(ref e) => fmt::Display::fmt(e, f),
|
||||
Utf8(ref e) => fmt::Display::fmt(e, f),
|
||||
ParseError::Io(ref e) => fmt::Display::fmt(e, f),
|
||||
ParseError::Utf8(ref e) => fmt::Display::fmt(e, f),
|
||||
ref e => f.write_str(e.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
impl StdError for ParseError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Method => "Invalid Method specified",
|
||||
Version => "Invalid HTTP version specified",
|
||||
Header => "Invalid Header provided",
|
||||
TooLarge => "Message head is too large",
|
||||
Status => "Invalid Status provided",
|
||||
Incomplete => "Message is incomplete",
|
||||
Timeout => "Timeout",
|
||||
Uri => "Uri error",
|
||||
Io(ref e) => e.description(),
|
||||
Utf8(ref e) => e.description(),
|
||||
ParseError::Method => "Invalid Method specified",
|
||||
ParseError::Version => "Invalid HTTP version specified",
|
||||
ParseError::Header => "Invalid Header provided",
|
||||
ParseError::TooLarge => "Message head is too large",
|
||||
ParseError::Status => "Invalid Status provided",
|
||||
ParseError::Incomplete => "Message is incomplete",
|
||||
ParseError::Timeout => "Timeout",
|
||||
ParseError::Uri => "Uri error",
|
||||
ParseError::Eof => "Unexpected eof during parse",
|
||||
ParseError::Io(ref e) => e.description(),
|
||||
ParseError::Utf8(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&StdError> {
|
||||
match *self {
|
||||
Io(ref error) => Some(error),
|
||||
Utf8(ref error) => Some(error),
|
||||
ParseError::Io(ref error) => Some(error),
|
||||
ParseError::Utf8(ref error) => Some(error),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Error {
|
||||
Io(err)
|
||||
impl From<IoError> for ParseError {
|
||||
fn from(err: IoError) -> ParseError {
|
||||
ParseError::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for Error {
|
||||
fn from(err: Utf8Error) -> Error {
|
||||
Utf8(err)
|
||||
impl From<Utf8Error> for ParseError {
|
||||
fn from(err: Utf8Error) -> ParseError {
|
||||
ParseError::Utf8(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for Error {
|
||||
fn from(err: FromUtf8Error) -> Error {
|
||||
Utf8(err.utf8_error())
|
||||
impl From<FromUtf8Error> for ParseError {
|
||||
fn from(err: FromUtf8Error) -> ParseError {
|
||||
ParseError::Utf8(err.utf8_error())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<httparse::Error> for Error {
|
||||
fn from(err: httparse::Error) -> Error {
|
||||
impl From<httparse::Error> for ParseError {
|
||||
fn from(err: httparse::Error) -> ParseError {
|
||||
match err {
|
||||
httparse::Error::HeaderName |
|
||||
httparse::Error::HeaderValue |
|
||||
httparse::Error::NewLine |
|
||||
httparse::Error::Token => Header,
|
||||
httparse::Error::Status => Status,
|
||||
httparse::Error::TooManyHeaders => TooLarge,
|
||||
httparse::Error::Version => Version,
|
||||
httparse::Error::Token => ParseError::Header,
|
||||
httparse::Error::Status => ParseError::Status,
|
||||
httparse::Error::TooManyHeaders => ParseError::TooLarge,
|
||||
httparse::Error::Version => ParseError::Version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return BadRequest for ParseError
|
||||
impl From<ParseError> for HttpResponse {
|
||||
fn from(err: ParseError) -> Self {
|
||||
HttpResponse::new(StatusCode::BAD_REQUEST,
|
||||
Body::Binary(err.description().into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Return InternalServerError for HttpError,
|
||||
/// Response generation can return HttpError, so it is internal error
|
||||
impl From<HttpError> for HttpResponse {
|
||||
fn from(err: HttpError) -> Self {
|
||||
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Body::Binary(err.description().into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Return BadRequest for cookie::ParseError
|
||||
impl From<cookie::ParseError> for HttpResponse {
|
||||
fn from(err: cookie::ParseError) -> Self {
|
||||
HttpResponse::new(StatusCode::BAD_REQUEST,
|
||||
Body::Binary(err.description().into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::error::Error as StdError;
|
||||
|
|
|
@ -35,8 +35,8 @@ impl StaticResponse {
|
|||
}
|
||||
|
||||
impl<S> RouteHandler<S> for StaticResponse {
|
||||
fn handle(&self, req: HttpRequest, _: Payload, _: Rc<S>) -> Task {
|
||||
Task::reply(req, HttpResponse::new(self.0, Body::Empty))
|
||||
fn handle(&self, _: HttpRequest, _: Payload, _: Rc<S>) -> Task {
|
||||
Task::reply(HttpResponse::new(self.0, Body::Empty))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Pieces pertaining to the HTTP message protocol.
|
||||
use std::{io, mem};
|
||||
use std::error::Error as StdError;
|
||||
use std::{io, mem, str};
|
||||
use std::convert::Into;
|
||||
|
||||
use cookie;
|
||||
use bytes::Bytes;
|
||||
use http::{Method, StatusCode, Version, Uri, HeaderMap, HttpTryFrom, Error};
|
||||
use http::header::{self, HeaderName, HeaderValue};
|
||||
|
@ -78,6 +78,17 @@ impl HttpRequest {
|
|||
self.uri.query()
|
||||
}
|
||||
|
||||
/// Return request cookie.
|
||||
pub fn cookie(&self) -> Result<Option<cookie::Cookie>, cookie::ParseError> {
|
||||
if let Some(val) = self.headers.get(header::COOKIE) {
|
||||
let s = str::from_utf8(val.as_bytes())
|
||||
.map_err(|e| cookie::ParseError::from(e))?;
|
||||
cookie::Cookie::parse(s).map(|c| Some(c))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the Request headers.
|
||||
#[inline]
|
||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||
|
@ -300,13 +311,7 @@ impl HttpResponse {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Error> for HttpResponse {
|
||||
fn from(err: Error) -> Self {
|
||||
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Body::Binary(err.description().into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper conversion implementation
|
||||
impl<I: Into<HttpResponse>, E: Into<HttpResponse>> From<Result<I, E>> for HttpResponse {
|
||||
fn from(res: Result<I, E>) -> Self {
|
||||
match res {
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
//! Http framework for [Actix](https://github.com/fafhrd91/actix)
|
||||
|
||||
#![cfg_attr(feature="nightly", feature(
|
||||
try_trait, // std::ops::Try #42327
|
||||
))]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate time;
|
||||
extern crate bytes;
|
||||
extern crate sha1;
|
||||
extern crate url;
|
||||
extern crate cookie;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate tokio_core;
|
||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -1,3 +1,4 @@
|
|||
#![feature(try_trait)]
|
||||
#![allow(dead_code, unused_variables)]
|
||||
extern crate actix;
|
||||
extern crate actix_http;
|
||||
|
@ -24,7 +25,7 @@ impl Route for MyRoute {
|
|||
ctx.add_stream(payload);
|
||||
Reply::stream(MyRoute{req: Some(req)})
|
||||
} else {
|
||||
Reply::reply(req, httpcodes::HTTPOk)
|
||||
Reply::reply(httpcodes::HTTPOk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +43,7 @@ impl Handler<PayloadItem> for MyRoute {
|
|||
{
|
||||
println!("CHUNK: {:?}", msg);
|
||||
if let Some(req) = self.req.take() {
|
||||
ctx.start(req, httpcodes::HTTPOk);
|
||||
ctx.start(httpcodes::HTTPOk);
|
||||
ctx.write_eof();
|
||||
}
|
||||
Self::empty()
|
||||
|
@ -58,16 +59,12 @@ impl Actor for MyWS {
|
|||
impl Route for MyWS {
|
||||
type State = ();
|
||||
|
||||
fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self> {
|
||||
match ws::handshake(&req) {
|
||||
Ok(resp) => {
|
||||
ctx.start(req, resp);
|
||||
ctx.add_stream(ws::WsStream::new(payload));
|
||||
Reply::stream(MyWS{})
|
||||
},
|
||||
Err(err) =>
|
||||
Reply::reply(req, err)
|
||||
}
|
||||
fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self>
|
||||
{
|
||||
let resp = ws::handshake(&req)?;
|
||||
ctx.start(resp);
|
||||
ctx.add_stream(ws::WsStream::new(payload));
|
||||
Reply::stream(MyWS{})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{self, fmt, io, ptr};
|
||||
use std::{self, io, ptr};
|
||||
|
||||
use httparse;
|
||||
use http::{Method, Version, Uri, HttpTryFrom, HeaderMap};
|
||||
|
@ -7,7 +7,7 @@ use bytes::{BytesMut, BufMut};
|
|||
use futures::{Async, Poll};
|
||||
use tokio_io::AsyncRead;
|
||||
|
||||
use error::{Error, Result};
|
||||
use error::ParseError;
|
||||
use decode::Decoder;
|
||||
use httpmessage::HttpRequest;
|
||||
use payload::{Payload, PayloadSender};
|
||||
|
@ -53,7 +53,7 @@ impl Reader {
|
|||
}
|
||||
}
|
||||
|
||||
fn decode(&mut self) -> std::result::Result<Decoding, Error>
|
||||
fn decode(&mut self) -> std::result::Result<Decoding, ParseError>
|
||||
{
|
||||
if let Some(ref mut payload) = self.payload {
|
||||
if payload.tx.maybe_paused() {
|
||||
|
@ -69,7 +69,7 @@ impl Reader {
|
|||
return Ok(Decoding::Ready)
|
||||
},
|
||||
Ok(Async::NotReady) => return Ok(Decoding::NotReady),
|
||||
Err(_) => return Err(Error::Incomplete),
|
||||
Err(_) => return Err(ParseError::Incomplete),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -77,7 +77,7 @@ impl Reader {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse<T>(&mut self, io: &mut T) -> Poll<(HttpRequest, Payload), Error>
|
||||
pub fn parse<T>(&mut self, io: &mut T) -> Poll<(HttpRequest, Payload), ParseError>
|
||||
where T: AsyncRead
|
||||
{
|
||||
loop {
|
||||
|
@ -89,8 +89,7 @@ impl Reader {
|
|||
},
|
||||
Decoding::NotReady => {
|
||||
if 0 == try_ready!(self.read_from_io(io)) {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof, ParseEof).into());
|
||||
return Err(ParseError::Eof)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,8 +118,7 @@ impl Reader {
|
|||
match self.read_from_io(io) {
|
||||
Ok(Async::Ready(0)) => {
|
||||
trace!("parse eof");
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof, ParseEof).into());
|
||||
return Err(ParseError::Eof);
|
||||
}
|
||||
Ok(Async::Ready(_)) => {
|
||||
continue
|
||||
|
@ -141,13 +139,13 @@ impl Reader {
|
|||
None => {
|
||||
if self.read_buf.capacity() >= MAX_BUFFER_SIZE {
|
||||
debug!("MAX_BUFFER_SIZE reached, closing");
|
||||
return Err(Error::TooLarge);
|
||||
return Err(ParseError::TooLarge);
|
||||
}
|
||||
},
|
||||
}
|
||||
if 0 == try_ready!(self.read_from_io(io)) {
|
||||
trace!("parse eof");
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, ParseEof).into());
|
||||
return Err(ParseError::Eof);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,23 +175,9 @@ impl Reader {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseEof;
|
||||
|
||||
impl fmt::Display for ParseEof {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("parse eof")
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::error::Error for ParseEof {
|
||||
fn description(&self) -> &str {
|
||||
"parse eof"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn parse(buf: &mut BytesMut) -> Result<Option<(HttpRequest, Option<Decoder>)>> {
|
||||
pub fn parse(buf: &mut BytesMut) -> Result<Option<(HttpRequest, Option<Decoder>)>, ParseError>
|
||||
{
|
||||
if buf.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
@ -211,7 +195,8 @@ pub fn parse(buf: &mut BytesMut) -> Result<Option<(HttpRequest, Option<Decoder>)
|
|||
match try!(req.parse(buf)) {
|
||||
httparse::Status::Complete(len) => {
|
||||
trace!("Request.parse Complete({})", len);
|
||||
let method = Method::try_from(req.method.unwrap()).map_err(|_| Error::Method)?;
|
||||
let method = Method::try_from(req.method.unwrap())
|
||||
.map_err(|_| ParseError::Method)?;
|
||||
let path = req.path.unwrap();
|
||||
let bytes_ptr = buf.as_ref().as_ptr() as usize;
|
||||
let path_start = path.as_ptr() as usize - bytes_ptr;
|
||||
|
@ -235,7 +220,7 @@ pub fn parse(buf: &mut BytesMut) -> Result<Option<(HttpRequest, Option<Decoder>)
|
|||
let slice = buf.split_to(len).freeze();
|
||||
let path = slice.slice(path.0, path.1);
|
||||
// path was found to be utf8 by httparse
|
||||
let uri = Uri::from_shared(path).map_err(|_| Error::Uri)?;
|
||||
let uri = Uri::from_shared(path).map_err(|_| ParseError::Uri)?;
|
||||
|
||||
// convert headers
|
||||
let mut headers = HeaderMap::with_capacity(headers_len);
|
||||
|
@ -246,10 +231,10 @@ pub fn parse(buf: &mut BytesMut) -> Result<Option<(HttpRequest, Option<Decoder>)
|
|||
{
|
||||
headers.insert(name, value);
|
||||
} else {
|
||||
return Err(Error::Header)
|
||||
return Err(ParseError::Header)
|
||||
}
|
||||
} else {
|
||||
return Err(Error::Header)
|
||||
return Err(ParseError::Header)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,18 +248,18 @@ pub fn parse(buf: &mut BytesMut) -> Result<Option<(HttpRequest, Option<Decoder>)
|
|||
// Content-Length
|
||||
else if let Some(len) = msg.headers().get(header::CONTENT_LENGTH) {
|
||||
if chunked {
|
||||
return Err(Error::Header)
|
||||
return Err(ParseError::Header)
|
||||
}
|
||||
if let Ok(s) = len.to_str() {
|
||||
if let Ok(len) = s.parse::<u64>() {
|
||||
Some(Decoder::length(len))
|
||||
} else {
|
||||
debug!("illegal Content-Length: {:?}", len);
|
||||
return Err(Error::Header)
|
||||
return Err(ParseError::Header)
|
||||
}
|
||||
} else {
|
||||
debug!("illegal Content-Length: {:?}", len);
|
||||
return Err(Error::Header)
|
||||
return Err(ParseError::Header)
|
||||
}
|
||||
} else if chunked {
|
||||
Some(Decoder::chunked())
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::rc::Rc;
|
||||
use std::convert::From;
|
||||
use std::marker::PhantomData;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -109,7 +110,7 @@ impl<S: 'static> RouteHandler<S> for Resource<S> {
|
|||
|
||||
#[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))]
|
||||
enum ReplyItem<A> where A: Actor + Route {
|
||||
Message(HttpRequest, HttpResponse),
|
||||
Message(HttpResponse),
|
||||
Actor(A),
|
||||
}
|
||||
|
||||
|
@ -124,15 +125,15 @@ impl<A> Reply<A> where A: Actor + Route
|
|||
}
|
||||
|
||||
/// Send response
|
||||
pub fn reply<R: Into<HttpResponse>>(req: HttpRequest, response: R) -> Self {
|
||||
Reply(ReplyItem::Message(req, response.into()))
|
||||
pub fn reply<R: Into<HttpResponse>>(response: R) -> Self {
|
||||
Reply(ReplyItem::Message(response.into()))
|
||||
}
|
||||
|
||||
pub fn into(self, mut ctx: HttpContext<A>) -> Task where A: Actor<Context=HttpContext<A>>
|
||||
{
|
||||
match self.0 {
|
||||
ReplyItem::Message(req, msg) => {
|
||||
Task::reply(req, msg)
|
||||
ReplyItem::Message(msg) => {
|
||||
Task::reply(msg)
|
||||
},
|
||||
ReplyItem::Actor(act) => {
|
||||
ctx.set_actor(act);
|
||||
|
@ -141,3 +142,32 @@ impl<A> Reply<A> where A: Actor + Route
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, T> From<T> for Reply<A>
|
||||
where T: Into<HttpResponse>, A: Actor + Route
|
||||
{
|
||||
fn from(item: T) -> Self {
|
||||
Reply::reply(item)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="nightly")]
|
||||
use std::ops::Try;
|
||||
|
||||
#[cfg(feature="nightly")]
|
||||
impl<A> Try for Reply<A> where A: Actor + Route {
|
||||
type Ok = HttpResponse;
|
||||
type Error = HttpResponse;
|
||||
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error> {
|
||||
panic!("Reply -> Result conversion is not supported")
|
||||
}
|
||||
|
||||
fn from_error(v: Self::Error) -> Self {
|
||||
Reply::reply(v)
|
||||
}
|
||||
|
||||
fn from_ok(v: Self::Ok) -> Self {
|
||||
Reply::reply(v)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use httpmessage::{HttpRequest, HttpResponse};
|
|||
#[derive(Debug)]
|
||||
#[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))]
|
||||
pub enum Frame {
|
||||
Message(HttpRequest, HttpResponse),
|
||||
Message(HttpResponse),
|
||||
Payload(Option<Bytes>),
|
||||
}
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ impl Router {
|
|||
return app.handle(req, payload)
|
||||
}
|
||||
}
|
||||
Task::reply(req, HTTPNotFound.response())
|
||||
Task::reply(HTTPNotFound.response())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{io, net};
|
||||
use std::{io, net, mem};
|
||||
use std::rc::Rc;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
|
@ -6,7 +6,7 @@ use actix::dev::*;
|
|||
use futures::{Future, Poll, Async};
|
||||
use tokio_core::net::{TcpListener, TcpStream};
|
||||
|
||||
use task::Task;
|
||||
use task::{Task, RequestInfo};
|
||||
use reader::Reader;
|
||||
use router::{Router, RoutingMap};
|
||||
|
||||
|
@ -55,6 +55,7 @@ impl Handler<(TcpStream, net::SocketAddr), io::Error> for HttpServer {
|
|||
addr: msg.1,
|
||||
stream: msg.0,
|
||||
reader: Reader::new(),
|
||||
error: false,
|
||||
items: VecDeque::new(),
|
||||
inactive: Vec::new(),
|
||||
});
|
||||
|
@ -65,6 +66,7 @@ impl Handler<(TcpStream, net::SocketAddr), io::Error> for HttpServer {
|
|||
|
||||
struct Entry {
|
||||
task: Task,
|
||||
req: RequestInfo,
|
||||
eof: bool,
|
||||
error: bool,
|
||||
finished: bool,
|
||||
|
@ -76,6 +78,7 @@ pub struct HttpChannel {
|
|||
addr: net::SocketAddr,
|
||||
stream: TcpStream,
|
||||
reader: Reader,
|
||||
error: bool,
|
||||
items: VecDeque<Entry>,
|
||||
inactive: Vec<Entry>,
|
||||
}
|
||||
|
@ -97,7 +100,13 @@ impl Future for HttpChannel {
|
|||
if self.items[idx].error {
|
||||
return Err(())
|
||||
}
|
||||
match self.items[idx].task.poll_io(&mut self.stream) {
|
||||
|
||||
// this is anoying
|
||||
let req: &RequestInfo = unsafe {
|
||||
mem::transmute(&self.items[idx].req)
|
||||
};
|
||||
match self.items[idx].task.poll_io(&mut self.stream, req)
|
||||
{
|
||||
Ok(Async::Ready(val)) => {
|
||||
let mut item = self.items.pop_front().unwrap();
|
||||
if !val {
|
||||
|
@ -107,7 +116,11 @@ impl Future for HttpChannel {
|
|||
continue
|
||||
},
|
||||
Ok(Async::NotReady) => (),
|
||||
Err(_) => return Err(()),
|
||||
Err(_) => {
|
||||
// it is not possible to recover from error
|
||||
// during task handling, so just drop connection
|
||||
return Err(())
|
||||
}
|
||||
}
|
||||
} else if !self.items[idx].finished {
|
||||
match self.items[idx].task.poll() {
|
||||
|
@ -121,19 +134,32 @@ impl Future for HttpChannel {
|
|||
idx += 1;
|
||||
}
|
||||
|
||||
// check for parse error
|
||||
if self.items.is_empty() && self.error {
|
||||
|
||||
}
|
||||
|
||||
// read incoming data
|
||||
match self.reader.parse(&mut self.stream) {
|
||||
Ok(Async::Ready((req, payload))) => {
|
||||
self.items.push_back(
|
||||
Entry {task: self.router.call(req, payload),
|
||||
eof: false,
|
||||
error: false,
|
||||
finished: false});
|
||||
},
|
||||
Ok(Async::NotReady) =>
|
||||
return Ok(Async::NotReady),
|
||||
Err(_) =>
|
||||
return Err(()),
|
||||
if !self.error {
|
||||
match self.reader.parse(&mut self.stream) {
|
||||
Ok(Async::Ready((req, payload))) => {
|
||||
let info = RequestInfo::new(&req);
|
||||
self.items.push_back(
|
||||
Entry {task: self.router.call(req, payload),
|
||||
req: info,
|
||||
eof: false,
|
||||
error: false,
|
||||
finished: false});
|
||||
}
|
||||
Ok(Async::NotReady) =>
|
||||
return Ok(Async::NotReady),
|
||||
Err(err) => return Err(())
|
||||
//self.items.push_back(
|
||||
// Entry {task: Task::reply(err),
|
||||
// eof: false,
|
||||
// error: false,
|
||||
// finished: false})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
36
src/task.rs
36
src/task.rs
|
@ -44,6 +44,20 @@ impl TaskIOState {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RequestInfo {
|
||||
version: Version,
|
||||
keep_alive: bool,
|
||||
}
|
||||
|
||||
impl RequestInfo {
|
||||
pub fn new(req: &HttpRequest) -> Self {
|
||||
RequestInfo {
|
||||
version: req.version(),
|
||||
keep_alive: req.keep_alive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Task {
|
||||
state: TaskRunningState,
|
||||
iostate: TaskIOState,
|
||||
|
@ -56,9 +70,9 @@ pub struct Task {
|
|||
|
||||
impl Task {
|
||||
|
||||
pub fn reply<R: Into<HttpResponse>>(req: HttpRequest, response: R) -> Self {
|
||||
pub fn reply<R: Into<HttpResponse>>(response: R) -> Self {
|
||||
let mut frames = VecDeque::new();
|
||||
frames.push_back(Frame::Message(req, response.into()));
|
||||
frames.push_back(Frame::Message(response.into()));
|
||||
frames.push_back(Frame::Payload(None));
|
||||
|
||||
Task {
|
||||
|
@ -86,13 +100,13 @@ impl Task {
|
|||
}
|
||||
}
|
||||
|
||||
fn prepare(&mut self, req: HttpRequest, mut msg: HttpResponse)
|
||||
fn prepare(&mut self, req: &RequestInfo, mut msg: HttpResponse)
|
||||
{
|
||||
trace!("Prepare message status={:?}", msg.status);
|
||||
|
||||
let mut extra = 0;
|
||||
let body = msg.replace_body(Body::Empty);
|
||||
let version = msg.version().unwrap_or_else(|| req.version());
|
||||
let version = msg.version().unwrap_or_else(|| req.version);
|
||||
|
||||
match body {
|
||||
Body::Empty => {
|
||||
|
@ -124,7 +138,7 @@ impl Task {
|
|||
Body::Streaming => {
|
||||
if msg.chunked() {
|
||||
if version < Version::HTTP_11 {
|
||||
error!("Chunked transfer encoding is forbidden for {:?}", msg.version);
|
||||
error!("Chunked transfer encoding is forbidden for {:?}", version);
|
||||
}
|
||||
msg.headers.remove(CONTENT_LENGTH);
|
||||
msg.headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
|
||||
|
@ -144,7 +158,7 @@ impl Task {
|
|||
msg.headers.insert(CONNECTION, HeaderValue::from_static("upgrade"));
|
||||
}
|
||||
// keep-alive
|
||||
else if msg.keep_alive().unwrap_or_else(|| req.keep_alive()) {
|
||||
else if msg.keep_alive().unwrap_or_else(|| req.keep_alive) {
|
||||
if version < Version::HTTP_11 {
|
||||
msg.headers.insert(CONNECTION, HeaderValue::from_static("keep-alive"));
|
||||
}
|
||||
|
@ -159,7 +173,7 @@ impl Task {
|
|||
if version == Version::HTTP_11 && msg.status == StatusCode::OK {
|
||||
self.buffer.extend(b"HTTP/1.1 200 OK\r\n");
|
||||
} else {
|
||||
let _ = write!(self.buffer, "{:?} {}\r\n", msg.version, msg.status);
|
||||
let _ = write!(self.buffer, "{:?} {}\r\n", version, msg.status);
|
||||
}
|
||||
for (key, value) in &msg.headers {
|
||||
let t: &[u8] = key.as_ref();
|
||||
|
@ -192,7 +206,7 @@ impl Task {
|
|||
msg.replace_body(body);
|
||||
}
|
||||
|
||||
pub(crate) fn poll_io(&mut self, io: &mut TcpStream) -> Poll<bool, ()> {
|
||||
pub(crate) fn poll_io(&mut self, io: &mut TcpStream, info: &RequestInfo) -> Poll<bool, ()> {
|
||||
trace!("POLL-IO frames:{:?}", self.frames.len());
|
||||
// response is completed
|
||||
if self.frames.is_empty() && self.iostate.is_done() {
|
||||
|
@ -213,8 +227,8 @@ impl Task {
|
|||
while let Some(frame) = self.frames.pop_front() {
|
||||
trace!("IO Frame: {:?}", frame);
|
||||
match frame {
|
||||
Frame::Message(request, response) => {
|
||||
self.prepare(request, response);
|
||||
Frame::Message(response) => {
|
||||
self.prepare(info, response);
|
||||
}
|
||||
Frame::Payload(chunk) => {
|
||||
match chunk {
|
||||
|
@ -277,7 +291,7 @@ impl Future for Task {
|
|||
match stream.poll() {
|
||||
Ok(Async::Ready(Some(frame))) => {
|
||||
match frame {
|
||||
Frame::Message(_, ref msg) => {
|
||||
Frame::Message(ref msg) => {
|
||||
if self.iostate != TaskIOState::ReadingMessage {
|
||||
error!("Non expected frame {:?}", frame);
|
||||
return Err(())
|
||||
|
|
Loading…
Reference in a new issue