1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-23 16:21:06 +01:00

Adjust JSON limit to 2MB and report on sizes (#2162)

Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
peter-formlogic 2021-06-17 00:22:49 +09:30 committed by GitHub
parent 75f65fea4f
commit fb2b362b60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 74 additions and 16 deletions

View File

@ -5,6 +5,9 @@
* `HttpServer::worker_max_blocking_threads` for setting block thread pool. [#2200]
### 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::checked_expr` now returns a `Result`. [#2201]
* Update `language-tags` to `0.3`.

View File

@ -93,9 +93,17 @@ impl ResponseError for UrlencodedError {
#[derive(Debug, Display, Error)]
#[non_exhaustive]
pub enum JsonPayloadError {
/// Payload size is bigger than allowed. (default: 32kB)
#[display(fmt = "Json payload size is bigger than allowed")]
Overflow,
/// Payload size is bigger than allowed & content length header set. (default: 2MB)
#[display(
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
#[display(fmt = "Content type error")]
@ -123,7 +131,11 @@ impl From<PayloadError> for JsonPayloadError {
impl ResponseError for JsonPayloadError {
fn status_code(&self) -> StatusCode {
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::Payload(err) => err.status_code(),
_ => StatusCode::BAD_REQUEST,
@ -208,7 +220,13 @@ mod tests {
#[test]
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);
let resp = JsonPayloadError::ContentType.error_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);

View File

@ -240,7 +240,7 @@ pub struct 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 {
self.limit = limit;
self
@ -273,9 +273,11 @@ impl JsonConfig {
}
}
const DEFAULT_LIMIT: usize = 2_097_152; // 2 mb
/// Allow shared refs used as default.
const DEFAULT_CONFIG: JsonConfig = JsonConfig {
limit: 32_768, // 2^15 bytes, (~32kB)
limit: DEFAULT_LIMIT,
err_handler: None,
content_type: None,
};
@ -349,7 +351,7 @@ where
let payload = payload.take();
JsonBody::Body {
limit: 32_768,
limit: DEFAULT_LIMIT,
length,
payload,
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 {
match self {
JsonBody::Body {
@ -368,7 +370,10 @@ where
} => {
if let Some(len) = length {
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 {
Some(chunk) => {
let chunk = chunk?;
if (buf.len() + chunk.len()) > *limit {
return Poll::Ready(Err(JsonPayloadError::Overflow));
let buf_len = buf.len() + chunk.len();
if buf_len > *limit {
return Poll::Ready(Err(JsonPayloadError::Overflow {
limit: *limit,
}));
} else {
buf.extend_from_slice(&chunk);
}
@ -445,7 +453,12 @@ mod tests {
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
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),
_ => false,
}
@ -538,7 +551,7 @@ mod tests {
let s = Json::<MyObject>::from_request(&req, &mut pl).await;
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()
.insert_header((
@ -589,7 +602,30 @@ mod tests {
let json = JsonBody::<MyObject>::new(&req, &mut pl, None)
.limit(100)
.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()
.insert_header((
@ -686,6 +722,7 @@ mod tests {
assert!(s.is_err());
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)."));
}
}