From 2cfa2d17a535bc75e77037a7a954c1e6f9c7452e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Sep 2021 16:46:33 +0100 Subject: [PATCH] update changelog --- CHANGES.md | 4 ++++ src/types/json.rs | 37 +++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d8831602d..b845e5901 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,14 @@ # Changes ## Unreleased - 2021-xx-xx +### Added +* Option to allow `Json` extractor to work without a `Content-Length` header present. [#2362] + ### Changed * Asscociated type `FromRequest::Config` was removed. [#2233] [#2233]: https://github.com/actix/actix-web/pull/2233 +[#2362]: https://github.com/actix/actix-web/pull/2362 ## 4.0.0-beta.9 - 2021-09-09 ### Added diff --git a/src/types/json.rs b/src/types/json.rs index 69a08b56a..5d5fd164b 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -127,7 +127,7 @@ impl Responder for Json { } /// See [here](#extractor) for example of usage as an extractor. -impl FromRequest for Json { +impl FromRequest for Json { type Error = Error; type Future = JsonExtractFut; @@ -136,13 +136,13 @@ impl FromRequest for Json { let config = JsonConfig::from_req(req); let limit = config.limit; - let content_type_required = config.content_type_required; - let ctype = config.content_type.as_deref(); + let ctype_required = config.content_type_required; + let ctype_fn = config.content_type.as_deref(); let err_handler = config.err_handler.clone(); JsonExtractFut { req: Some(req.clone()), - fut: JsonBody::new(req, payload, ctype, content_type_required).limit(limit), + fut: JsonBody::new(req, payload, ctype_fn, ctype_required).limit(limit), err_handler, } } @@ -157,7 +157,7 @@ pub struct JsonExtractFut { err_handler: JsonErrorHandler, } -impl Future for JsonExtractFut { +impl Future for JsonExtractFut { type Output = Result, Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -286,15 +286,18 @@ impl Default for JsonConfig { /// Future that resolves to some `T` when parsed from a JSON payload. /// -/// Form can be deserialized from any type `T` that implements [`serde::Deserialize`]. +/// Can deserialize any type `T` that implements [`Deserialize`][serde::Deserialize]. /// /// Returns error if: -/// - content type is not `application/json` -/// - content length is greater than [limit](JsonBody::limit()) +/// - `Content-Type` is not `application/json` when `ctype_required` (passed to [`new`][Self::new]) +/// is `true`. +/// - `Content-Length` is greater than [limit](JsonBody::limit()). +/// - The payload, when consumed, is not valid JSON. pub enum JsonBody { Error(Option), Body { limit: usize, + /// Length as reported by `Content-Length` header, if present. length: Option, #[cfg(feature = "__compress")] payload: Decompress, @@ -313,19 +316,21 @@ impl JsonBody { pub fn new( req: &HttpRequest, payload: &mut Payload, - ctype: Option<&(dyn Fn(mime::Mime) -> bool + Send + Sync)>, - content_type_required: bool, + ctype_fn: Option<&(dyn Fn(mime::Mime) -> bool + Send + Sync)>, + ctype_required: bool, ) -> Self { // check content-type - let has_valid_content_type = if let Ok(Some(mime)) = req.mime_type() { + let can_parse_json = if let Ok(Some(mime)) = req.mime_type() { mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON) - || ctype.map_or(false, |predicate| predicate(mime)) + || ctype_fn.map_or(false, |predicate| predicate(mime)) } else { - !content_type_required + // if `ctype_required` is false, assume payload is + // json even when content-type header is missing + !ctype_required }; - if !has_valid_content_type { + if !can_parse_json { return JsonBody::Error(Some(JsonPayloadError::ContentType)); } @@ -335,7 +340,7 @@ impl JsonBody { .and_then(|l| l.to_str().ok()) .and_then(|s| s.parse::().ok()); - // Notice the content_length is not checked against limit of json config here. + // Notice the content-length is not checked against limit of json config here. // As the internal usage always call JsonBody::limit after JsonBody::new. // And limit check to return an error variant of JsonBody happens there. @@ -389,7 +394,7 @@ impl JsonBody { } } -impl Future for JsonBody { +impl Future for JsonBody { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {