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 || {