From 6a0ea51b15fc8df2cdbf3438c334486def7a8eda Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 22 Jul 2023 03:16:01 +0100 Subject: [PATCH] add `ContentLength` typed header (#2490) --- actix-web/CHANGES.md | 1 + actix-web/src/http/header/allow.rs | 8 ++- actix-web/src/http/header/content_length.rs | 56 +++++++++++++++++++++ actix-web/src/http/header/content_range.rs | 1 - actix-web/src/http/header/mod.rs | 2 + actix-web/src/types/json.rs | 8 +-- 6 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 actix-web/src/http/header/content_length.rs diff --git a/actix-web/CHANGES.md b/actix-web/CHANGES.md index f22a815c3..39535d41f 100644 --- a/actix-web/CHANGES.md +++ b/actix-web/CHANGES.md @@ -8,6 +8,7 @@ - Add `Resource::{get, post, etc...}` methods for more concisely adding routes that don't need additional guards. - Add `web::Payload::to_bytes[_limited]()` helper methods. - Add missing constructors on `HttpResponse` for several status codes. +- Add `http::header::ContentLength` typed header. ### Changed diff --git a/actix-web/src/http/header/allow.rs b/actix-web/src/http/header/allow.rs index 1ae8f00dc..b1c35c3d7 100644 --- a/actix-web/src/http/header/allow.rs +++ b/actix-web/src/http/header/allow.rs @@ -48,12 +48,15 @@ crate::http::header::common_header! { (Allow, header::ALLOW) => (Method)* test_parse_and_format { - // From the RFC + // from the RFC + crate::http::header::common_header_test!( test1, [b"GET, HEAD, PUT"], Some(HeaderField(vec![Method::GET, Method::HEAD, Method::PUT]))); - // Own tests + + // other tests + crate::http::header::common_header_test!( test2, [b"OPTIONS, GET, PUT, POST, DELETE, HEAD, TRACE, CONNECT, PATCH"], @@ -67,6 +70,7 @@ crate::http::header::common_header! { Method::TRACE, Method::CONNECT, Method::PATCH]))); + crate::http::header::common_header_test!( test3, [b""], diff --git a/actix-web/src/http/header/content_length.rs b/actix-web/src/http/header/content_length.rs new file mode 100644 index 000000000..b2769e5fa --- /dev/null +++ b/actix-web/src/http/header/content_length.rs @@ -0,0 +1,56 @@ +use super::common_header; +use crate::http::header; + +common_header! { + /// `Content-Length` header, defined in [RFC 7230 §3.3.2]. + /// + /// The Content-Length + /// + /// # ABNF + /// + /// ```plain + /// Content-Length = 1*DIGIT + /// ``` + /// + /// # Example Values + /// + /// - `0` + /// - `3495` + /// + /// # Examples + /// + /// ``` + /// use actix_web::{http::header::ContentLength, HttpResponse}; + /// + /// let res_empty = HttpResponse::Ok() + /// .insert_header(ContentLength(0)); + /// + /// let res_fake_cl = HttpResponse::Ok() + /// .insert_header(ContentLength(3_495)); + /// ``` + /// + /// [RFC 7230 §3.3.2]: https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 + (ContentLength, header::CONTENT_LENGTH) => [usize] + + test_parse_and_format { + common_header_test!(no_header, [b""; 0], None); + common_header_test!(empty_header, [b""; 1], None); + + common_header_test!(zero, [b"0"], Some(ContentLength(0))); + common_header_test!(one, [b"1"], Some(ContentLength(1))); + common_header_test!(one_two_three, [b"123"], Some(ContentLength(123))); + common_header_test!( + thirty_two_power_plus_one, + [b"4294967297"], + Some(ContentLength(4_294_967_297)) + ); + common_header_test!( + sixty_four_power_minus_one, + [b"18446744073709551615"], + Some(ContentLength(18_446_744_073_709_551_615)) + ); + + common_header_test!(invalid1, [b"123,567"], None); + common_header_test!(invalid2, [b"123_567"], None); + } +} diff --git a/actix-web/src/http/header/content_range.rs b/actix-web/src/http/header/content_range.rs index 884926195..2604f9ba2 100644 --- a/actix-web/src/http/header/content_range.rs +++ b/actix-web/src/http/header/content_range.rs @@ -67,7 +67,6 @@ crate::http::header::common_header! { crate::http::header::common_header_test!(test_bytes_many_dashes, [b"bytes 1-2-3/500"], None::); - } } diff --git a/actix-web/src/http/header/mod.rs b/actix-web/src/http/header/mod.rs index 65c5c88a0..51ac4fcfd 100644 --- a/actix-web/src/http/header/mod.rs +++ b/actix-web/src/http/header/mod.rs @@ -24,6 +24,7 @@ mod allow; mod cache_control; mod content_disposition; mod content_language; +mod content_length; mod content_range; mod content_type; mod date; @@ -53,6 +54,7 @@ pub use self::{ cache_control::{CacheControl, CacheDirective}, content_disposition::{ContentDisposition, DispositionParam, DispositionType}, content_language::ContentLanguage, + content_length::ContentLength, content_range::{ContentRange, ContentRangeSpec}, content_type::ContentType, date::Date, diff --git a/actix-web/src/types/json.rs b/actix-web/src/types/json.rs index 2523e55f2..59c95da4c 100644 --- a/actix-web/src/types/json.rs +++ b/actix-web/src/types/json.rs @@ -21,7 +21,7 @@ use crate::{ body::EitherBody, error::{Error, JsonPayloadError}, extract::FromRequest, - http::header::CONTENT_LENGTH, + http::header::{ContentLength, Header as _}, request::HttpRequest, web, HttpMessage, HttpResponse, Responder, }; @@ -342,11 +342,7 @@ impl JsonBody { return JsonBody::Error(Some(JsonPayloadError::ContentType)); } - let length = req - .headers() - .get(&CONTENT_LENGTH) - .and_then(|l| l.to_str().ok()) - .and_then(|s| s.parse::().ok()); + let length = ContentLength::parse(req).ok().map(|x| x.0); // Notice the content-length is not checked against limit of json config here. // As the internal usage always call JsonBody::limit after JsonBody::new.