diff --git a/.travis.yml b/.travis.yml index 640aa1b92..76e1a8c5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,6 +59,7 @@ script: cd examples/multipart && cargo check && cd ../.. cd examples/json && cargo check && cd ../.. cd examples/juniper && cargo check && cd ../.. + cd examples/protobuf && cargo check && cd ../.. cd examples/state && cargo check && cd ../.. cd examples/template_tera && cargo check && cd ../.. cd examples/diesel && cargo check && cd ../.. @@ -77,7 +78,7 @@ script: after_success: - | if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_RUST_VERSION" == "nightly" ]]; then - cargo doc --features "alpn, tls" --no-deps && + cargo doc --features "alpn, tls, protobuf" --no-deps && echo "" > target/doc/index.html && cargo install mdbook && cd guide && mdbook build -d ../target/doc/guide && cd .. && diff --git a/CHANGES.md b/CHANGES.md index 6a08119ff..8ea018081 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,8 +2,12 @@ ## 0.4.6 (2018-03-xx) +* Add experimental protobuf support + * Fix client cookie handling +* Optimize websockets stream support + ## 0.4.5 (2018-03-07) diff --git a/Cargo.toml b/Cargo.toml index dd22754d7..59dd89f7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,9 @@ alpn = ["openssl", "openssl/v102", "openssl/v110", "tokio-openssl"] # sessions session = ["cookie/secure"] +# protobuf +protobuf = ["prost"] + [dependencies] actix = "^0.5.2" @@ -60,7 +63,6 @@ rand = "0.4" regex = "0.2" serde = "1.0" serde_json = "1.0" -prost = "^0.2" sha1 = "0.6" smallvec = "0.6" time = "0.1" @@ -88,6 +90,9 @@ tokio-tls = { version="0.1", optional = true } openssl = { version="0.10", optional = true } tokio-openssl = { version="0.2", optional = true } +# protobuf +prost = { version="^0.2", optional = true } + [dev-dependencies] env_logger = "0.5" skeptic = "0.13" diff --git a/examples/protobuf/Cargo.toml b/examples/protobuf/Cargo.toml index 75776ce5a..e5035ec0a 100644 --- a/examples/protobuf/Cargo.toml +++ b/examples/protobuf/Cargo.toml @@ -12,4 +12,4 @@ prost = "0.2.0" prost-derive = "0.2.0" actix = "0.5" -actix-web = { path="../../" } \ No newline at end of file +actix-web = { path="../../", features=["protobuf"] } diff --git a/examples/protobuf/src/main.rs b/examples/protobuf/src/main.rs index ff411fff8..a7fd2cbee 100644 --- a/examples/protobuf/src/main.rs +++ b/examples/protobuf/src/main.rs @@ -8,6 +8,7 @@ extern crate prost; extern crate prost_derive; use actix_web::*; +use actix_web::ProtoBufBody; use futures::Future; @@ -22,7 +23,7 @@ pub struct MyObj { /// This handler uses `HttpRequest::json()` for loading serde json object. fn index(req: HttpRequest) -> Box> { - req.protobuf() + ProtoBufBody::new(req) .from_err() // convert all errors into `Error` .and_then(|val: MyObj| { println!("model: {:?}", val); diff --git a/src/error.rs b/src/error.rs index 3c3a088b7..40ecf7045 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,8 +15,6 @@ use http::{header, StatusCode, Error as HttpError}; use http::uri::InvalidUriBytes; use http_range::HttpRangeParseError; use serde_json::error::Error as JsonError; -use prost::EncodeError as ProtoBufEncodeError; -use prost::DecodeError as ProtoBufDecodeError; pub use url::ParseError as UrlParseError; // re-exports @@ -109,10 +107,6 @@ impl From for Error { /// `InternalServerError` for `JsonError` impl ResponseError for JsonError {} -/// `InternalServerError` for `ProtoBufEncodeError` `ProtoBufDecodeError` -impl ResponseError for ProtoBufEncodeError {} -impl ResponseError for ProtoBufDecodeError {} - /// `InternalServerError` for `UrlParseError` impl ResponseError for UrlParseError {} @@ -456,44 +450,6 @@ impl From for JsonPayloadError { } } -#[derive(Fail, Debug)] -pub enum ProtoBufPayloadError { - /// Payload size is bigger than 256k - #[fail(display="Payload size is bigger than 256k")] - Overflow, - /// Content type error - #[fail(display="Content type error")] - ContentType, - /// Deserialize error - #[fail(display="Json deserialize error: {}", _0)] - Deserialize(#[cause] ProtoBufDecodeError), - /// Payload error - #[fail(display="Error that occur during reading payload: {}", _0)] - Payload(#[cause] PayloadError), -} - -impl ResponseError for ProtoBufPayloadError { - - fn error_response(&self) -> HttpResponse { - match *self { - ProtoBufPayloadError::Overflow => httpcodes::HttpPayloadTooLarge.into(), - _ => httpcodes::HttpBadRequest.into(), - } - } -} - -impl From for ProtoBufPayloadError { - fn from(err: PayloadError) -> ProtoBufPayloadError { - ProtoBufPayloadError::Payload(err) - } -} - -impl From for ProtoBufPayloadError { - fn from(err: ProtoBufDecodeError) -> ProtoBufPayloadError { - ProtoBufPayloadError::Deserialize(err) - } -} - /// Errors which can occur when attempting to interpret a segment string as a /// valid path segment. #[derive(Fail, Debug, PartialEq)] diff --git a/src/httpmessage.rs b/src/httpmessage.rs index 61ab3c4da..69065c49c 100644 --- a/src/httpmessage.rs +++ b/src/httpmessage.rs @@ -4,7 +4,6 @@ use bytes::{Bytes, BytesMut}; use futures::{Future, Stream, Poll}; use http_range::HttpRange; use serde::de::DeserializeOwned; -use prost::Message; use mime::Mime; use url::form_urlencoded; use encoding::all::UTF_8; @@ -13,7 +12,6 @@ use encoding::label::encoding_from_whatwg_label; use http::{header, HeaderMap}; use json::JsonBody; -use protobuf::ProtoBufBody; use header::Header; use multipart::Multipart; use error::{ParseError, ContentTypeError, @@ -211,12 +209,6 @@ pub trait HttpMessage { JsonBody::new(self) } - fn protobuf(self) -> ProtoBufBody - where Self: Stream + Sized - { - ProtoBufBody::new(self) - } - /// Return stream to http payload processes as multipart. /// /// Content-type: multipart/form-data; diff --git a/src/httpresponse.rs b/src/httpresponse.rs index 2147a42c1..9c99d4d68 100644 --- a/src/httpresponse.rs +++ b/src/httpresponse.rs @@ -11,7 +11,6 @@ use http::{StatusCode, Version, HeaderMap, HttpTryFrom, Error as HttpError}; use http::header::{self, HeaderName, HeaderValue}; use serde_json; use serde::Serialize; -use prost::Message; use body::Body; use error::Error; @@ -509,22 +508,6 @@ impl HttpResponseBuilder { Ok(self.body(body)?) } - pub fn protobuf(&mut self, value: T) -> Result { - let mut body = Vec::new(); - value.encode(&mut body)?; - - let contains = if let Some(parts) = parts(&mut self.response, &self.err) { - parts.headers.contains_key(header::CONTENT_TYPE) - } else { - true - }; - if !contains { - self.header(header::CONTENT_TYPE, "application/protobuf"); - } - - Ok(self.body(body)?) - } - /// Set an empty body and generate `HttpResponse` /// /// `HttpResponseBuilder` can not be used after this call. diff --git a/src/lib.rs b/src/lib.rs index 39e1e48a9..8c021ca99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,6 @@ extern crate url; extern crate libc; extern crate serde; extern crate serde_json; -extern crate prost; extern crate flate2; extern crate brotli2; extern crate encoding; @@ -89,6 +88,9 @@ extern crate h2 as http2; extern crate trust_dns_resolver; #[macro_use] extern crate actix; +#[cfg(feature="protobuf")] +extern crate prost; + #[cfg(test)] #[macro_use] extern crate serde_derive; @@ -112,7 +114,6 @@ mod httprequest; mod httpresponse; mod info; mod json; -mod protobuf; mod route; mod router; mod resource; @@ -120,6 +121,11 @@ mod param; mod payload; mod pipeline; +#[cfg(feature="protobuf")] +mod protobuf; +#[cfg(feature="protobuf")] +pub use protobuf::{ProtoBuf, ProtoBufBody}; + pub mod client; pub mod fs; pub mod ws; @@ -134,7 +140,6 @@ pub mod server; pub use error::{Error, Result, ResponseError}; pub use body::{Body, Binary}; pub use json::Json; -pub use protobuf::ProtoBuf; pub use application::Application; pub use httpmessage::HttpMessage; pub use httprequest::HttpRequest; diff --git a/src/protobuf.rs b/src/protobuf.rs index 439f26179..e0147a1de 100644 --- a/src/protobuf.rs +++ b/src/protobuf.rs @@ -1,16 +1,60 @@ use bytes::{Bytes, BytesMut}; use futures::{Poll, Future, Stream}; -use http::header::CONTENT_LENGTH; +use http::header::{CONTENT_TYPE, CONTENT_LENGTH}; use bytes::IntoBuf; use prost::Message; +use prost::EncodeError as ProtoBufEncodeError; +use prost::DecodeError as ProtoBufDecodeError; -use error::{Error, ProtoBufPayloadError, PayloadError}; +use error::{Error, PayloadError, ResponseError}; use handler::Responder; use httpmessage::HttpMessage; use httprequest::HttpRequest; -use httpresponse::HttpResponse; +use httpresponse::{HttpResponse, HttpResponseBuilder}; +use httpcodes::{HttpBadRequest, HttpPayloadTooLarge}; +#[derive(Fail, Debug)] +pub enum ProtoBufPayloadError { + /// Payload size is bigger than 256k + #[fail(display="Payload size is bigger than 256k")] + Overflow, + /// Content type error + #[fail(display="Content type error")] + ContentType, + /// Deserialize error + #[fail(display="Json deserialize error: {}", _0)] + Deserialize(#[cause] ProtoBufDecodeError), + /// Payload error + #[fail(display="Error that occur during reading payload: {}", _0)] + Payload(#[cause] PayloadError), +} + +impl ResponseError for ProtoBufPayloadError { + + fn error_response(&self) -> HttpResponse { + match *self { + ProtoBufPayloadError::Overflow => HttpPayloadTooLarge.into(), + _ => HttpBadRequest.into(), + } + } +} + +impl From for ProtoBufPayloadError { + fn from(err: PayloadError) -> ProtoBufPayloadError { + ProtoBufPayloadError::Payload(err) + } +} + +impl From for ProtoBufPayloadError { + fn from(err: ProtoBufDecodeError) -> ProtoBufPayloadError { + ProtoBufPayloadError::Deserialize(err) + } +} + +/// `InternalServerError` for `ProtoBufEncodeError` `ProtoBufDecodeError` +impl ResponseError for ProtoBufEncodeError {} +impl ResponseError for ProtoBufDecodeError {} #[derive(Debug)] pub struct ProtoBuf(pub T); @@ -32,9 +76,6 @@ impl Responder for ProtoBuf { } } - - - pub struct ProtoBufBody{ limit: usize, ct: &'static str, @@ -110,4 +151,16 @@ impl Future for ProtoBufBody self.fut.as_mut().expect("ProtoBufBody could not be used second time").poll() } -} \ No newline at end of file +} + + +impl HttpResponseBuilder { + + pub fn protobuf(&mut self, value: T) -> Result { + self.header(CONTENT_TYPE, "application/protobuf"); + + let mut body = Vec::new(); + value.encode(&mut body)?; + Ok(self.body(body)?) + } +}