1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-12-23 08:36:34 +00:00

refactory response body

This commit is contained in:
Nikolay Kim 2017-10-23 23:25:32 -07:00
parent 779b480663
commit c435f16170
12 changed files with 148 additions and 51 deletions

13
CHANGES.md Normal file
View file

@ -0,0 +1,13 @@
# CHANGES
## 0.2.0 (2017-10-xx)
* Refactor response `Body`
* Refactor `RouteRecognizer` usability
## 0.1.0 (2017-10-23)
* First release

View file

@ -17,8 +17,8 @@ fn with_param(req: &mut HttpRequest, _payload: Payload, state: &()) -> HttpRespo
HttpResponse::builder(StatusCode::OK) HttpResponse::builder(StatusCode::OK)
.content_type("test/plain") .content_type("test/plain")
.body(Body::Binary( .body(format!("Hello {}!", req.match_info().get("name").unwrap()))
format!("Hello {}!", req.match_info().get("name").unwrap()).into())).unwrap() .unwrap()
} }
fn main() { fn main() {

View file

@ -19,7 +19,7 @@ fn index(req: &mut HttpRequest, _: Payload, state: &AppState) -> HttpResponse {
println!("{:?}", req); println!("{:?}", req);
state.counter.set(state.counter.get() + 1); state.counter.set(state.counter.get() + 1);
httpcodes::HTTPOk.with_body( httpcodes::HTTPOk.with_body(
Body::Binary(format!("Num of requests: {}", state.counter.get()).into())) format!("Num of requests: {}", state.counter.get()))
} }
/// `MyWebSocket` counts how many messages it receives from peer, /// `MyWebSocket` counts how many messages it receives from peer,

108
src/body.rs Normal file
View file

@ -0,0 +1,108 @@
use std::rc::Rc;
use std::sync::Arc;
use bytes::Bytes;
/// Represents various types of http message body.
#[derive(Debug)]
pub enum Body {
/// Empty response. `Content-Length` header is set to `0`
Empty,
/// Specific response body.
Binary(BinaryBody),
/// Streaming response body with specified length.
Length(u64),
/// Unspecified streaming response. Developer is responsible for setting
/// right `Content-Length` or `Transfer-Encoding` headers.
Streaming,
/// Upgrade connection.
Upgrade,
}
/// Represents various types of binary body.
/// `Content-Length` header is set to length of the body.
#[derive(Debug)]
pub enum BinaryBody {
/// Bytes body
Bytes(Bytes),
/// Static slice
Slice(&'static [u8]),
/// Shared bytes body
SharedBytes(Rc<Bytes>),
/// Shared bytes body
#[doc(hidden)]
ArcSharedBytes(Arc<Bytes>),
}
impl Body {
/// Does this body have payload.
pub fn has_body(&self) -> bool {
match *self {
Body::Length(_) | Body::Streaming => true,
_ => false
}
}
/// Create body from static string
pub fn from_slice<'a>(s: &'a [u8]) -> Body {
Body::Binary(BinaryBody::Bytes(Bytes::from(s)))
}
}
impl From<&'static str> for Body {
fn from(s: &'static str) -> Body {
Body::Binary(BinaryBody::Slice(s.as_ref()))
}
}
impl From<&'static [u8]> for Body {
fn from(s: &'static [u8]) -> Body {
Body::Binary(BinaryBody::Slice(s))
}
}
impl From<Vec<u8>> for Body {
fn from(vec: Vec<u8>) -> Body {
Body::Binary(BinaryBody::Bytes(Bytes::from(vec)))
}
}
impl From<String> for Body {
fn from(s: String) -> Body {
Body::Binary(BinaryBody::Bytes(Bytes::from(s)))
}
}
impl From<Rc<Bytes>> for Body {
fn from(body: Rc<Bytes>) -> Body {
Body::Binary(BinaryBody::SharedBytes(body))
}
}
impl From<Arc<Bytes>> for Body {
fn from(body: Arc<Bytes>) -> Body {
Body::Binary(BinaryBody::ArcSharedBytes(body))
}
}
impl BinaryBody {
pub fn len(&self) -> usize {
match self {
&BinaryBody::Bytes(ref bytes) => bytes.len(),
&BinaryBody::Slice(slice) => slice.len(),
&BinaryBody::SharedBytes(ref bytes) => bytes.len(),
&BinaryBody::ArcSharedBytes(ref bytes) => bytes.len(),
}
}
}
impl AsRef<[u8]> for BinaryBody {
fn as_ref(&self) -> &[u8] {
match self {
&BinaryBody::Bytes(ref bytes) => bytes.as_ref(),
&BinaryBody::Slice(slice) => slice,
&BinaryBody::SharedBytes(ref bytes) => bytes.as_ref(),
&BinaryBody::ArcSharedBytes(ref bytes) => bytes.as_ref(),
}
}
}

