mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into scope-data
This commit is contained in:
commit
abc58e2a12
|
@ -5,11 +5,15 @@
|
||||||
* `HttpServer::worker_max_blocking_threads` for setting block thread pool. [#2200]
|
* `HttpServer::worker_max_blocking_threads` for setting block thread pool. [#2200]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
* Adjusted default JSON payload limit to 2MB (from 32kb) and included size and limits in the `JsonPayloadError::Overflow` error variant. [#2162]
|
||||||
|
[#2162]: (https://github.com/actix/actix-web/pull/2162)
|
||||||
* `ServiceResponse::error_response` now uses body type of `Body`. [#2201]
|
* `ServiceResponse::error_response` now uses body type of `Body`. [#2201]
|
||||||
* `ServiceResponse::checked_expr` now returns a `Result`. [#2201]
|
* `ServiceResponse::checked_expr` now returns a `Result`. [#2201]
|
||||||
* Update `language-tags` to `0.3`.
|
* Update `language-tags` to `0.3`.
|
||||||
* `ServiceResponse::take_body`. [#2201]
|
* `ServiceResponse::take_body`. [#2201]
|
||||||
* `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody<B>` types. [#2201]
|
* `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody<B>` types. [#2201]
|
||||||
|
* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226]
|
||||||
* `middleware::normalize` now will not try to normalize URIs with no valid path [#2246]
|
* `middleware::normalize` now will not try to normalize URIs with no valid path [#2246]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
* `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135]
|
* `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135]
|
||||||
* For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156]
|
* For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156]
|
||||||
* `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225]
|
* `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225]
|
||||||
|
* `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257]
|
||||||
|
|
||||||
[#2135]: https://github.com/actix/actix-web/pull/2135
|
[#2135]: https://github.com/actix/actix-web/pull/2135
|
||||||
[#2156]: https://github.com/actix/actix-web/pull/2156
|
[#2156]: https://github.com/actix/actix-web/pull/2156
|
||||||
[#2225]: https://github.com/actix/actix-web/pull/2225
|
[#2225]: https://github.com/actix/actix-web/pull/2225
|
||||||
|
[#2257]: https://github.com/actix/actix-web/pull/2257
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.4 - 2021-04-02
|
## 0.6.0-beta.4 - 2021-04-02
|
||||||
|
|
|
@ -279,6 +279,22 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_named_file_javascript() {
|
||||||
|
let file = NamedFile::open("tests/test.js").unwrap();
|
||||||
|
|
||||||
|
let req = TestRequest::default().to_http_request();
|
||||||
|
let resp = file.respond_to(&req).await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
"application/javascript"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||||
|
"inline; filename=\"test.js\""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_named_file_image_attachment() {
|
async fn test_named_file_image_attachment() {
|
||||||
let cd = ContentDisposition {
|
let cd = ContentDisposition {
|
||||||
|
|
|
@ -120,6 +120,11 @@ impl NamedFile {
|
||||||
|
|
||||||
let disposition = match ct.type_() {
|
let disposition = match ct.type_() {
|
||||||
mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
|
mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
|
||||||
|
mime::APPLICATION => match ct.subtype() {
|
||||||
|
mime::JAVASCRIPT | mime::JSON => DispositionType::Inline,
|
||||||
|
name if name == "wasm" => DispositionType::Inline,
|
||||||
|
_ => DispositionType::Attachment,
|
||||||
|
},
|
||||||
_ => DispositionType::Attachment,
|
_ => DispositionType::Attachment,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -213,9 +218,11 @@ impl NamedFile {
|
||||||
|
|
||||||
/// Set the Content-Disposition for serving this file. This allows
|
/// Set the Content-Disposition for serving this file. This allows
|
||||||
/// changing the inline/attachment disposition as well as the filename
|
/// changing the inline/attachment disposition as well as the filename
|
||||||
/// sent to the peer. By default the disposition is `inline` for text,
|
/// sent to the peer.
|
||||||
/// image, and video content types, and `attachment` otherwise, and
|
///
|
||||||
/// the filename is taken from the path provided in the `open` method
|
/// By default the disposition is `inline` for `text/*`, `image/*`, `video/*` and
|
||||||
|
/// `application/{javascript, json, wasm}` mime types, and `attachment` otherwise,
|
||||||
|
/// and the filename is taken from the path provided in the `open` method
|
||||||
/// after converting it to UTF-8 using.
|
/// after converting it to UTF-8 using.
|
||||||
/// [`std::ffi::OsStr::to_string_lossy`]
|
/// [`std::ffi::OsStr::to_string_lossy`]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
// this file is empty.
|
|
@ -19,6 +19,7 @@
|
||||||
* Update `language-tags` to `0.3`.
|
* Update `language-tags` to `0.3`.
|
||||||
* Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201]
|
* Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201]
|
||||||
* `ResponseBuilder::message_body` now returns a `Result`. [#2201]
|
* `ResponseBuilder::message_body` now returns a `Result`. [#2201]
|
||||||
|
* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
* Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171]
|
* Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171]
|
||||||
|
|
|
@ -91,6 +91,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tls-openssl = { version = "0.10", package = "openssl" }
|
tls-openssl = { version = "0.10", package = "openssl" }
|
||||||
tls-rustls = { version = "0.19", package = "rustls" }
|
tls-rustls = { version = "0.19", package = "rustls" }
|
||||||
|
webpki = { version = "0.21.0" }
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "ws"
|
name = "ws"
|
||||||
|
|
|
@ -171,7 +171,8 @@ mod rustls {
|
||||||
Error = TlsError<io::Error, DispatchError>,
|
Error = TlsError<io::Error, DispatchError>,
|
||||||
InitError = S::InitError,
|
InitError = S::InitError,
|
||||||
> {
|
> {
|
||||||
let protos = vec!["h2".to_string().into()];
|
let mut protos = vec![b"h2".to_vec()];
|
||||||
|
protos.extend_from_slice(&config.alpn_protocols);
|
||||||
config.set_protocols(&protos);
|
config.set_protocols(&protos);
|
||||||
|
|
||||||
Acceptor::new(config)
|
Acceptor::new(config)
|
||||||
|
|
|
@ -305,7 +305,8 @@ mod rustls {
|
||||||
Error = TlsError<io::Error, DispatchError>,
|
Error = TlsError<io::Error, DispatchError>,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
> {
|
> {
|
||||||
let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()];
|
let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||||
|
protos.extend_from_slice(&config.alpn_protocols);
|
||||||
config.set_protocols(&protos);
|
config.set_protocols(&protos);
|
||||||
|
|
||||||
Acceptor::new(config)
|
Acceptor::new(config)
|
||||||
|
|
|
@ -20,10 +20,15 @@ use futures_core::Stream;
|
||||||
use futures_util::stream::{once, StreamExt as _};
|
use futures_util::stream::{once, StreamExt as _};
|
||||||
use rustls::{
|
use rustls::{
|
||||||
internal::pemfile::{certs, pkcs8_private_keys},
|
internal::pemfile::{certs, pkcs8_private_keys},
|
||||||
NoClientAuth, ServerConfig as RustlsServerConfig,
|
NoClientAuth, ServerConfig as RustlsServerConfig, Session,
|
||||||
};
|
};
|
||||||
|
use webpki::DNSNameRef;
|
||||||
|
|
||||||
use std::io::{self, BufReader};
|
use std::{
|
||||||
|
io::{self, BufReader, Write},
|
||||||
|
net::{SocketAddr, TcpStream as StdTcpStream},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
async fn load_body<S>(mut stream: S) -> Result<BytesMut, PayloadError>
|
async fn load_body<S>(mut stream: S) -> Result<BytesMut, PayloadError>
|
||||||
where
|
where
|
||||||
|
@ -52,6 +57,25 @@ fn tls_config() -> RustlsServerConfig {
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_negotiated_alpn_protocol(
|
||||||
|
addr: SocketAddr,
|
||||||
|
client_alpn_protocol: &[u8],
|
||||||
|
) -> Option<Vec<u8>> {
|
||||||
|
let mut config = rustls::ClientConfig::new();
|
||||||
|
config.alpn_protocols.push(client_alpn_protocol.to_vec());
|
||||||
|
let mut sess = rustls::ClientSession::new(
|
||||||
|
&Arc::new(config),
|
||||||
|
DNSNameRef::try_from_ascii_str("localhost").unwrap(),
|
||||||
|
);
|
||||||
|
let mut sock = StdTcpStream::connect(addr).unwrap();
|
||||||
|
let mut stream = rustls::Stream::new(&mut sess, &mut sock);
|
||||||
|
// The handshake will fails because the client will not be able to verify the server
|
||||||
|
// certificate, but it doesn't matter here as we are just interested in the negotiated ALPN
|
||||||
|
// protocol
|
||||||
|
let _ = stream.flush();
|
||||||
|
sess.get_alpn_protocol().map(|proto| proto.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_h1() -> io::Result<()> {
|
async fn test_h1() -> io::Result<()> {
|
||||||
let srv = test_server(move || {
|
let srv = test_server(move || {
|
||||||
|
@ -460,3 +484,85 @@ async fn test_h1_service_error() {
|
||||||
let bytes = srv.load_body(response).await.unwrap();
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
assert_eq!(bytes, Bytes::from_static(b"error"));
|
assert_eq!(bytes, Bytes::from_static(b"error"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const H2_ALPN_PROTOCOL: &[u8] = b"h2";
|
||||||
|
const HTTP1_1_ALPN_PROTOCOL: &[u8] = b"http/1.1";
|
||||||
|
const CUSTOM_ALPN_PROTOCOL: &[u8] = b"custom";
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_alpn_h1() -> io::Result<()> {
|
||||||
|
let srv = test_server(move || {
|
||||||
|
let mut config = tls_config();
|
||||||
|
config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());
|
||||||
|
HttpService::build()
|
||||||
|
.h1(|_| ok::<_, Error>(Response::ok()))
|
||||||
|
.rustls(config)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_negotiated_alpn_protocol(srv.addr(), CUSTOM_ALPN_PROTOCOL),
|
||||||
|
Some(CUSTOM_ALPN_PROTOCOL.to_vec())
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_alpn_h2() -> io::Result<()> {
|
||||||
|
let srv = test_server(move || {
|
||||||
|
let mut config = tls_config();
|
||||||
|
config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());
|
||||||
|
HttpService::build()
|
||||||
|
.h2(|_| ok::<_, Error>(Response::ok()))
|
||||||
|
.rustls(config)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_negotiated_alpn_protocol(srv.addr(), H2_ALPN_PROTOCOL),
|
||||||
|
Some(H2_ALPN_PROTOCOL.to_vec())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_negotiated_alpn_protocol(srv.addr(), CUSTOM_ALPN_PROTOCOL),
|
||||||
|
Some(CUSTOM_ALPN_PROTOCOL.to_vec())
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_alpn_h2_1() -> io::Result<()> {
|
||||||
|
let srv = test_server(move || {
|
||||||
|
let mut config = tls_config();
|
||||||
|
config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());
|
||||||
|
HttpService::build()
|
||||||
|
.finish(|_| ok::<_, Error>(Response::ok()))
|
||||||
|
.rustls(config)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_negotiated_alpn_protocol(srv.addr(), H2_ALPN_PROTOCOL),
|
||||||
|
Some(H2_ALPN_PROTOCOL.to_vec())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_negotiated_alpn_protocol(srv.addr(), HTTP1_1_ALPN_PROTOCOL),
|
||||||
|
Some(HTTP1_1_ALPN_PROTOCOL.to_vec())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_negotiated_alpn_protocol(srv.addr(), CUSTOM_ALPN_PROTOCOL),
|
||||||
|
Some(CUSTOM_ALPN_PROTOCOL.to_vec())
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = srv.sget("/").send().await.unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -93,9 +93,17 @@ impl ResponseError for UrlencodedError {
|
||||||
#[derive(Debug, Display, Error)]
|
#[derive(Debug, Display, Error)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum JsonPayloadError {
|
pub enum JsonPayloadError {
|
||||||
/// Payload size is bigger than allowed. (default: 32kB)
|
/// Payload size is bigger than allowed & content length header set. (default: 2MB)
|
||||||
#[display(fmt = "Json payload size is bigger than allowed")]
|
#[display(
|
||||||
Overflow,
|
fmt = "JSON payload ({} bytes) is larger than allowed (limit: {} bytes).",
|
||||||
|
length,
|
||||||
|
limit
|
||||||
|
)]
|
||||||
|
OverflowKnownLength { length: usize, limit: usize },
|
||||||
|
|
||||||
|
/// Payload size is bigger than allowed but no content length header set. (default: 2MB)
|
||||||
|
#[display(fmt = "JSON payload has exceeded limit ({} bytes).", limit)]
|
||||||
|
Overflow { limit: usize },
|
||||||
|
|
||||||
/// Content type error
|
/// Content type error
|
||||||
#[display(fmt = "Content type error")]
|
#[display(fmt = "Content type error")]
|
||||||
|
@ -123,7 +131,11 @@ impl From<PayloadError> for JsonPayloadError {
|
||||||
impl ResponseError for JsonPayloadError {
|
impl ResponseError for JsonPayloadError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
Self::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
|
Self::OverflowKnownLength {
|
||||||
|
length: _,
|
||||||
|
limit: _,
|
||||||
|
} => StatusCode::PAYLOAD_TOO_LARGE,
|
||||||
|
Self::Overflow { limit: _ } => StatusCode::PAYLOAD_TOO_LARGE,
|
||||||
Self::Serialize(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
Self::Serialize(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
Self::Payload(err) => err.status_code(),
|
Self::Payload(err) => err.status_code(),
|
||||||
_ => StatusCode::BAD_REQUEST,
|
_ => StatusCode::BAD_REQUEST,
|
||||||
|
@ -208,7 +220,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_json_payload_error() {
|
fn test_json_payload_error() {
|
||||||
let resp = JsonPayloadError::Overflow.error_response();
|
let resp = JsonPayloadError::OverflowKnownLength {
|
||||||
|
length: 0,
|
||||||
|
limit: 0,
|
||||||
|
}
|
||||||
|
.error_response();
|
||||||
|
assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
||||||
|
let resp = JsonPayloadError::Overflow { limit: 0 }.error_response();
|
||||||
assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
||||||
let resp = JsonPayloadError::ContentType.error_response();
|
let resp = JsonPayloadError::ContentType.error_response();
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
|
@ -368,7 +368,7 @@ where
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
/// Use listener for accepting incoming tls connection requests
|
/// Use listener for accepting incoming tls connection requests
|
||||||
///
|
///
|
||||||
/// This method sets alpn protocols to "h2" and "http/1.1"
|
/// This method prepends alpn protocols "h2" and "http/1.1" to configured ones
|
||||||
pub fn listen_rustls(
|
pub fn listen_rustls(
|
||||||
self,
|
self,
|
||||||
lst: net::TcpListener,
|
lst: net::TcpListener,
|
||||||
|
@ -482,7 +482,7 @@ where
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
/// Start listening for incoming tls connections.
|
/// Start listening for incoming tls connections.
|
||||||
///
|
///
|
||||||
/// This method sets alpn protocols to "h2" and "http/1.1"
|
/// This method prepends alpn protocols "h2" and "http/1.1" to configured ones
|
||||||
pub fn bind_rustls<A: net::ToSocketAddrs>(
|
pub fn bind_rustls<A: net::ToSocketAddrs>(
|
||||||
mut self,
|
mut self,
|
||||||
addr: A,
|
addr: A,
|
||||||
|
|
|
@ -240,7 +240,7 @@ pub struct JsonConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JsonConfig {
|
impl JsonConfig {
|
||||||
/// Set maximum accepted payload size. By default this limit is 32kB.
|
/// Set maximum accepted payload size. By default this limit is 2MB.
|
||||||
pub fn limit(mut self, limit: usize) -> Self {
|
pub fn limit(mut self, limit: usize) -> Self {
|
||||||
self.limit = limit;
|
self.limit = limit;
|
||||||
self
|
self
|
||||||
|
@ -273,9 +273,11 @@ impl JsonConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_LIMIT: usize = 2_097_152; // 2 mb
|
||||||
|
|
||||||
/// Allow shared refs used as default.
|
/// Allow shared refs used as default.
|
||||||
const DEFAULT_CONFIG: JsonConfig = JsonConfig {
|
const DEFAULT_CONFIG: JsonConfig = JsonConfig {
|
||||||
limit: 32_768, // 2^15 bytes, (~32kB)
|
limit: DEFAULT_LIMIT,
|
||||||
err_handler: None,
|
err_handler: None,
|
||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
|
@ -349,7 +351,7 @@ where
|
||||||
let payload = payload.take();
|
let payload = payload.take();
|
||||||
|
|
||||||
JsonBody::Body {
|
JsonBody::Body {
|
||||||
limit: 32_768,
|
limit: DEFAULT_LIMIT,
|
||||||
length,
|
length,
|
||||||
payload,
|
payload,
|
||||||
buf: BytesMut::with_capacity(8192),
|
buf: BytesMut::with_capacity(8192),
|
||||||
|
@ -357,7 +359,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set maximum accepted payload size. The default limit is 32kB.
|
/// Set maximum accepted payload size. The default limit is 2MB.
|
||||||
pub fn limit(self, limit: usize) -> Self {
|
pub fn limit(self, limit: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
JsonBody::Body {
|
JsonBody::Body {
|
||||||
|
@ -368,7 +370,10 @@ where
|
||||||
} => {
|
} => {
|
||||||
if let Some(len) = length {
|
if let Some(len) = length {
|
||||||
if len > limit {
|
if len > limit {
|
||||||
return JsonBody::Error(Some(JsonPayloadError::Overflow));
|
return JsonBody::Error(Some(JsonPayloadError::OverflowKnownLength {
|
||||||
|
length: len,
|
||||||
|
limit,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,8 +410,11 @@ where
|
||||||
match res {
|
match res {
|
||||||
Some(chunk) => {
|
Some(chunk) => {
|
||||||
let chunk = chunk?;
|
let chunk = chunk?;
|
||||||
if (buf.len() + chunk.len()) > *limit {
|
let buf_len = buf.len() + chunk.len();
|
||||||
return Poll::Ready(Err(JsonPayloadError::Overflow));
|
if buf_len > *limit {
|
||||||
|
return Poll::Ready(Err(JsonPayloadError::Overflow {
|
||||||
|
limit: *limit,
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
buf.extend_from_slice(&chunk);
|
buf.extend_from_slice(&chunk);
|
||||||
}
|
}
|
||||||
|
@ -445,7 +453,12 @@ mod tests {
|
||||||
|
|
||||||
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
|
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
|
||||||
match err {
|
match err {
|
||||||
JsonPayloadError::Overflow => matches!(other, JsonPayloadError::Overflow),
|
JsonPayloadError::Overflow { .. } => {
|
||||||
|
matches!(other, JsonPayloadError::Overflow { .. })
|
||||||
|
}
|
||||||
|
JsonPayloadError::OverflowKnownLength { .. } => {
|
||||||
|
matches!(other, JsonPayloadError::OverflowKnownLength { .. })
|
||||||
|
}
|
||||||
JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),
|
JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -538,7 +551,7 @@ mod tests {
|
||||||
|
|
||||||
let s = Json::<MyObject>::from_request(&req, &mut pl).await;
|
let s = Json::<MyObject>::from_request(&req, &mut pl).await;
|
||||||
assert!(format!("{}", s.err().unwrap())
|
assert!(format!("{}", s.err().unwrap())
|
||||||
.contains("Json payload size is bigger than allowed"));
|
.contains("JSON payload (16 bytes) is larger than allowed (limit: 10 bytes)."));
|
||||||
|
|
||||||
let (req, mut pl) = TestRequest::default()
|
let (req, mut pl) = TestRequest::default()
|
||||||
.insert_header((
|
.insert_header((
|
||||||
|
@ -589,7 +602,30 @@ mod tests {
|
||||||
let json = JsonBody::<MyObject>::new(&req, &mut pl, None)
|
let json = JsonBody::<MyObject>::new(&req, &mut pl, None)
|
||||||
.limit(100)
|
.limit(100)
|
||||||
.await;
|
.await;
|
||||||
assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow));
|
assert!(json_eq(
|
||||||
|
json.err().unwrap(),
|
||||||
|
JsonPayloadError::OverflowKnownLength {
|
||||||
|
length: 10000,
|
||||||
|
limit: 100
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
let (req, mut pl) = TestRequest::default()
|
||||||
|
.insert_header((
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
header::HeaderValue::from_static("application/json"),
|
||||||
|
))
|
||||||
|
.set_payload(Bytes::from_static(&[0u8; 1000]))
|
||||||
|
.to_http_parts();
|
||||||
|
|
||||||
|
let json = JsonBody::<MyObject>::new(&req, &mut pl, None)
|
||||||
|
.limit(100)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(json_eq(
|
||||||
|
json.err().unwrap(),
|
||||||
|
JsonPayloadError::Overflow { limit: 100 }
|
||||||
|
));
|
||||||
|
|
||||||
let (req, mut pl) = TestRequest::default()
|
let (req, mut pl) = TestRequest::default()
|
||||||
.insert_header((
|
.insert_header((
|
||||||
|
@ -686,6 +722,7 @@ mod tests {
|
||||||
assert!(s.is_err());
|
assert!(s.is_err());
|
||||||
|
|
||||||
let err_str = s.err().unwrap().to_string();
|
let err_str = s.err().unwrap().to_string();
|
||||||
assert!(err_str.contains("Json payload size is bigger than allowed"));
|
assert!(err_str
|
||||||
|
.contains("JSON payload (16 bytes) is larger than allowed (limit: 10 bytes)."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue