Merge branch 'master' into fix-1611-pr

This commit is contained in:
masnagam 2020-07-21 22:54:18 +09:00 committed by GitHub
commit 8e2ea594ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 200 additions and 137 deletions

View File

@ -1,6 +1,8 @@
## PR Type <!-- Thanks for considering contributing actix! -->
What kind of change does this PR make? <!-- Please fill out the following to make our reviews easy. -->
## PR Type
<!-- What kind of change does this PR make? -->
<!-- Bug Fix / Feature / Refactor / Code Style / Other --> <!-- Bug Fix / Feature / Refactor / Code Style / Other -->
INSERT_PR_TYPE INSERT_PR_TYPE

View File

@ -8,7 +8,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
version: version:
- 1.41.1 # MSRV - 1.42.0 # MSRV
- stable - stable
- nightly - nightly

View File

@ -1,10 +1,19 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2020-xx-xx
### Changed
* `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set
using `App::data`. [#1610]
* `web::Path` now has a public representation: `web::Path(pub T)` that enables
destructuring. [#1594]
* MSRV is now 1.42.0.
### Fixed ### Fixed
* Memory leak of app data in pooled requests. [#1609] * Memory leak of app data in pooled requests. [#1609]
[#1594]: https://github.com/actix/actix-web/pull/1594
[#1609]: https://github.com/actix/actix-web/pull/1609 [#1609]: https://github.com/actix/actix-web/pull/1609
[#1610]: https://github.com/actix/actix-web/pull/1610
## 3.0.0-beta.1 - 2020-07-13 ## 3.0.0-beta.1 - 2020-07-13

View File

@ -12,6 +12,26 @@
* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a * `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
`u64` instead of a `usize`. `u64` instead of a `usize`.
* Code that was using `path.<index>` to access a `web::Path<(A, B, C)>`s elements now needs to use
destructuring or `.into_inner()`. For example:
```rust
// Previously:
async fn some_route(path: web::Path<(String, String)>) -> String {
format!("Hello, {} {}", path.0, path.1)
}
// Now (this also worked before):
async fn some_route(path: web::Path<(String, String)>) -> String {
let (first_name, last_name) = path.into_inner();
format!("Hello, {} {}", first_name, last_name)
}
// Or (this wasn't previously supported):
async fn some_route(web::Path((first_name, last_name)): web::Path<(String, String)>) -> String {
format!("Hello, {} {}", first_name, last_name)
}
```
## 2.0.0 ## 2.0.0
* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to * `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to

View File

@ -7,7 +7,7 @@
[![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web)
[![Documentation](https://docs.rs/actix-web/badge.svg)](https://docs.rs/actix-web) [![Documentation](https://docs.rs/actix-web/badge.svg)](https://docs.rs/actix-web)
[![Version](https://img.shields.io/badge/rustc-1.41+-lightgray.svg)](https://blog.rust-lang.org/2020/02/27/Rust-1.41.1.html) [![Version](https://img.shields.io/badge/rustc-1.42+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.42.html)
![License](https://img.shields.io/crates/l/actix-web.svg) ![License](https://img.shields.io/crates/l/actix-web.svg)
<br /> <br />
[![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web)
@ -32,7 +32,7 @@
* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/)) * Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
* Includes an async [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html) * Includes an async [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html)
* Supports [Actix actor framework](https://github.com/actix/actix) * Supports [Actix actor framework](https://github.com/actix/actix)
* Runs on stable Rust 1.41+ * Runs on stable Rust 1.42+
## Documentation ## Documentation
@ -61,8 +61,8 @@ Code:
use actix_web::{get, web, App, HttpServer, Responder}; use actix_web::{get, web, App, HttpServer, Responder};
#[get("/{id}/{name}/index.html")] #[get("/{id}/{name}/index.html")]
async fn index(info: web::Path<(u32, String)>) -> impl Responder { async fn index(web::Path((id, name)): web::Path<(u32, String)>) -> impl Responder {
format!("Hello {}! id:{}", info.1, info.0) format!("Hello {}! id:{}", name, id)
} }
#[actix_web::main] #[actix_web::main]

View File

@ -192,14 +192,8 @@ impl MessageBody for Body {
impl PartialEq for Body { impl PartialEq for Body {
fn eq(&self, other: &Body) -> bool { fn eq(&self, other: &Body) -> bool {
match *self { match *self {
Body::None => match *other { Body::None => matches!(*other, Body::None),
Body::None => true, Body::Empty => matches!(*other, Body::Empty),
_ => false,
},
Body::Empty => match *other {
Body::Empty => true,
_ => false,
},
Body::Bytes(ref b) => match *other { Body::Bytes(ref b) => match *other {
Body::Bytes(ref b2) => b == b2, Body::Bytes(ref b2) => b == b2,
_ => false, _ => false,

View File

@ -37,10 +37,7 @@ where
trace!("Sending client request: {:?} {:?}", head, body.size()); trace!("Sending client request: {:?} {:?}", head, body.size());
let head_req = head.as_ref().method == Method::HEAD; let head_req = head.as_ref().method == Method::HEAD;
let length = body.size(); let length = body.size();
let eof = match length { let eof = matches!(length, BodySize::None | BodySize::Empty | BodySize::Sized(0));
BodySize::None | BodySize::Empty | BodySize::Sized(0) => true,
_ => false,
};
let mut req = Request::new(()); let mut req = Request::new(());
*req.uri_mut() = head.as_ref().uri.clone(); *req.uri_mut() = head.as_ref().uri.clone();

View File

@ -652,10 +652,7 @@ mod tests {
} }
fn is_unhandled(&self) -> bool { fn is_unhandled(&self) -> bool {
match self { matches!(self, PayloadType::Stream(_))
PayloadType::Stream(_) => true,
_ => false,
}
} }
} }
@ -667,10 +664,7 @@ mod tests {
} }
} }
fn eof(&self) -> bool { fn eof(&self) -> bool {
match *self { matches!(*self, PayloadItem::Eof)
PayloadItem::Eof => true,
_ => false,
}
} }
} }

View File

@ -156,14 +156,8 @@ enum PollResponse {
impl PartialEq for PollResponse { impl PartialEq for PollResponse {
fn eq(&self, other: &PollResponse) -> bool { fn eq(&self, other: &PollResponse) -> bool {
match self { match self {
PollResponse::DrainWriteBuf => match other { PollResponse::DrainWriteBuf => matches!(other, PollResponse::DrainWriteBuf),
PollResponse::DrainWriteBuf => true, PollResponse::DoNothing => matches!(other, PollResponse::DoNothing),
_ => false,
},
PollResponse::DoNothing => match other {
PollResponse::DoNothing => true,
_ => false,
},
_ => false, _ => false,
} }
} }

View File

@ -387,26 +387,17 @@ impl ContentDisposition {
/// Returns `true` if it is [`Inline`](DispositionType::Inline). /// Returns `true` if it is [`Inline`](DispositionType::Inline).
pub fn is_inline(&self) -> bool { pub fn is_inline(&self) -> bool {
match self.disposition { matches!(self.disposition, DispositionType::Inline)
DispositionType::Inline => true,
_ => false,
}
} }
/// Returns `true` if it is [`Attachment`](DispositionType::Attachment). /// Returns `true` if it is [`Attachment`](DispositionType::Attachment).
pub fn is_attachment(&self) -> bool { pub fn is_attachment(&self) -> bool {
match self.disposition { matches!(self.disposition, DispositionType::Attachment)
DispositionType::Attachment => true,
_ => false,
}
} }
/// Returns `true` if it is [`FormData`](DispositionType::FormData). /// Returns `true` if it is [`FormData`](DispositionType::FormData).
pub fn is_form_data(&self) -> bool { pub fn is_form_data(&self) -> bool {
match self.disposition { matches!(self.disposition, DispositionType::FormData)
DispositionType::FormData => true,
_ => false,
}
} }
/// Returns `true` if it is [`Ext`](DispositionType::Ext) and the `disp_type` matches. /// Returns `true` if it is [`Ext`](DispositionType::Ext) and the `disp_type` matches.

View File

@ -148,10 +148,7 @@ impl ContentEncoding {
#[inline] #[inline]
/// Is the content compressed? /// Is the content compressed?
pub fn is_compression(self) -> bool { pub fn is_compression(self) -> bool {
match self { matches!(self, ContentEncoding::Identity | ContentEncoding::Auto)
ContentEncoding::Identity | ContentEncoding::Auto => false,
_ => true,
}
} }
#[inline] #[inline]

View File

@ -229,10 +229,7 @@ mod tests {
fn is_none( fn is_none(
frm: &Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError>, frm: &Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError>,
) -> bool { ) -> bool {
match *frm { matches!(*frm, Ok(None))
Ok(None) => true,
_ => false,
}
} }
fn extract( fn extract(

View File

@ -402,14 +402,9 @@ mod tests {
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool { fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
match err { match err {
JsonPayloadError::Payload(PayloadError::Overflow) => match other { JsonPayloadError::Payload(PayloadError::Overflow) =>
JsonPayloadError::Payload(PayloadError::Overflow) => true, matches!(other, JsonPayloadError::Payload(PayloadError::Overflow)),
_ => false, JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),
},
JsonPayloadError::ContentType => match other {
JsonPayloadError::ContentType => true,
_ => false,
},
_ => false, _ => false,
} }
} }

View File

@ -1 +1 @@
1.41.1 1.42.0

View File

@ -9,8 +9,8 @@
//! use actix_web::{get, web, App, HttpServer, Responder}; //! use actix_web::{get, web, App, HttpServer, Responder};
//! //!
//! #[get("/{id}/{name}/index.html")] //! #[get("/{id}/{name}/index.html")]
//! async fn index(info: web::Path<(u32, String)>) -> impl Responder { //! async fn index(web::Path((id, name)): web::Path<(u32, String)>) -> impl Responder {
//! format!("Hello {}! id:{}", info.1, info.0) //! format!("Hello {}! id:{}", name, id)
//! } //! }
//! //!
//! #[actix_web::main] //! #[actix_web::main]

View File

@ -407,18 +407,9 @@ mod tests {
fn eq(err: UrlencodedError, other: UrlencodedError) -> bool { fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {
match err { match err {
UrlencodedError::Overflow { .. } => match other { UrlencodedError::Overflow { .. } => matches!(other, UrlencodedError::Overflow { .. }),
UrlencodedError::Overflow { .. } => true, UrlencodedError::UnknownLength => matches!(other, UrlencodedError::UnknownLength),
_ => false, UrlencodedError::ContentType => matches!(other, UrlencodedError::ContentType),
},
UrlencodedError::UnknownLength => match other {
UrlencodedError::UnknownLength => true,
_ => false,
},
UrlencodedError::ContentType => match other {
UrlencodedError::ContentType => true,
_ => false,
},
_ => false, _ => false,
} }
} }

View File

@ -433,14 +433,8 @@ mod tests {
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool { fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
match err { match err {
JsonPayloadError::Overflow => match other { JsonPayloadError::Overflow => matches!(other, JsonPayloadError::Overflow),
JsonPayloadError::Overflow => true, JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),
_ => false,
},
JsonPayloadError::ContentType => match other {
JsonPayloadError::ContentType => true,
_ => false,
},
_ => false, _ => false,
} }
} }

View File

@ -25,8 +25,8 @@ use crate::FromRequest;
/// /// extract path info from "/{username}/{count}/index.html" url /// /// extract path info from "/{username}/{count}/index.html" url
/// /// {username} - deserializes to a String /// /// {username} - deserializes to a String
/// /// {count} - - deserializes to a u32 /// /// {count} - - deserializes to a u32
/// async fn index(info: web::Path<(String, u32)>) -> String { /// async fn index(web::Path((username, count)): web::Path<(String, u32)>) -> String {
/// format!("Welcome {}! {}", info.0, info.1) /// format!("Welcome {}! {}", username, count)
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -61,20 +61,18 @@ use crate::FromRequest;
/// ); /// );
/// } /// }
/// ``` /// ```
pub struct Path<T> { pub struct Path<T>(pub T);
inner: T,
}
impl<T> Path<T> { impl<T> Path<T> {
/// Deconstruct to an inner value /// Deconstruct to an inner value
pub fn into_inner(self) -> T { pub fn into_inner(self) -> T {
self.inner self.0
} }
} }
impl<T> AsRef<T> for Path<T> { impl<T> AsRef<T> for Path<T> {
fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
&self.inner &self.0
} }
} }
@ -82,31 +80,31 @@ impl<T> ops::Deref for Path<T> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
&self.inner &self.0
} }
} }
impl<T> ops::DerefMut for Path<T> { impl<T> ops::DerefMut for Path<T> {
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
&mut self.inner &mut self.0
} }
} }
impl<T> From<T> for Path<T> { impl<T> From<T> for Path<T> {
fn from(inner: T) -> Path<T> { fn from(inner: T) -> Path<T> {
Path { inner } Path(inner)
} }
} }
impl<T: fmt::Debug> fmt::Debug for Path<T> { impl<T: fmt::Debug> fmt::Debug for Path<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f) self.0.fmt(f)
} }
} }
impl<T: fmt::Display> fmt::Display for Path<T> { impl<T: fmt::Display> fmt::Display for Path<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f) self.0.fmt(f)
} }
} }
@ -120,8 +118,8 @@ impl<T: fmt::Display> fmt::Display for Path<T> {
/// /// extract path info from "/{username}/{count}/index.html" url /// /// extract path info from "/{username}/{count}/index.html" url
/// /// {username} - deserializes to a String /// /// {username} - deserializes to a String
/// /// {count} - - deserializes to a u32 /// /// {count} - - deserializes to a u32
/// async fn index(info: web::Path<(String, u32)>) -> String { /// async fn index(web::Path((username, count)): web::Path<(String, u32)>) -> String {
/// format!("Welcome {}! {}", info.0, info.1) /// format!("Welcome {}! {}", username, count)
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -173,7 +171,7 @@ where
ready( ready(
de::Deserialize::deserialize(PathDeserializer::new(req.match_info())) de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
.map(|inner| Path { inner }) .map(Path)
.map_err(move |e| { .map_err(move |e| {
log::debug!( log::debug!(
"Failed during Path extractor deserialization. \ "Failed during Path extractor deserialization. \
@ -290,21 +288,22 @@ mod tests {
resource.match_path(req.match_info_mut()); resource.match_path(req.match_info_mut());
let (req, mut pl) = req.into_parts(); let (req, mut pl) = req.into_parts();
let res = <(Path<(String, String)>,)>::from_request(&req, &mut pl) let (Path(res),) = <(Path<(String, String)>,)>::from_request(&req, &mut pl)
.await .await
.unwrap(); .unwrap();
assert_eq!((res.0).0, "name"); assert_eq!(res.0, "name");
assert_eq!((res.0).1, "user1"); assert_eq!(res.1, "user1");
let res = <(Path<(String, String)>, Path<(String, String)>)>::from_request( let (Path(a), Path(b)) =
<(Path<(String, String)>, Path<(String, String)>)>::from_request(
&req, &mut pl, &req, &mut pl,
) )
.await .await
.unwrap(); .unwrap();
assert_eq!((res.0).0, "name"); assert_eq!(a.0, "name");
assert_eq!((res.0).1, "user1"); assert_eq!(a.1, "user1");
assert_eq!((res.1).0, "name"); assert_eq!(b.0, "name");
assert_eq!((res.1).1, "user1"); assert_eq!(b.1, "user1");
let () = <()>::from_request(&req, &mut pl).await.unwrap(); let () = <()>::from_request(&req, &mut pl).await.unwrap();
} }
@ -329,7 +328,7 @@ mod tests {
let s = s.into_inner(); let s = s.into_inner();
assert_eq!(s.value, "user2"); assert_eq!(s.value, "user2");
let s = Path::<(String, String)>::from_request(&req, &mut pl) let Path(s) = Path::<(String, String)>::from_request(&req, &mut pl)
.await .await
.unwrap(); .unwrap();
assert_eq!(s.0, "name"); assert_eq!(s.0, "name");
@ -344,7 +343,7 @@ mod tests {
assert_eq!(s.as_ref().key, "name"); assert_eq!(s.as_ref().key, "name");
assert_eq!(s.value, 32); assert_eq!(s.value, 32);
let s = Path::<(String, u8)>::from_request(&req, &mut pl) let Path(s) = Path::<(String, u8)>::from_request(&req, &mut pl)
.await .await
.unwrap(); .unwrap();
assert_eq!(s.0, "name"); assert_eq!(s.0, "name");

View File

@ -13,10 +13,10 @@ use futures_util::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready};
use futures_util::StreamExt; use futures_util::StreamExt;
use mime::Mime; use mime::Mime;
use crate::dev;
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::http::header; use crate::http::header;
use crate::request::HttpRequest; use crate::request::HttpRequest;
use crate::{dev, web};
/// Payload extractor returns request 's payload stream. /// Payload extractor returns request 's payload stream.
/// ///
@ -142,13 +142,8 @@ impl FromRequest for Bytes {
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
let tmp; // allow both Config and Data<Config>
let cfg = if let Some(cfg) = req.app_data::<PayloadConfig>() { let cfg = PayloadConfig::from_req(req);
cfg
} else {
tmp = PayloadConfig::default();
&tmp
};
if let Err(e) = cfg.check_mimetype(req) { if let Err(e) = cfg.check_mimetype(req) {
return Either::Right(err(e)); return Either::Right(err(e));
@ -197,13 +192,7 @@ impl FromRequest for String {
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
let tmp; let cfg = PayloadConfig::from_req(req);
let cfg = if let Some(cfg) = req.app_data::<PayloadConfig>() {
cfg
} else {
tmp = PayloadConfig::default();
&tmp
};
// check content-type // check content-type
if let Err(e) = cfg.check_mimetype(req) { if let Err(e) = cfg.check_mimetype(req) {
@ -237,7 +226,12 @@ impl FromRequest for String {
) )
} }
} }
/// Payload configuration for request's payload.
/// Configuration for request's payload.
///
/// Applies to the built-in `Bytes` and `String` extractors. Note that the Payload extractor does
/// not automatically check conformance with this configuration to allow more flexibility when
/// building extractors on top of `Payload`.
#[derive(Clone)] #[derive(Clone)]
pub struct PayloadConfig { pub struct PayloadConfig {
limit: usize, limit: usize,
@ -284,14 +278,28 @@ impl PayloadConfig {
} }
Ok(()) Ok(())
} }
/// Allow payload config extraction from app data checking both `T` and `Data<T>`, in that
/// order, and falling back to the default payload config.
fn from_req(req: &HttpRequest) -> &PayloadConfig {
req.app_data::<PayloadConfig>()
.or_else(|| {
req.app_data::<web::Data<PayloadConfig>>()
.map(|d| d.as_ref())
})
.unwrap_or_else(|| &DEFAULT_PAYLOAD_CONFIG)
} }
}
// Allow shared refs to default.
static DEFAULT_PAYLOAD_CONFIG: PayloadConfig = PayloadConfig {
limit: 262_144, // 2^18 bytes (~256kB)
mimetype: None,
};
impl Default for PayloadConfig { impl Default for PayloadConfig {
fn default() -> Self { fn default() -> Self {
PayloadConfig { DEFAULT_PAYLOAD_CONFIG.clone()
limit: 262_144,
mimetype: None,
}
} }
} }
@ -407,8 +415,9 @@ mod tests {
use bytes::Bytes; use bytes::Bytes;
use super::*; use super::*;
use crate::http::header; use crate::http::{header, StatusCode};
use crate::test::TestRequest; use crate::test::{call_service, init_service, TestRequest};
use crate::{web, App, Responder};
#[actix_rt::test] #[actix_rt::test]
async fn test_payload_config() { async fn test_payload_config() {
@ -428,6 +437,86 @@ mod tests {
assert!(cfg.check_mimetype(&req).is_ok()); assert!(cfg.check_mimetype(&req).is_ok());
} }
#[actix_rt::test]
async fn test_config_recall_locations() {
async fn bytes_handler(_: Bytes) -> impl Responder {
"payload is probably json bytes"
}
async fn string_handler(_: String) -> impl Responder {
"payload is probably json string"
}
let mut srv = init_service(
App::new()
.service(
web::resource("/bytes-app-data")
.app_data(
PayloadConfig::default().mimetype(mime::APPLICATION_JSON),
)
.route(web::get().to(bytes_handler)),
)
.service(
web::resource("/bytes-data")
.data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
.route(web::get().to(bytes_handler)),
)
.service(
web::resource("/string-app-data")
.app_data(
PayloadConfig::default().mimetype(mime::APPLICATION_JSON),
)
.route(web::get().to(string_handler)),
)
.service(
web::resource("/string-data")
.data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
.route(web::get().to(string_handler)),
),
)
.await;
let req = TestRequest::with_uri("/bytes-app-data").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/bytes-data").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/string-app-data").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/string-data").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/bytes-app-data")
.header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
.to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/bytes-data")
.header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
.to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/string-app-data")
.header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
.to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/string-data")
.header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
.to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
}
#[actix_rt::test] #[actix_rt::test]
async fn test_bytes() { async fn test_bytes() {
let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "11") let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "11")