diff --git a/src/app.rs b/src/app.rs index b8efdd38b71..0daa54b668b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,18 +3,21 @@ use std::marker::PhantomData; use std::rc::Rc; use actix_http::body::{Body, MessageBody}; +#[cfg(any(feature = "brotli", feature = "flate2-zlib", feature = "flate2-rust"))] +use actix_http::encoding::{Decoder, Encoder}; use actix_server_config::ServerConfig; use actix_service::boxed::{self, BoxedNewService}; use actix_service::{ ApplyTransform, IntoNewService, IntoTransform, NewService, Transform, }; -use futures::IntoFuture; +use bytes::Bytes; +use futures::{IntoFuture, Stream}; use crate::app_service::{AppChain, AppEntry, AppInit, AppRouting, AppRoutingFactory}; use crate::config::{AppConfig, AppConfigInner}; use crate::data::{Data, DataFactory}; -use crate::dev::{PayloadStream, ResourceDef}; -use crate::error::Error; +use crate::dev::{Payload, PayloadStream, ResourceDef}; +use crate::error::{Error, PayloadError}; use crate::resource::Resource; use crate::route::Route; use crate::service::{ @@ -27,17 +30,17 @@ type HttpNewService

= /// Application builder - structure that follows the builder pattern /// for building application instances. -pub struct App +pub struct App where - T: NewService>, + T: NewService, Response = ServiceRequest>, { chain: T, data: Vec>, config: AppConfigInner, - _t: PhantomData<(P,)>, + _t: PhantomData<(In, Out)>, } -impl App { +impl App { /// Create application builder. Application can be configured with a builder-like pattern. pub fn new() -> Self { App { @@ -49,12 +52,13 @@ impl App { } } -impl App +impl App where - P: 'static, + In: 'static, + Out: 'static, T: NewService< - Request = ServiceRequest, - Response = ServiceRequest

, + Request = ServiceRequest, + Response = ServiceRequest, Error = Error, InitError = (), >, @@ -97,11 +101,11 @@ where /// Set application data factory. This function is /// similar to `.data()` but it accepts data factory. Data object get /// constructed asynchronously during application initialization. - pub fn data_factory(mut self, data: F) -> Self + pub fn data_factory(mut self, data: F) -> Self where - F: Fn() -> Out + 'static, - Out: IntoFuture + 'static, - Out::Error: std::fmt::Debug, + F: Fn() -> R + 'static, + R: IntoFuture + 'static, + R::Error: std::fmt::Debug, { self.data.push(Box::new(data)); self @@ -113,10 +117,10 @@ where mw: F, ) -> AppRouter< T, - P, + Out, B, impl NewService< - Request = ServiceRequest

, + Request = ServiceRequest, Response = ServiceResponse, Error = Error, InitError = (), @@ -124,13 +128,13 @@ where > where M: Transform< - AppRouting

, - Request = ServiceRequest

, + AppRouting, + Request = ServiceRequest, Response = ServiceResponse, Error = Error, InitError = (), >, - F: IntoTransform>, + F: IntoTransform>, { let fref = Rc::new(RefCell::new(None)); let endpoint = ApplyTransform::new(mw, AppEntry::new(fref.clone())); @@ -176,17 +180,17 @@ where mw: F, ) -> AppRouter< T, - P, + Out, B, impl NewService< - Request = ServiceRequest

, + Request = ServiceRequest, Response = ServiceResponse, Error = Error, InitError = (), >, > where - F: FnMut(ServiceRequest

, &mut AppRouting

) -> R + Clone, + F: FnMut(ServiceRequest, &mut AppRouting) -> R + Clone, R: IntoFuture, Error = Error>, { self.wrap(mw) @@ -194,22 +198,23 @@ where /// Register a request modifier. It can modify any request parameters /// including request payload type. - pub fn chain( + pub fn chain( self, chain: F, ) -> App< - P1, + In, + P, impl NewService< - Request = ServiceRequest, - Response = ServiceRequest, + Request = ServiceRequest, + Response = ServiceRequest

, Error = Error, InitError = (), >, > where C: NewService< - Request = ServiceRequest

, - Response = ServiceRequest, + Request = ServiceRequest, + Response = ServiceRequest

, Error = Error, InitError = (), >, @@ -246,8 +251,8 @@ where pub fn route( self, path: &str, - mut route: Route

, - ) -> AppRouter> { + mut route: Route, + ) -> AppRouter> { self.service( Resource::new(path) .add_guards(route.take_guards()) @@ -264,9 +269,9 @@ where /// * *Resource* is an entry in resource table which corresponds to requested URL. /// * *Scope* is a set of resources with common root path. /// * "StaticFiles" is a service for static files support - pub fn service(self, service: F) -> AppRouter> + pub fn service(self, service: F) -> AppRouter> where - F: HttpServiceFactory

+ 'static, + F: HttpServiceFactory + 'static, { let fref = Rc::new(RefCell::new(None)); @@ -294,6 +299,34 @@ where self.config.host = val.to_owned(); self } + + #[cfg(any(feature = "brotli", feature = "flate2-zlib", feature = "flate2-rust"))] + /// Enable content compression and decompression. + pub fn enable_encoding( + self, + ) -> AppRouter< + impl NewService< + Request = ServiceRequest, + Response = ServiceRequest>>, + Error = Error, + InitError = (), + >, + Decoder>, + Encoder, + impl NewService< + Request = ServiceRequest>>, + Response = ServiceResponse>, + Error = Error, + InitError = (), + >, + > + where + Out: Stream, + { + use crate::middleware::encoding::{Compress, Decompress}; + + self.chain(Decompress::new()).wrap(Compress::default()) + } } /// Application router builder - Structure that follows the builder pattern diff --git a/tests/test_server.rs b/tests/test_server.rs index 364f9262618..c30cf67f87e 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -335,6 +335,34 @@ fn test_body_brotli() { assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } +#[test] +fn test_encoding() { + let mut srv = TestServer::new(move || { + HttpService::new( + App::new().enable_encoding().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); + + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); + + let request = srv + .post() + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.block_on(HttpMessageBody::new(&mut response)).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + #[test] fn test_gzip_encoding() { let mut srv = TestServer::new(move || {