From 2e19f572ee9b4270508408fd9521dffb22eb773e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 24 Apr 2019 11:27:57 -0700 Subject: [PATCH] add tests for camel case headers rendering --- actix-http/CHANGES.md | 5 ++ actix-http/src/h1/encoder.rs | 117 ++++++++++++++++++++++++++++---- actix-http/src/message.rs | 4 +- actix-http/tests/test_client.rs | 2 - awc/CHANGES.md | 4 ++ awc/src/request.rs | 13 +--- awc/tests/test_client.rs | 4 ++ 7 files changed, 123 insertions(+), 26 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 37d0eec65..c51b421c3 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,10 @@ # Changes +### Added + +* Allow to render h1 request headers in `Camel-Case` + + ## [0.1.3] - 2019-04-23 ### Fixed diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 177661b5b..60bf2262b 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -43,7 +43,7 @@ pub(crate) trait MessageType: Sized { fn headers(&self) -> &HeaderMap; - fn upper_camel_case(&self) -> bool { + fn camel_case(&self) -> bool { false } @@ -61,6 +61,7 @@ pub(crate) trait MessageType: Sized { ) -> io::Result<()> { let chunked = self.chunked(); let mut skip_len = length != BodySize::Stream; + let camel_case = self.camel_case(); // Content length if let Some(status) = self.status() { @@ -78,18 +79,30 @@ pub(crate) trait MessageType: Sized { match length { BodySize::Stream => { if chunked { - dst.put_slice(b"\r\ntransfer-encoding: chunked\r\n") + if camel_case { + dst.put_slice(b"\r\nTransfer-Encoding: chunked\r\n") + } else { + dst.put_slice(b"\r\nTransfer-Encoding: chunked\r\n") + } } else { skip_len = false; dst.put_slice(b"\r\n"); } } BodySize::Empty => { - dst.put_slice(b"\r\ncontent-length: 0\r\n"); + if camel_case { + dst.put_slice(b"\r\nContent-Length: 0\r\n"); + } else { + dst.put_slice(b"\r\ncontent-length: 0\r\n"); + } } BodySize::Sized(len) => helpers::write_content_length(len, dst), BodySize::Sized64(len) => { - dst.put_slice(b"\r\ncontent-length: "); + if camel_case { + dst.put_slice(b"\r\nContent-Length: "); + } else { + dst.put_slice(b"\r\ncontent-length: "); + } write!(dst.writer(), "{}\r\n", len)?; } BodySize::None => dst.put_slice(b"\r\n"), @@ -99,10 +112,18 @@ pub(crate) trait MessageType: Sized { match ctype { ConnectionType::Upgrade => dst.put_slice(b"connection: upgrade\r\n"), ConnectionType::KeepAlive if version < Version::HTTP_11 => { - dst.put_slice(b"connection: keep-alive\r\n") + if camel_case { + dst.put_slice(b"Connection: keep-alive\r\n") + } else { + dst.put_slice(b"connection: keep-alive\r\n") + } } ConnectionType::Close if version >= Version::HTTP_11 => { - dst.put_slice(b"connection: close\r\n") + if camel_case { + dst.put_slice(b"Connection: close\r\n") + } else { + dst.put_slice(b"connection: close\r\n") + } } _ => (), } @@ -137,7 +158,12 @@ pub(crate) trait MessageType: Sized { buf = &mut *(dst.bytes_mut() as *mut _); } } - buf[pos..pos + k.len()].copy_from_slice(k); + // use upper Camel-Case + if camel_case { + write_camel_case(k, &mut buf[pos..pos + k.len()]); + } else { + buf[pos..pos + k.len()].copy_from_slice(k); + } pos += k.len(); buf[pos..pos + 2].copy_from_slice(b": "); pos += 2; @@ -162,7 +188,12 @@ pub(crate) trait MessageType: Sized { buf = &mut *(dst.bytes_mut() as *mut _); } } - buf[pos..pos + k.len()].copy_from_slice(k); + // use upper Camel-Case + if camel_case { + write_camel_case(k, &mut buf[pos..pos + k.len()]); + } else { + buf[pos..pos + k.len()].copy_from_slice(k); + } pos += k.len(); buf[pos..pos + 2].copy_from_slice(b": "); pos += 2; @@ -225,8 +256,8 @@ impl MessageType for RequestHead { self.chunked() } - fn upper_camel_case(&self) -> bool { - self.upper_camel_case_headers() + fn camel_case(&self) -> bool { + RequestHead::camel_case_headers(self) } fn headers(&self) -> &HeaderMap { @@ -426,7 +457,7 @@ impl<'a> io::Write for Writer<'a> { } } -fn write_upper_camel_case(value: &[u8], buffer: &mut [u8]) { +fn write_camel_case(value: &[u8], buffer: &mut [u8]) { let mut index = 0; let key = value; let mut key_iter = key.iter(); @@ -456,9 +487,11 @@ fn write_upper_camel_case(value: &[u8], buffer: &mut [u8]) { #[cfg(test)] mod tests { - use super::*; use bytes::Bytes; + use super::*; + use crate::http::header::{HeaderValue, CONTENT_TYPE}; + #[test] fn test_chunked_te() { let mut bytes = BytesMut::new(); @@ -472,4 +505,64 @@ mod tests { Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n") ); } + + #[test] + fn test_camel_case() { + let mut bytes = BytesMut::with_capacity(2048); + let mut head = RequestHead::default(); + head.set_camel_case_headers(true); + head.headers.insert(DATE, HeaderValue::from_static("date")); + head.headers + .insert(CONTENT_TYPE, HeaderValue::from_static("plain/text")); + + let _ = head.encode_headers( + &mut bytes, + Version::HTTP_11, + BodySize::Empty, + ConnectionType::Close, + &ServiceConfig::default(), + ); + assert_eq!( + bytes.take().freeze(), + Bytes::from_static(b"\r\nContent-Length: 0\r\nConnection: close\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n") + ); + + let _ = head.encode_headers( + &mut bytes, + Version::HTTP_11, + BodySize::Stream, + ConnectionType::KeepAlive, + &ServiceConfig::default(), + ); + assert_eq!( + bytes.take().freeze(), + Bytes::from_static(b"\r\nTransfer-Encoding: chunked\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n") + ); + + let _ = head.encode_headers( + &mut bytes, + Version::HTTP_11, + BodySize::Sized64(100), + ConnectionType::KeepAlive, + &ServiceConfig::default(), + ); + assert_eq!( + bytes.take().freeze(), + Bytes::from_static(b"\r\nContent-Length: 100\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n") + ); + + head.headers + .append(CONTENT_TYPE, HeaderValue::from_static("xml")); + let _ = head.encode_headers( + &mut bytes, + Version::HTTP_11, + BodySize::Stream, + ConnectionType::KeepAlive, + &ServiceConfig::default(), + ); + assert_eq!( + bytes.take().freeze(), + Bytes::from_static(b"\r\nTransfer-Encoding: chunked\r\nDate: date\r\nContent-Type: xml\r\nContent-Type: plain/text\r\n\r\n") + ); + } } diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index f3129c758..c279aaebf 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -101,13 +101,13 @@ impl RequestHead { /// Is to uppercase headers with Camel-Case. /// Befault is `false` #[inline] - pub fn upper_camel_case_headers(&self) -> bool { + pub fn camel_case_headers(&self) -> bool { self.flags.contains(Flags::CAMEL_CASE) } /// Set `true` to send headers which are uppercased with Camel-Case. #[inline] - pub fn set_upper_camel_case_headers(&mut self, val: bool) { + pub fn set_camel_case_headers(&mut self, val: bool) { if val { self.flags.insert(Flags::CAMEL_CASE); } else { diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index 6d382478f..a4f1569cc 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -59,9 +59,7 @@ fn test_connection_close() { .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .map(|_| ()) }); - println!("REQ: {:?}", srv.get("/").force_close()); let response = srv.block_on(srv.get("/").force_close().send()).unwrap(); - println!("RES: {:?}", response); assert!(response.status().is_success()); } diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 30fd4a6d2..10ab87bda 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,9 @@ # Changes +### Added + +* Allow to send headers in `Camel-Case` form. + ## [0.1.1] - 2019-04-19 ### Added diff --git a/awc/src/request.rs b/awc/src/request.rs index d99a9418e..5c09df816 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -235,17 +235,10 @@ impl ClientRequest { self } - /// Is to uppercase headers with Camel-Case. - /// Befault is `false` + /// Send headers in `Camel-Case` form. #[inline] - pub fn upper_camel_case_headers(&self) -> bool { - self.head.upper_camel_case_headers() - } - - /// Set `true` to send headers which are uppercased with Camel-Case. - #[inline] - pub fn set_upper_camel_case_headers(&mut self, value: bool) -> &mut Self { - self.head.set_upper_camel_case_headers(value); + pub fn camel_case(mut self) -> Self { + self.head.set_camel_case_headers(true); self } diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 7e2dc6ba4..94684dd97 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -90,6 +90,10 @@ fn test_simple() { // read response let bytes = srv.block_on(response.body()).unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + + // camel case + let response = srv.block_on(srv.post("/").camel_case().send()).unwrap(); + assert!(response.status().is_success()); } #[test]