View file

@ -11,7 +11,8 @@ use http::{StatusCode, Error as HttpError};
use HttpRangeParseError; use HttpRangeParseError;
use multipart::MultipartError; use multipart::MultipartError;
use httpresponse::{Body, HttpResponse}; use body::Body;
use httpresponse::{HttpResponse};
/// A set of errors that can occur during parsing HTTP streams. /// A set of errors that can occur during parsing HTTP streams.
@ -139,8 +140,8 @@ impl From<MultipartError> for HttpResponse {
/// Return `BadRequest` for `HttpRangeParseError` /// Return `BadRequest` for `HttpRangeParseError`
impl From<HttpRangeParseError> for HttpResponse { impl From<HttpRangeParseError> for HttpResponse {
fn from(_: HttpRangeParseError) -> Self { fn from(_: HttpRangeParseError) -> Self {
HttpResponse::new(StatusCode::BAD_REQUEST, HttpResponse::new(
Body::Binary("Invalid Range header provided".into())) StatusCode::BAD_REQUEST, Body::from("Invalid Range header provided"))
} }
} }

View file

@ -3,11 +3,12 @@
use std::rc::Rc; use std::rc::Rc;
use http::StatusCode; use http::StatusCode;
use body::Body;
use task::Task; use task::Task;
use route::RouteHandler; use route::RouteHandler;
use payload::Payload; use payload::Payload;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::{Body, HttpResponse, HttpResponseBuilder}; use httpresponse::{HttpResponse, HttpResponseBuilder};
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK); pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED); pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
@ -64,8 +65,8 @@ impl StaticResponse {
resp.set_reason(reason); resp.set_reason(reason);
resp resp
} }
pub fn with_body(self, body: Body) -> HttpResponse { pub fn with_body<B: Into<Body>>(self, body: B) -> HttpResponse {
HttpResponse::new(self.0, body) HttpResponse::new(self.0, body.into())
} }
} }

View file

