mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-24 07:53:00 +01:00
add client decompression support
This commit is contained in:
parent
2629699b62
commit
1cca25c276
@ -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<Option<Bytes>> {
|
||||
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()?;
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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<CookieJar>,
|
||||
default_headers: bool,
|
||||
response_decompress: bool,
|
||||
connector: Rc<RefCell<dyn Connect>>,
|
||||
}
|
||||
|
||||
@ -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<F>(mut self, value: bool, f: F) -> Self
|
||||
@ -303,7 +320,10 @@ impl ClientRequest {
|
||||
pub fn send_body<B>(
|
||||
mut self,
|
||||
body: B,
|
||||
) -> impl Future<Item = ClientResponse, Error = SendRequestError>
|
||||
) -> impl Future<
|
||||
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
|
||||
Error = SendRequestError,
|
||||
>
|
||||
where
|
||||
B: Into<Body>,
|
||||
{
|
||||
@ -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<T: Serialize>(
|
||||
self,
|
||||
value: T,
|
||||
) -> impl Future<Item = ClientResponse, Error = SendRequestError> {
|
||||
) -> impl Future<
|
||||
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
|
||||
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<T: Serialize>(
|
||||
self,
|
||||
value: T,
|
||||
) -> impl Future<Item = ClientResponse, Error = SendRequestError> {
|
||||
) -> impl Future<
|
||||
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
|
||||
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<S, E>(
|
||||
self,
|
||||
stream: S,
|
||||
) -> impl Future<Item = ClientResponse, Error = SendRequestError>
|
||||
) -> impl Future<
|
||||
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
|
||||
Error = SendRequestError,
|
||||
>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = E> + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
@ -450,7 +506,12 @@ impl ClientRequest {
|
||||
}
|
||||
|
||||
/// Set an empty body and generate `ClientRequest`.
|
||||
pub fn send(self) -> impl Future<Item = ClientResponse, Error = SendRequestError> {
|
||||
pub fn send(
|
||||
self,
|
||||
) -> impl Future<
|
||||
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
|
||||
Error = SendRequestError,
|
||||
> {
|
||||
self.send_body(Body::Empty)
|
||||
}
|
||||
}
|
||||
|
@ -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<S = PayloadStream> {
|
||||
pub(crate) head: ResponseHead,
|
||||
pub(crate) payload: Payload,
|
||||
pub(crate) payload: Payload<S>,
|
||||
}
|
||||
|
||||
impl HttpMessage for ClientResponse {
|
||||
type Stream = PayloadStream;
|
||||
impl<S> HttpMessage for ClientResponse<S> {
|
||||
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<S> {
|
||||
std::mem::replace(&mut self.payload, Payload::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientResponse {
|
||||
impl<S> ClientResponse<S> {
|
||||
/// Create new Request instance
|
||||
pub(crate) fn new(head: ResponseHead, payload: Payload) -> ClientResponse {
|
||||
pub(crate) fn new(head: ResponseHead, payload: Payload<S>) -> 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<F, U>(mut self, f: F) -> ClientResponse<U>
|
||||
where
|
||||
F: FnOnce(&mut ResponseHead, Payload<S>) -> Payload<U>,
|
||||
{
|
||||
let payload = f(&mut self.head, self.payload);
|
||||
|
||||
ClientResponse {
|
||||
payload,
|
||||
head: self.head,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for ClientResponse {
|
||||
impl<S> ClientResponse<S>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
{
|
||||
/// Load http response's body.
|
||||
pub fn body(self) -> MessageBody<S> {
|
||||
MessageBody::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Stream for ClientResponse<S>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
||||
{
|
||||
type Item = Bytes;
|
||||
type Error = PayloadError;
|
||||
|
||||
@ -90,7 +117,7 @@ impl Stream for ClientResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ClientResponse {
|
||||
impl<S> fmt::Debug for ClientResponse<S> {
|
||||
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<S> {
|
||||
limit: usize,
|
||||
length: Option<usize>,
|
||||
stream: Option<ClientResponse<S>>,
|
||||
err: Option<PayloadError>,
|
||||
fut: Option<Box<Future<Item = Bytes, Error = PayloadError>>>,
|
||||
}
|
||||
|
||||
impl<S> MessageBody<S>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
{
|
||||
/// Create `MessageBody` for request.
|
||||
pub fn new(res: ClientResponse<S>) -> MessageBody<S> {
|
||||
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::<usize>() {
|
||||
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<S> Future for MessageBody<S>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
{
|
||||
type Item = Bytes;
|
||||
type Error = PayloadError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
508
awc/tests/test_client.rs
Normal file
508
awc/tests/test_client.rs
Normal file
@ -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::<String>();
|
||||
|
||||
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::<String>();
|
||||
|
||||
// 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::<String>();
|
||||
|
||||
// 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 <base64 encoded username:password>
|
||||
// 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 <token>
|
||||
// let request = srv
|
||||
// .get()
|
||||
// .bearer_auth("someS3cr3tAutht0k3n")
|
||||
// .finish()
|
||||
// .unwrap();
|
||||
// let repr = format!("{:?}", request);
|
||||
// assert!(repr.contains("Bearer someS3cr3tAutht0k3n"));
|
||||
// }
|
@ -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());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user