diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index b4246c64d..8be6702fc 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -74,7 +74,7 @@ where Err(e) => return Err(e.into()), } } else { - break; + return Ok(Async::Ready(Some(chunk))); } } Async::Ready(None) => { @@ -150,7 +150,7 @@ impl ContentDecoder { #[allow(unreachable_patterns)] fn feed_data(&mut self, data: Bytes) -> io::Result> { match self { - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] + #[cfg(feature = "brotli")] ContentDecoder::Br(ref mut decoder) => match decoder.write_all(&data) { Ok(_) => { decoder.flush()?; diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 72b72d369..88c3be421 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -30,7 +30,7 @@ ssl = ["openssl", "actix-http/ssl"] cookies = ["cookie", "actix-http/cookies"] # brotli encoding, requires c compiler -brotli = ["actix-http/brotli2"] +brotli = ["actix-http/brotli"] # miniz-sys backend for flate2 crate flate2-zlib = ["actix-http/flate2-zlib"] @@ -53,8 +53,12 @@ cookie = { version="0.11", features=["percent-encode"], optional = true } openssl = { version="0.10", optional = true } [dev-dependencies] -env_logger = "0.6" -mime = "0.3" actix-rt = "0.2.1" +actix-web = { path = "..", features=["ssl"] } actix-http = { path = "../actix-http/", features=["ssl"] } actix-http-test = { path = "../test-server/", features=["ssl"] } +brotli2 = { version="^0.3.2" } +flate2 = { version="^1.0.2" } +env_logger = "0.6" +mime = "0.3" +rand = "0.6" diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 89acf7d58..4898a0627 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -2,6 +2,7 @@ use std::cell::RefCell; use std::rc::Rc; pub use actix_http::client::{ConnectError, InvalidUrl, SendRequestError}; +pub use actix_http::error::PayloadError; pub use actix_http::http; use actix_http::client::Connector; diff --git a/awc/src/request.rs b/awc/src/request.rs index f23aa7ef9..90f9a1ab9 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -13,15 +13,24 @@ use serde_json; use actix_http::body::{Body, BodyStream}; use actix_http::client::{InvalidUrl, SendRequestError}; -use actix_http::http::header::{self, Header, IntoHeaderValue}; +use actix_http::encoding::Decoder; +use actix_http::http::header::{self, ContentEncoding, Header, IntoHeaderValue}; use actix_http::http::{ uri, ConnectionType, Error as HttpError, HeaderName, HeaderValue, HttpTryFrom, Method, Uri, Version, }; -use actix_http::{Error, Head, RequestHead}; +use actix_http::{Error, Head, Payload, RequestHead}; use crate::response::ClientResponse; -use crate::Connect; +use crate::{Connect, PayloadError}; + +#[cfg(any(feature = "brotli", feature = "flate2-zlib", feature = "flate2-rust"))] +const HTTPS_ENCODING: &str = "br, gzip, deflate"; +#[cfg(all( + any(feature = "flate2-zlib", feature = "flate2-rust"), + not(feature = "brotli") +))] +const HTTPS_ENCODING: &str = "gzip, deflate"; /// An HTTP Client request builder /// @@ -52,6 +61,7 @@ pub struct ClientRequest { #[cfg(feature = "cookies")] cookies: Option, default_headers: bool, + response_decompress: bool, connector: Rc>, } @@ -81,6 +91,7 @@ impl ClientRequest { #[cfg(feature = "cookies")] cookies: None, default_headers: true, + response_decompress: true, } } @@ -275,6 +286,12 @@ impl ClientRequest { self } + /// Disable automatic decompress of response's body + pub fn no_decompress(mut self) -> Self { + self.response_decompress = false; + self + } + /// This method calls provided closure with builder reference if /// value is `true`. pub fn if_true(mut self, value: bool, f: F) -> Self @@ -303,7 +320,10 @@ impl ClientRequest { pub fn send_body( mut self, body: B, - ) -> impl Future + ) -> impl Future< + Item = ClientResponse>, + Error = SendRequestError, + > where B: Into, { @@ -311,42 +331,44 @@ impl ClientRequest { return Either::A(err(e.into())); } - let mut slf = if self.default_headers { - // enable br only for https - let https = self - .head - .uri - .scheme_part() - .map(|s| s == &uri::Scheme::HTTPS) - .unwrap_or(true); - - let mut slf = if https { - self.set_header_if_none(header::ACCEPT_ENCODING, "br, gzip, deflate") - } else { - self.set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate") - }; + // validate uri + let uri = &self.head.uri; + if uri.host().is_none() { + return Either::A(err(InvalidUrl::MissingHost.into())); + } else if uri.scheme_part().is_none() { + return Either::A(err(InvalidUrl::MissingScheme.into())); + } else if let Some(scheme) = uri.scheme_part() { + match scheme.as_str() { + "http" | "ws" | "https" | "wss" => (), + _ => return Either::A(err(InvalidUrl::UnknownScheme.into())), + } + } else { + return Either::A(err(InvalidUrl::UnknownScheme.into())); + } + // set default headers + let slf = if self.default_headers { // set request host header - if let Some(host) = slf.head.uri.host() { - if !slf.head.headers.contains_key(header::HOST) { + if let Some(host) = self.head.uri.host() { + if !self.head.headers.contains_key(header::HOST) { let mut wrt = BytesMut::with_capacity(host.len() + 5).writer(); - let _ = match slf.head.uri.port_u16() { + let _ = match self.head.uri.port_u16() { None | Some(80) | Some(443) => write!(wrt, "{}", host), Some(port) => write!(wrt, "{}:{}", host, port), }; match wrt.get_mut().take().freeze().try_into() { Ok(value) => { - slf.head.headers.insert(header::HOST, value); + self.head.headers.insert(header::HOST, value); } - Err(e) => slf.err = Some(e.into()), + Err(e) => return Either::A(err(HttpError::from(e).into())), } } } // user agent - slf.set_header_if_none( + self.set_header_if_none( header::USER_AGENT, concat!("actix-http/", env!("CARGO_PKG_VERSION")), ) @@ -354,6 +376,32 @@ impl ClientRequest { self }; + // enable br only for https + let https = slf + .head + .uri + .scheme_part() + .map(|s| s == &uri::Scheme::HTTPS) + .unwrap_or(true); + + #[cfg(any( + feature = "brotli", + feature = "flate2-zlib", + feature = "flate2-rust" + ))] + let mut slf = { + if https { + slf.set_header_if_none(header::ACCEPT_ENCODING, HTTPS_ENCODING) + } else { + #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] + { + slf.set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate") + } + #[cfg(not(any(feature = "flate2-zlib", feature = "flate2-rust")))] + slf + } + }; + #[allow(unused_mut)] let mut head = slf.head; @@ -378,30 +426,32 @@ impl ClientRequest { } } - let uri = head.uri.clone(); + let response_decompress = slf.response_decompress; - // validate uri - if uri.host().is_none() { - Either::A(err(InvalidUrl::MissingHost.into())) - } else if uri.scheme_part().is_none() { - Either::A(err(InvalidUrl::MissingScheme.into())) - } else if let Some(scheme) = uri.scheme_part() { - match scheme.as_str() { - "http" | "ws" | "https" | "wss" => { - Either::B(slf.connector.borrow_mut().send_request(head, body.into())) - } - _ => Either::A(err(InvalidUrl::UnknownScheme.into())), - } - } else { - Either::A(err(InvalidUrl::UnknownScheme.into())) - } + let fut = slf + .connector + .borrow_mut() + .send_request(head, body.into()) + .map(move |res| { + res.map_body(|head, payload| { + if response_decompress { + Payload::Stream(Decoder::from_headers(&head.headers, payload)) + } else { + Payload::Stream(Decoder::new(payload, ContentEncoding::Identity)) + } + }) + }); + Either::B(fut) } /// Set a JSON body and generate `ClientRequest` pub fn send_json( self, value: T, - ) -> impl Future { + ) -> impl Future< + Item = ClientResponse>, + Error = SendRequestError, + > { let body = match serde_json::to_string(&value) { Ok(body) => body, Err(e) => return Either::A(err(Error::from(e).into())), @@ -422,7 +472,10 @@ impl ClientRequest { pub fn send_form( self, value: T, - ) -> impl Future { + ) -> impl Future< + Item = ClientResponse>, + Error = SendRequestError, + > { let body = match serde_urlencoded::to_string(&value) { Ok(body) => body, Err(e) => return Either::A(err(Error::from(e).into())), @@ -441,7 +494,10 @@ impl ClientRequest { pub fn send_stream( self, stream: S, - ) -> impl Future + ) -> impl Future< + Item = ClientResponse>, + Error = SendRequestError, + > where S: Stream + 'static, E: Into + 'static, @@ -450,7 +506,12 @@ impl ClientRequest { } /// Set an empty body and generate `ClientRequest`. - pub fn send(self) -> impl Future { + pub fn send( + self, + ) -> impl Future< + Item = ClientResponse>, + Error = SendRequestError, + > { self.send_body(Body::Empty) } } diff --git a/awc/src/response.rs b/awc/src/response.rs index 0ae66df0f..4525bbc1a 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -1,21 +1,22 @@ use std::cell::{Ref, RefMut}; use std::fmt; -use bytes::Bytes; -use futures::{Poll, Stream}; +use bytes::{Bytes, BytesMut}; +use futures::{Future, Poll, Stream}; use actix_http::error::PayloadError; +use actix_http::http::header::CONTENT_LENGTH; use actix_http::http::{HeaderMap, StatusCode, Version}; use actix_http::{Extensions, Head, HttpMessage, Payload, PayloadStream, ResponseHead}; /// Client Response -pub struct ClientResponse { +pub struct ClientResponse { pub(crate) head: ResponseHead, - pub(crate) payload: Payload, + pub(crate) payload: Payload, } -impl HttpMessage for ClientResponse { - type Stream = PayloadStream; +impl HttpMessage for ClientResponse { + type Stream = S; fn headers(&self) -> &HeaderMap { &self.head.headers @@ -29,14 +30,14 @@ impl HttpMessage for ClientResponse { self.head.extensions_mut() } - fn take_payload(&mut self) -> Payload { + fn take_payload(&mut self) -> Payload { std::mem::replace(&mut self.payload, Payload::None) } } -impl ClientResponse { +impl ClientResponse { /// Create new Request instance - pub(crate) fn new(head: ResponseHead, payload: Payload) -> ClientResponse { + pub(crate) fn new(head: ResponseHead, payload: Payload) -> Self { ClientResponse { head, payload } } @@ -79,9 +80,35 @@ impl ClientResponse { pub fn keep_alive(&self) -> bool { self.head().keep_alive() } + + /// Set a body and return previous body value + pub fn map_body(mut self, f: F) -> ClientResponse + where + F: FnOnce(&mut ResponseHead, Payload) -> Payload, + { + let payload = f(&mut self.head, self.payload); + + ClientResponse { + payload, + head: self.head, + } + } } -impl Stream for ClientResponse { +impl ClientResponse +where + S: Stream + 'static, +{ + /// Load http response's body. + pub fn body(self) -> MessageBody { + MessageBody::new(self) + } +} + +impl Stream for ClientResponse +where + S: Stream, +{ type Item = Bytes; type Error = PayloadError; @@ -90,7 +117,7 @@ impl Stream for ClientResponse { } } -impl fmt::Debug for ClientResponse { +impl fmt::Debug for ClientResponse { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?; writeln!(f, " headers:")?; @@ -100,3 +127,100 @@ impl fmt::Debug for ClientResponse { Ok(()) } } + +/// Future that resolves to a complete http message body. +pub struct MessageBody { + limit: usize, + length: Option, + stream: Option>, + err: Option, + fut: Option>>, +} + +impl MessageBody +where + S: Stream + 'static, +{ + /// Create `MessageBody` for request. + pub fn new(res: ClientResponse) -> MessageBody { + let mut len = None; + if let Some(l) = res.headers().get(CONTENT_LENGTH) { + if let Ok(s) = l.to_str() { + if let Ok(l) = s.parse::() { + len = Some(l) + } else { + return Self::err(PayloadError::UnknownLength); + } + } else { + return Self::err(PayloadError::UnknownLength); + } + } + + MessageBody { + limit: 262_144, + length: len, + stream: Some(res), + fut: None, + err: None, + } + } + + /// Change max size of payload. By default max size is 256Kb + pub fn limit(mut self, limit: usize) -> Self { + self.limit = limit; + self + } + + fn err(e: PayloadError) -> Self { + MessageBody { + stream: None, + limit: 262_144, + fut: None, + err: Some(e), + length: None, + } + } +} + +impl Future for MessageBody +where + S: Stream + 'static, +{ + type Item = Bytes; + type Error = PayloadError; + + fn poll(&mut self) -> Poll { + if let Some(ref mut fut) = self.fut { + return fut.poll(); + } + + if let Some(err) = self.err.take() { + return Err(err); + } + + if let Some(len) = self.length.take() { + if len > self.limit { + return Err(PayloadError::Overflow); + } + } + + // future + let limit = self.limit; + self.fut = Some(Box::new( + self.stream + .take() + .expect("Can not be used second time") + .from_err() + .fold(BytesMut::with_capacity(8192), move |mut body, chunk| { + if (body.len() + chunk.len()) > limit { + Err(PayloadError::Overflow) + } else { + body.extend_from_slice(&chunk); + Ok(body) + } + }) + .map(|body| body.freeze()), + )); + self.poll() + } +} diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs new file mode 100644 index 000000000..f7605b59c --- /dev/null +++ b/awc/tests/test_client.rs @@ -0,0 +1,508 @@ +use std::io::{Read, Write}; +use std::{net, thread}; + +use brotli2::write::BrotliEncoder; +use bytes::Bytes; +use flate2::write::{GzEncoder, ZlibEncoder}; +use flate2::Compression; +use futures::stream::once; +use futures::Future; +use rand::Rng; + +use actix_http::HttpService; +use actix_http_test::TestServer; +use actix_web::{middleware, web, App, HttpRequest, HttpResponse}; + +const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World"; + +#[test] +fn test_simple() { + let mut srv = + TestServer::new(|| { + HttpService::new(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), + )) + }); + + let request = srv.get().header("x-test", "111").send(); + let response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + + let response = srv.block_on(srv.post().send()).unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +// #[test] +// fn test_connection_close() { +// let mut srv = +// test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR))); + +// let request = srv.get().header("Connection", "close").finish().unwrap(); +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); +// } + +// #[test] +// fn test_with_query_parameter() { +// let mut srv = test::TestServer::new(|app| { +// app.handler(|req: &HttpRequest| match req.query().get("qp") { +// Some(_) => HttpResponse::Ok().finish(), +// None => HttpResponse::BadRequest().finish(), +// }) +// }); + +// let request = srv.get().uri(srv.url("/?qp=5").as_str()).finish().unwrap(); + +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); +// } + +// #[test] +// fn test_no_decompress() { +// let mut srv = +// test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR))); + +// let request = srv.get().disable_decompress().finish().unwrap(); +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); + +// // read response +// let bytes = srv.execute(response.body()).unwrap(); + +// let mut e = GzDecoder::new(&bytes[..]); +// let mut dec = Vec::new(); +// e.read_to_end(&mut dec).unwrap(); +// assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + +// // POST +// let request = srv.post().disable_decompress().finish().unwrap(); +// let response = srv.execute(request.send()).unwrap(); + +// let bytes = srv.execute(response.body()).unwrap(); +// let mut e = GzDecoder::new(&bytes[..]); +// let mut dec = Vec::new(); +// e.read_to_end(&mut dec).unwrap(); +// assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); +// } + +#[test] +fn test_client_gzip_encoding() { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let data = e.finish().unwrap(); + + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + })))) + }); + + // client request + let response = srv.block_on(srv.post().send()).unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +#[test] +fn test_client_gzip_encoding_large() { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.repeat(10).as_ref()).unwrap(); + let data = e.finish().unwrap(); + + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + })))) + }); + + // client request + let response = srv.block_on(srv.post().send()).unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from(STR.repeat(10))); +} + +#[test] +fn test_client_gzip_encoding_large_random() { + let data = rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .take(100_000) + .collect::(); + + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |data: Bytes| { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(&data).unwrap(); + let data = e.finish().unwrap(); + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + }, + )))) + }); + + // client request + let response = srv.block_on(srv.post().send_body(data.clone())).unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from(data)); +} + +#[test] +fn test_client_brotli_encoding() { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |data: Bytes| { + let mut e = BrotliEncoder::new(Vec::new(), 5); + e.write_all(&data).unwrap(); + let data = e.finish().unwrap(); + HttpResponse::Ok() + .header("content-encoding", "br") + .body(data) + }, + )))) + }); + + // client request + let response = srv.block_on(srv.post().send_body(STR)).unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +// #[test] +// fn test_client_brotli_encoding_large_random() { +// let data = rand::thread_rng() +// .sample_iter(&rand::distributions::Alphanumeric) +// .take(70_000) +// .collect::(); + +// let mut srv = test::TestServer::new(|app| { +// app.handler(|req: &HttpRequest| { +// req.body() +// .and_then(move |bytes: Bytes| { +// Ok(HttpResponse::Ok() +// .content_encoding(http::ContentEncoding::Gzip) +// .body(bytes)) +// }) +// .responder() +// }) +// }); + +// // client request +// let request = srv +// .client(http::Method::POST, "/") +// .content_encoding(http::ContentEncoding::Br) +// .body(data.clone()) +// .unwrap(); +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); + +// // read response +// let bytes = srv.execute(response.body()).unwrap(); +// assert_eq!(bytes.len(), data.len()); +// assert_eq!(bytes, Bytes::from(data)); +// } + +// #[cfg(feature = "brotli")] +// #[test] +// fn test_client_deflate_encoding() { +// let mut srv = test::TestServer::new(|app| { +// app.handler(|req: &HttpRequest| { +// req.body() +// .and_then(|bytes: Bytes| { +// Ok(HttpResponse::Ok() +// .content_encoding(http::ContentEncoding::Br) +// .body(bytes)) +// }) +// .responder() +// }) +// }); + +// // client request +// let request = srv +// .post() +// .content_encoding(http::ContentEncoding::Deflate) +// .body(STR) +// .unwrap(); +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); + +// // read response +// let bytes = srv.execute(response.body()).unwrap(); +// assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +// } + +// #[test] +// fn test_client_deflate_encoding_large_random() { +// let data = rand::thread_rng() +// .sample_iter(&rand::distributions::Alphanumeric) +// .take(70_000) +// .collect::(); + +// let mut srv = test::TestServer::new(|app| { +// app.handler(|req: &HttpRequest| { +// req.body() +// .and_then(|bytes: Bytes| { +// Ok(HttpResponse::Ok() +// .content_encoding(http::ContentEncoding::Br) +// .body(bytes)) +// }) +// .responder() +// }) +// }); + +// // client request +// let request = srv +// .post() +// .content_encoding(http::ContentEncoding::Deflate) +// .body(data.clone()) +// .unwrap(); +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); + +// // read response +// let bytes = srv.execute(response.body()).unwrap(); +// assert_eq!(bytes, Bytes::from(data)); +// } + +// #[test] +// fn test_client_streaming_explicit() { +// let mut srv = test::TestServer::new(|app| { +// app.handler(|req: &HttpRequest| { +// req.body() +// .map_err(Error::from) +// .and_then(|body| { +// Ok(HttpResponse::Ok() +// .chunked() +// .content_encoding(http::ContentEncoding::Identity) +// .body(body)) +// }) +// .responder() +// }) +// }); + +// let body = once(Ok(Bytes::from_static(STR.as_ref()))); + +// let request = srv.get().body(Body::Streaming(Box::new(body))).unwrap(); +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); + +// // read response +// let bytes = srv.execute(response.body()).unwrap(); +// assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +// } + +// #[test] +// fn test_body_streaming_implicit() { +// let mut srv = test::TestServer::new(|app| { +// app.handler(|_| { +// let body = once(Ok(Bytes::from_static(STR.as_ref()))); +// HttpResponse::Ok() +// .content_encoding(http::ContentEncoding::Gzip) +// .body(Body::Streaming(Box::new(body))) +// }) +// }); + +// let request = srv.get().finish().unwrap(); +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); + +// // read response +// let bytes = srv.execute(response.body()).unwrap(); +// assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +// } + +// #[test] +// fn test_client_cookie_handling() { +// use actix_web::http::Cookie; +// fn err() -> Error { +// use std::io::{Error as IoError, ErrorKind}; +// // stub some generic error +// Error::from(IoError::from(ErrorKind::NotFound)) +// } +// let cookie1 = Cookie::build("cookie1", "value1").finish(); +// let cookie2 = Cookie::build("cookie2", "value2") +// .domain("www.example.org") +// .path("/") +// .secure(true) +// .http_only(true) +// .finish(); +// // Q: are all these clones really necessary? A: Yes, possibly +// let cookie1b = cookie1.clone(); +// let cookie2b = cookie2.clone(); +// let mut srv = test::TestServer::new(move |app| { +// let cookie1 = cookie1b.clone(); +// let cookie2 = cookie2b.clone(); +// app.handler(move |req: &HttpRequest| { +// // Check cookies were sent correctly +// req.cookie("cookie1") +// .ok_or_else(err) +// .and_then(|c1| { +// if c1.value() == "value1" { +// Ok(()) +// } else { +// Err(err()) +// } +// }) +// .and_then(|()| req.cookie("cookie2").ok_or_else(err)) +// .and_then(|c2| { +// if c2.value() == "value2" { +// Ok(()) +// } else { +// Err(err()) +// } +// }) +// // Send some cookies back +// .map(|_| { +// HttpResponse::Ok() +// .cookie(cookie1.clone()) +// .cookie(cookie2.clone()) +// .finish() +// }) +// }) +// }); + +// let request = srv +// .get() +// .cookie(cookie1.clone()) +// .cookie(cookie2.clone()) +// .finish() +// .unwrap(); +// let response = srv.execute(request.send()).unwrap(); +// assert!(response.status().is_success()); +// let c1 = response.cookie("cookie1").expect("Missing cookie1"); +// assert_eq!(c1, cookie1); +// let c2 = response.cookie("cookie2").expect("Missing cookie2"); +// assert_eq!(c2, cookie2); +// } + +// #[test] +// fn test_default_headers() { +// let srv = test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR))); + +// let request = srv.get().finish().unwrap(); +// let repr = format!("{:?}", request); +// assert!(repr.contains("\"accept-encoding\": \"gzip, deflate\"")); +// assert!(repr.contains(concat!( +// "\"user-agent\": \"actix-web/", +// env!("CARGO_PKG_VERSION"), +// "\"" +// ))); + +// let request_override = srv +// .get() +// .header("User-Agent", "test") +// .header("Accept-Encoding", "over_test") +// .finish() +// .unwrap(); +// let repr_override = format!("{:?}", request_override); +// assert!(repr_override.contains("\"user-agent\": \"test\"")); +// assert!(repr_override.contains("\"accept-encoding\": \"over_test\"")); +// assert!(!repr_override.contains("\"accept-encoding\": \"gzip, deflate\"")); +// assert!(!repr_override.contains(concat!( +// "\"user-agent\": \"Actix-web/", +// env!("CARGO_PKG_VERSION"), +// "\"" +// ))); +// } + +// #[test] +// fn client_read_until_eof() { +// let addr = test::TestServer::unused_addr(); + +// thread::spawn(move || { +// let lst = net::TcpListener::bind(addr).unwrap(); + +// for stream in lst.incoming() { +// let mut stream = stream.unwrap(); +// let mut b = [0; 1000]; +// let _ = stream.read(&mut b).unwrap(); +// let _ = stream +// .write_all(b"HTTP/1.1 200 OK\r\nconnection: close\r\n\r\nwelcome!"); +// } +// }); + +// let mut sys = actix::System::new("test"); + +// // client request +// let req = client::ClientRequest::get(format!("http://{}/", addr).as_str()) +// .finish() +// .unwrap(); +// let response = sys.block_on(req.send()).unwrap(); +// assert!(response.status().is_success()); + +// // read response +// let bytes = sys.block_on(response.body()).unwrap(); +// assert_eq!(bytes, Bytes::from_static(b"welcome!")); +// } + +// #[test] +// fn client_basic_auth() { +// let mut srv = +// test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR))); +// /// set authorization header to Basic +// let request = srv +// .get() +// .basic_auth("username", Some("password")) +// .finish() +// .unwrap(); +// let repr = format!("{:?}", request); +// assert!(repr.contains("Basic dXNlcm5hbWU6cGFzc3dvcmQ=")); +// } + +// #[test] +// fn client_bearer_auth() { +// let mut srv = +// test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR))); +// /// set authorization header to Bearer +// let request = srv +// .get() +// .bearer_auth("someS3cr3tAutht0k3n") +// .finish() +// .unwrap(); +// let repr = format!("{:?}", request); +// assert!(repr.contains("Bearer someS3cr3tAutht0k3n")); +// } diff --git a/tests/test_server.rs b/tests/test_server.rs index acea029c6..29998bc06 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -65,7 +65,7 @@ fn test_body_gzip() { ) }); - let mut response = srv.block_on(srv.get().send()).unwrap(); + let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap(); assert!(response.status().is_success()); // read response @@ -95,7 +95,7 @@ fn test_body_gzip_large() { ) }); - let mut response = srv.block_on(srv.get().send()).unwrap(); + let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap(); assert!(response.status().is_success()); // read response @@ -128,7 +128,7 @@ fn test_body_gzip_large_random() { ) }); - let mut response = srv.block_on(srv.get().send()).unwrap(); + let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap(); assert!(response.status().is_success()); // read response @@ -156,7 +156,7 @@ fn test_body_chunked_implicit() { ) }); - let mut response = srv.block_on(srv.get().send()).unwrap(); + let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap(); assert!(response.status().is_success()); assert_eq!( response.headers().get(TRANSFER_ENCODING).unwrap(), @@ -188,7 +188,12 @@ fn test_body_br_streaming() { }); let mut response = srv - .block_on(srv.get().header(ACCEPT_ENCODING, "br").send()) + .block_on( + srv.get() + .header(ACCEPT_ENCODING, "br") + .no_decompress() + .send(), + ) .unwrap(); assert!(response.status().is_success()); @@ -258,7 +263,7 @@ fn test_body_deflate() { }); // client request - let mut response = srv.block_on(srv.get().send()).unwrap(); + let mut response = srv.block_on(srv.get().no_decompress().send()).unwrap(); assert!(response.status().is_success()); // read response @@ -285,7 +290,12 @@ fn test_body_brotli() { // client request let mut response = srv - .block_on(srv.get().header(ACCEPT_ENCODING, "br").send()) + .block_on( + srv.get() + .header(ACCEPT_ENCODING, "br") + .no_decompress() + .send(), + ) .unwrap(); assert!(response.status().is_success());