From 141790b200e895d0c69365f8c7a1b39c486019cd Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 21 Jan 2022 20:15:43 +0000 Subject: [PATCH] use camel case in special headers fixes #2595 --- actix-http/src/config.rs | 34 +++++++++++++---- actix-http/src/h1/encoder.rs | 4 +- actix-http/src/helpers.rs | 65 +++++++++++++++++++++----------- actix-http/src/responses/head.rs | 12 +++++- 4 files changed, 82 insertions(+), 33 deletions(-) diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 5d020edf..b6d5a7d5 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -174,12 +174,15 @@ impl ServiceConfig { } #[doc(hidden)] - pub fn set_date(&self, dst: &mut BytesMut) { + pub fn set_date(&self, dst: &mut BytesMut, camel_case: bool) { let mut buf: [u8; 39] = [0; 39]; - buf[..6].copy_from_slice(b"date: "); + + buf[..6].copy_from_slice(if camel_case { b"Date: " } else { b"date: " }); + self.0 .date_service .set_date(|date| buf[6..35].copy_from_slice(&date.bytes)); + buf[35..].copy_from_slice(b"\r\n\r\n"); dst.extend_from_slice(&buf); } @@ -326,6 +329,7 @@ mod tests { use super::*; use actix_rt::{task::yield_now, time::sleep}; + use memchr::memmem; #[actix_rt::test] async fn test_date_service_update() { @@ -334,7 +338,7 @@ mod tests { yield_now().await; let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); - settings.set_date(&mut buf1); + settings.set_date(&mut buf1, false); let now1 = settings.now(); sleep_until(Instant::now() + Duration::from_secs(2)).await; @@ -342,7 +346,7 @@ mod tests { let now2 = settings.now(); let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); - settings.set_date(&mut buf2); + settings.set_date(&mut buf2, false); assert_ne!(now1, now2); @@ -395,11 +399,27 @@ mod tests { #[actix_rt::test] async fn test_date() { - let settings = ServiceConfig::new(KeepAlive::Os, 0, 0, false, None); + let settings = ServiceConfig::default(); + let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); - settings.set_date(&mut buf1); + settings.set_date(&mut buf1, false); + let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); - settings.set_date(&mut buf2); + settings.set_date(&mut buf2, false); + assert_eq!(buf1, buf2); } + + #[actix_rt::test] + async fn test_date_camel_case() { + let settings = ServiceConfig::default(); + + let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); + settings.set_date(&mut buf, false); + assert!(memmem::find(&buf, b"date:").is_some()); + + let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); + settings.set_date(&mut buf, true); + assert!(memmem::find(&buf, b"Date:").is_some()); + } } diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 8b1e3b62..bd0de75b 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -105,7 +105,7 @@ pub(crate) trait MessageType: Sized { } BodySize::Sized(0) if camel_case => dst.put_slice(b"\r\nContent-Length: 0\r\n"), BodySize::Sized(0) => dst.put_slice(b"\r\ncontent-length: 0\r\n"), - BodySize::Sized(len) => helpers::write_content_length(len, dst), + BodySize::Sized(len) => helpers::write_content_length(len, dst, camel_case), BodySize::None => dst.put_slice(b"\r\n"), } @@ -213,7 +213,7 @@ pub(crate) trait MessageType: Sized { // optimized date header, set_date writes \r\n if !has_date { - config.set_date(dst); + config.set_date(dst, camel_case); } else { // msg eof dst.extend_from_slice(b"\r\n"); diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index cba94d9b..7f28018e 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -30,15 +30,25 @@ pub(crate) fn write_status_line(version: Version, n: u16, buf: &mut B /// Write out content length header. /// /// Buffer must to contain enough space or be implicitly extendable. -pub fn write_content_length(n: u64, buf: &mut B) { +pub fn write_content_length(n: u64, buf: &mut B, camel_case: bool) { if n == 0 { - buf.put_slice(b"\r\ncontent-length: 0\r\n"); + if camel_case { + buf.put_slice(b"\r\nContent-Length: 0\r\n"); + } else { + buf.put_slice(b"\r\ncontent-length: 0\r\n"); + } + return; } let mut buffer = itoa::Buffer::new(); - buf.put_slice(b"\r\ncontent-length: "); + if camel_case { + buf.put_slice(b"\r\nContent-Length: "); + } else { + buf.put_slice(b"\r\ncontent-length: "); + } + buf.put_slice(buffer.format(n).as_bytes()); buf.put_slice(b"\r\n"); } @@ -95,77 +105,88 @@ mod tests { fn test_write_content_length() { let mut bytes = BytesMut::new(); bytes.reserve(50); - write_content_length(0, &mut bytes); + write_content_length(0, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 0\r\n"[..]); bytes.reserve(50); - write_content_length(9, &mut bytes); + write_content_length(9, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 9\r\n"[..]); bytes.reserve(50); - write_content_length(10, &mut bytes); + write_content_length(10, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 10\r\n"[..]); bytes.reserve(50); - write_content_length(99, &mut bytes); + write_content_length(99, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 99\r\n"[..]); bytes.reserve(50); - write_content_length(100, &mut bytes); + write_content_length(100, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 100\r\n"[..]); bytes.reserve(50); - write_content_length(101, &mut bytes); + write_content_length(101, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 101\r\n"[..]); bytes.reserve(50); - write_content_length(998, &mut bytes); + write_content_length(998, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 998\r\n"[..]); bytes.reserve(50); - write_content_length(1000, &mut bytes); + write_content_length(1000, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 1000\r\n"[..]); bytes.reserve(50); - write_content_length(1001, &mut bytes); + write_content_length(1001, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 1001\r\n"[..]); bytes.reserve(50); - write_content_length(5909, &mut bytes); + write_content_length(5909, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 5909\r\n"[..]); bytes.reserve(50); - write_content_length(9999, &mut bytes); + write_content_length(9999, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 9999\r\n"[..]); bytes.reserve(50); - write_content_length(10001, &mut bytes); + write_content_length(10001, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 10001\r\n"[..]); bytes.reserve(50); - write_content_length(59094, &mut bytes); + write_content_length(59094, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 59094\r\n"[..]); bytes.reserve(50); - write_content_length(99999, &mut bytes); + write_content_length(99999, &mut bytes, false); assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 99999\r\n"[..]); bytes.reserve(50); - write_content_length(590947, &mut bytes); + write_content_length(590947, &mut bytes, false); assert_eq!( bytes.split().freeze(), b"\r\ncontent-length: 590947\r\n"[..] ); bytes.reserve(50); - write_content_length(999999, &mut bytes); + write_content_length(999999, &mut bytes, false); assert_eq!( bytes.split().freeze(), b"\r\ncontent-length: 999999\r\n"[..] ); bytes.reserve(50); - write_content_length(5909471, &mut bytes); + write_content_length(5909471, &mut bytes, false); assert_eq!( bytes.split().freeze(), b"\r\ncontent-length: 5909471\r\n"[..] ); bytes.reserve(50); - write_content_length(59094718, &mut bytes); + write_content_length(59094718, &mut bytes, false); assert_eq!( bytes.split().freeze(), b"\r\ncontent-length: 59094718\r\n"[..] ); bytes.reserve(50); - write_content_length(4294973728, &mut bytes); + write_content_length(4294973728, &mut bytes, false); assert_eq!( bytes.split().freeze(), b"\r\ncontent-length: 4294973728\r\n"[..] ); } + + #[test] + fn write_content_length_camel_case() { + let mut bytes = BytesMut::new(); + write_content_length(0, &mut bytes, false); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 0\r\n"[..]); + + let mut bytes = BytesMut::new(); + write_content_length(0, &mut bytes, true); + assert_eq!(bytes.split().freeze(), b"\r\nContent-Length: 0\r\n"[..]); + } } diff --git a/actix-http/src/responses/head.rs b/actix-http/src/responses/head.rs index 91e96a92..870073ab 100644 --- a/actix-http/src/responses/head.rs +++ b/actix-http/src/responses/head.rs @@ -240,15 +240,23 @@ mod tests { let _ = stream.read(&mut data); assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); assert!(memmem::find(&data, b"Foo-Bar").is_some()); - assert!(!memmem::find(&data, b"foo-bar").is_some()); + assert!(memmem::find(&data, b"foo-bar").is_none()); + assert!(memmem::find(&data, b"Date").is_some()); + assert!(memmem::find(&data, b"date").is_none()); + assert!(memmem::find(&data, b"Content-Length").is_some()); + assert!(memmem::find(&data, b"content-length").is_none()); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream.write_all(b"GET /lower HTTP/1.1\r\nConnection: Close\r\n\r\n"); let mut data = vec![0; 1024]; let _ = stream.read(&mut data); assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - assert!(!memmem::find(&data, b"Foo-Bar").is_some()); + assert!(memmem::find(&data, b"Foo-Bar").is_none()); assert!(memmem::find(&data, b"foo-bar").is_some()); + assert!(memmem::find(&data, b"Date").is_none()); + assert!(memmem::find(&data, b"date").is_some()); + assert!(memmem::find(&data, b"Content-Length").is_none()); + assert!(memmem::find(&data, b"content-length").is_some()); srv.stop().await; }