@ -4,11 +4,11 @@ use std::error::Error as Error;
use std::convert::Into; use std::convert::Into;
use cookie::CookieJar; use cookie::CookieJar;
use bytes::Bytes;
use http::{StatusCode, Version, HeaderMap, HttpTryFrom, Error as HttpError}; use http::{StatusCode, Version, HeaderMap, HttpTryFrom, Error as HttpError};
use http::header::{self, HeaderName, HeaderValue}; use http::header::{self, HeaderName, HeaderValue};
use Cookie; use Cookie;
use body::Body;
/// Represents various types of connection /// Represents various types of connection
@ -22,32 +22,6 @@ pub enum ConnectionType {
Upgrade, Upgrade,
} }
/// Represents various types of http message body.
#[derive(Debug)]
pub enum Body {
/// Empty response. `Content-Length` header is set to `0`
Empty,
/// Specific response body. `Content-Length` header is set to length of bytes.
Binary(Bytes),
/// Streaming response body with specified length.
Length(u64),
/// Unspecified streaming response. Developer is responsible for setting
/// right `Content-Length` or `Transfer-Encoding` headers.
Streaming,
/// Upgrade connection.
Upgrade,
}
impl Body {
/// Does this body have payload.
pub fn has_body(&self) -> bool {
match *self {
Body::Length(_) | Body::Streaming => true,
_ => false
}
}
}
#[derive(Debug)] #[derive(Debug)]
/// An HTTP Response /// An HTTP Response
pub struct HttpResponse { pub struct HttpResponse {
@ -90,14 +64,12 @@ impl HttpResponse {
/// Constructs a response from error /// Constructs a response from error
#[inline] #[inline]
pub fn from_error<E: Error + 'static>(status: StatusCode, error: E) -> HttpResponse { pub fn from_error<E: Error + 'static>(status: StatusCode, error: E) -> HttpResponse {
let body = Body::Binary(error.description().into());
HttpResponse { HttpResponse {
version: None, version: None,
headers: Default::default(), headers: Default::default(),
status: status, status: status,
reason: None, reason: None,
body: body, body: Body::from_slice(error.description().as_ref()),
chunked: false, chunked: false,
// compression: None, // compression: None,
connection_type: None, connection_type: None,

View file

@ -22,6 +22,7 @@ extern crate url;
extern crate actix; extern crate actix;
mod application; mod application;
mod body;
mod context; mod context;
mod error; mod error;
mod date; mod date;
@ -45,9 +46,10 @@ pub mod dev;
pub mod httpcodes; pub mod httpcodes;
pub mod multipart; pub mod multipart;
pub use error::ParseError; pub use error::ParseError;
pub use body::{Body, BinaryBody};
pub use application::{Application, ApplicationBuilder, Middleware}; pub use application::{Application, ApplicationBuilder, Middleware};
pub use httprequest::{HttpRequest, UrlEncoded}; pub use httprequest::{HttpRequest, UrlEncoded};
pub use httpresponse::{Body, HttpResponse, HttpResponseBuilder}; pub use httpresponse::{HttpResponse, HttpResponseBuilder};
pub use payload::{Payload, PayloadItem, PayloadError}; pub use payload::{Payload, PayloadItem, PayloadError};
pub use route::{Route, RouteFactory, RouteHandler, RouteResult}; pub use route::{Route, RouteFactory, RouteHandler, RouteResult};
pub use resource::{Reply, Resource}; pub use resource::{Reply, Resource};

View file

@ -12,7 +12,7 @@ use context::HttpContext;
use resource::Reply; use resource::Reply;
use payload::Payload; use payload::Payload;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::{Body, HttpResponse}; use httpresponse::HttpResponse;
use httpcodes::HTTPExpectationFailed; use httpcodes::HTTPExpectationFailed;
#[doc(hidden)] #[doc(hidden)]
@ -55,12 +55,10 @@ pub trait Route: Actor {
ctx.write("HTTP/1.1 100 Continue\r\n\r\n"); ctx.write("HTTP/1.1 100 Continue\r\n\r\n");
Ok(()) Ok(())
} else { } else {
Err(HTTPExpectationFailed.with_body( Err(HTTPExpectationFailed.with_body("Unknown Expect"))
Body::Binary("Unknown Expect".into())))
} }
} else { } else {
Err(HTTPExpectationFailed.with_body( Err(HTTPExpectationFailed.with_body("Unknown Expect"))
Body::Binary("Unknown Expect".into())))
} }
} else { } else {
Ok(()) Ok(())

View file

@ -14,7 +14,7 @@ use route::RouteHandler;
use payload::Payload; use payload::Payload;
use mime_guess::get_mime_type; use mime_guess::get_mime_type;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::{Body, HttpResponse}; use httpresponse::HttpResponse;
use httpcodes::{HTTPOk, HTTPNotFound, HTTPForbidden, HTTPInternalServerError}; use httpcodes::{HTTPOk, HTTPNotFound, HTTPForbidden, HTTPInternalServerError};
/// Static files handling /// Static files handling
@ -111,7 +111,7 @@ impl StaticFiles {
Ok( Ok(
HTTPOk.builder() HTTPOk.builder()
.content_type("text/html; charset=utf-8") .content_type("text/html; charset=utf-8")
.body(Body::Binary(html.into())).unwrap() .body(html).unwrap()
) )
} }
@ -188,7 +188,7 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
Ok(mut file) => { Ok(mut file) => {
let mut data = Vec::new(); let mut data = Vec::new();
let _ = file.read_to_end(&mut data); let _ = file.read_to_end(&mut data);
Task::reply(resp.body(Body::Binary(data.into())).unwrap()) Task::reply(resp.body(data).unwrap())
}, },
Err(err) => { Err(err) => {
Task::reply(HTTPInternalServerError) Task::reply(HTTPInternalServerError)

View file

@ -11,10 +11,11 @@ use futures::{Async, Future, Poll, Stream};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use date; use date;
use body::Body;
use route::Frame; use route::Frame;
use application::Middleware; use application::Middleware;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::{Body, HttpResponse}; use httpresponse::HttpResponse;
type FrameStream = Stream<Item=Frame, Error=io::Error>; type FrameStream = Stream<Item=Frame, Error=io::Error>;
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
@ -219,7 +220,7 @@ impl Task {
self.buffer.extend(b"\r\n"); self.buffer.extend(b"\r\n");
if let Body::Binary(ref bytes) = body { if let Body::Binary(ref bytes) = body {
self.buffer.extend(bytes); self.buffer.extend_from_slice(bytes.as_ref());
self.prepared = Some(msg); self.prepared = Some(msg);
return return
} }

View file

@ -67,12 +67,13 @@ use futures::{Async, Poll, Stream};
use actix::{Actor, ResponseType}; use actix::{Actor, ResponseType};
use body::Body;
use context::HttpContext; use context::HttpContext;
use route::Route; use route::Route;
use payload::Payload; use payload::Payload;
use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed}; use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed};
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::{Body, ConnectionType, HttpResponse}; use httpresponse::{ConnectionType, HttpResponse};
use wsframe; use wsframe;
use wsproto::*; use wsproto::*;