From c435f16170b8f39a1fb311f5a7cdbcd382580e3c Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 23 Oct 2017 23:25:32 -0700 Subject: [PATCH] refactory response body --- CHANGES.md | 13 ++++++ examples/basic.rs | 4 +- examples/state.rs | 2 +- src/body.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 7 +-- src/httpcodes.rs | 7 +-- src/httpresponse.rs | 32 +------------ src/lib.rs | 4 +- src/route.rs | 8 ++-- src/staticfiles.rs | 6 +-- src/task.rs | 5 +- src/ws.rs | 3 +- 12 files changed, 148 insertions(+), 51 deletions(-) create mode 100644 CHANGES.md create mode 100644 src/body.rs diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 00000000..57f4090c --- /dev/null +++ b/CHANGES.md @@ -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 diff --git a/examples/basic.rs b/examples/basic.rs index 0614f621..91ede065 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -17,8 +17,8 @@ fn with_param(req: &mut HttpRequest, _payload: Payload, state: &()) -> HttpRespo HttpResponse::builder(StatusCode::OK) .content_type("test/plain") - .body(Body::Binary( - format!("Hello {}!", req.match_info().get("name").unwrap()).into())).unwrap() + .body(format!("Hello {}!", req.match_info().get("name").unwrap())) + .unwrap() } fn main() { diff --git a/examples/state.rs b/examples/state.rs index 7fa6b8c2..5b8f6a4c 100644 --- a/examples/state.rs +++ b/examples/state.rs @@ -19,7 +19,7 @@ fn index(req: &mut HttpRequest, _: Payload, state: &AppState) -> HttpResponse { println!("{:?}", req); state.counter.set(state.counter.get() + 1); 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, diff --git a/src/body.rs b/src/body.rs new file mode 100644 index 00000000..0a8b6b7f --- /dev/null +++ b/src/body.rs @@ -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), + /// Shared bytes body + #[doc(hidden)] + ArcSharedBytes(Arc), +} + +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> for Body { + fn from(vec: Vec) -> Body { + Body::Binary(BinaryBody::Bytes(Bytes::from(vec))) + } +} + +impl From for Body { + fn from(s: String) -> Body { + Body::Binary(BinaryBody::Bytes(Bytes::from(s))) + } +} + +impl From> for Body { + fn from(body: Rc) -> Body { + Body::Binary(BinaryBody::SharedBytes(body)) + } +} + +impl From> for Body { + fn from(body: Arc) -> 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(), + } + } +} diff --git a/src/error.rs b/src/error.rs index 1c94abd8..25223a4e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,7 +11,8 @@ use http::{StatusCode, Error as HttpError}; use HttpRangeParseError; use multipart::MultipartError; -use httpresponse::{Body, HttpResponse}; +use body::Body; +use httpresponse::{HttpResponse}; /// A set of errors that can occur during parsing HTTP streams. @@ -139,8 +140,8 @@ impl From for HttpResponse { /// Return `BadRequest` for `HttpRangeParseError` impl From for HttpResponse { fn from(_: HttpRangeParseError) -> Self { - HttpResponse::new(StatusCode::BAD_REQUEST, - Body::Binary("Invalid Range header provided".into())) + HttpResponse::new( + StatusCode::BAD_REQUEST, Body::from("Invalid Range header provided")) } } diff --git a/src/httpcodes.rs b/src/httpcodes.rs index 87299aaf..5d8a3f25 100644 --- a/src/httpcodes.rs +++ b/src/httpcodes.rs @@ -3,11 +3,12 @@ use std::rc::Rc; use http::StatusCode; +use body::Body; use task::Task; use route::RouteHandler; use payload::Payload; use httprequest::HttpRequest; -use httpresponse::{Body, HttpResponse, HttpResponseBuilder}; +use httpresponse::{HttpResponse, HttpResponseBuilder}; pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK); pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED); @@ -64,8 +65,8 @@ impl StaticResponse { resp.set_reason(reason); resp } - pub fn with_body(self, body: Body) -> HttpResponse { - HttpResponse::new(self.0, body) + pub fn with_body>(self, body: B) -> HttpResponse { + HttpResponse::new(self.0, body.into()) } } diff --git a/src/httpresponse.rs b/src/httpresponse.rs index e7569563..dbb82ffe 100644 --- a/src/httpresponse.rs +++ b/src/httpresponse.rs @@ -4,11 +4,11 @@ use std::error::Error as Error; use std::convert::Into; use cookie::CookieJar; -use bytes::Bytes; use http::{StatusCode, Version, HeaderMap, HttpTryFrom, Error as HttpError}; use http::header::{self, HeaderName, HeaderValue}; use Cookie; +use body::Body; /// Represents various types of connection @@ -22,32 +22,6 @@ pub enum ConnectionType { 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)] /// An HTTP Response pub struct HttpResponse { @@ -90,14 +64,12 @@ impl HttpResponse { /// Constructs a response from error #[inline] pub fn from_error(status: StatusCode, error: E) -> HttpResponse { - let body = Body::Binary(error.description().into()); - HttpResponse { version: None, headers: Default::default(), status: status, reason: None, - body: body, + body: Body::from_slice(error.description().as_ref()), chunked: false, // compression: None, connection_type: None, diff --git a/src/lib.rs b/src/lib.rs index 22d1ea1d..6c2d3eb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ extern crate url; extern crate actix; mod application; +mod body; mod context; mod error; mod date; @@ -45,9 +46,10 @@ pub mod dev; pub mod httpcodes; pub mod multipart; pub use error::ParseError; +pub use body::{Body, BinaryBody}; pub use application::{Application, ApplicationBuilder, Middleware}; pub use httprequest::{HttpRequest, UrlEncoded}; -pub use httpresponse::{Body, HttpResponse, HttpResponseBuilder}; +pub use httpresponse::{HttpResponse, HttpResponseBuilder}; pub use payload::{Payload, PayloadItem, PayloadError}; pub use route::{Route, RouteFactory, RouteHandler, RouteResult}; pub use resource::{Reply, Resource}; diff --git a/src/route.rs b/src/route.rs index 787254ed..3a7cc204 100644 --- a/src/route.rs +++ b/src/route.rs @@ -12,7 +12,7 @@ use context::HttpContext; use resource::Reply; use payload::Payload; use httprequest::HttpRequest; -use httpresponse::{Body, HttpResponse}; +use httpresponse::HttpResponse; use httpcodes::HTTPExpectationFailed; #[doc(hidden)] @@ -55,12 +55,10 @@ pub trait Route: Actor { ctx.write("HTTP/1.1 100 Continue\r\n\r\n"); Ok(()) } else { - Err(HTTPExpectationFailed.with_body( - Body::Binary("Unknown Expect".into()))) + Err(HTTPExpectationFailed.with_body("Unknown Expect")) } } else { - Err(HTTPExpectationFailed.with_body( - Body::Binary("Unknown Expect".into()))) + Err(HTTPExpectationFailed.with_body("Unknown Expect")) } } else { Ok(()) diff --git a/src/staticfiles.rs b/src/staticfiles.rs index 6ea1d247..f963d969 100644 --- a/src/staticfiles.rs +++ b/src/staticfiles.rs @@ -14,7 +14,7 @@ use route::RouteHandler; use payload::Payload; use mime_guess::get_mime_type; use httprequest::HttpRequest; -use httpresponse::{Body, HttpResponse}; +use httpresponse::HttpResponse; use httpcodes::{HTTPOk, HTTPNotFound, HTTPForbidden, HTTPInternalServerError}; /// Static files handling @@ -111,7 +111,7 @@ impl StaticFiles { Ok( HTTPOk.builder() .content_type("text/html; charset=utf-8") - .body(Body::Binary(html.into())).unwrap() + .body(html).unwrap() ) } @@ -188,7 +188,7 @@ impl RouteHandler for StaticFiles { Ok(mut file) => { let mut data = Vec::new(); let _ = file.read_to_end(&mut data); - Task::reply(resp.body(Body::Binary(data.into())).unwrap()) + Task::reply(resp.body(data).unwrap()) }, Err(err) => { Task::reply(HTTPInternalServerError) diff --git a/src/task.rs b/src/task.rs index 782d710f..29a570ff 100644 --- a/src/task.rs +++ b/src/task.rs @@ -11,10 +11,11 @@ use futures::{Async, Future, Poll, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; use date; +use body::Body; use route::Frame; use application::Middleware; use httprequest::HttpRequest; -use httpresponse::{Body, HttpResponse}; +use httpresponse::HttpResponse; type FrameStream = Stream; const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific @@ -219,7 +220,7 @@ impl Task { self.buffer.extend(b"\r\n"); if let Body::Binary(ref bytes) = body { - self.buffer.extend(bytes); + self.buffer.extend_from_slice(bytes.as_ref()); self.prepared = Some(msg); return } diff --git a/src/ws.rs b/src/ws.rs index d278e14e..345bf1ec 100644 --- a/src/ws.rs +++ b/src/ws.rs @@ -67,12 +67,13 @@ use futures::{Async, Poll, Stream}; use actix::{Actor, ResponseType}; +use body::Body; use context::HttpContext; use route::Route; use payload::Payload; use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed}; use httprequest::HttpRequest; -use httpresponse::{Body, ConnectionType, HttpResponse}; +use httpresponse::{ConnectionType, HttpResponse}; use wsframe; use wsproto::*;