From 64f603b0766e52e21b1bf9bacdf8e2e250c54f31 Mon Sep 17 00:00:00 2001 From: Peter Ding Date: Thu, 25 Apr 2019 01:48:49 +0800 Subject: [PATCH] Support to set header names of `ClientRequest` as Camel-Case (#713) * Support to set header names of `ClientRequest` as Camel-Case This is the case for supporting to request for servers which don't perfectly implement the `RFC 7230`. It is important for an app which uses `ClientRequest` as core part. * Add field `upper_camel_case_headers` to `ClientRequest`. * Add function `set_upper_camel_case_headers` to `ClientRequest` and `ClientRequestBuilder` to set field `upper_camel_case_headers`. * Add trait `client::writer::UpperCamelCaseHeader` for `http::header::HeaderName`, let it can be converted to Camel-Case then writed to buffer. * Add test `test_client::test_upper_camel_case_headers`. * Support upper Camel-Case headers * [actix-http] Add field `upper_camel_case_headers` for `RequestHead` * [actix-http] Add code for `MessageType` to support upper camel case * [awc] Add functions for `ClientRequest` to set upper camel case * Use `Flags::CAMEL_CASE` for upper camel case of headers --- actix-http/src/h1/encoder.rs | 36 ++++++++++++++++++++++++++++++++++++ actix-http/src/message.rs | 18 ++++++++++++++++++ awc/src/request.rs | 14 ++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 8f98fe67e..177661b5b 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -43,6 +43,10 @@ pub(crate) trait MessageType: Sized { fn headers(&self) -> &HeaderMap; + fn upper_camel_case(&self) -> bool { + false + } + fn chunked(&self) -> bool; fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()>; @@ -221,6 +225,10 @@ impl MessageType for RequestHead { self.chunked() } + fn upper_camel_case(&self) -> bool { + self.upper_camel_case_headers() + } + fn headers(&self) -> &HeaderMap { &self.headers } @@ -418,6 +426,34 @@ impl<'a> io::Write for Writer<'a> { } } +fn write_upper_camel_case(value: &[u8], buffer: &mut [u8]) { + let mut index = 0; + let key = value; + let mut key_iter = key.iter(); + + if let Some(c) = key_iter.next() { + if *c >= b'a' && *c <= b'z' { + buffer[index] = *c ^ b' '; + index += 1; + } + } else { + return; + } + + while let Some(c) = key_iter.next() { + buffer[index] = *c; + index += 1; + if *c == b'-' { + if let Some(c) = key_iter.next() { + if *c >= b'a' && *c <= b'z' { + buffer[index] = *c ^ b' '; + index += 1; + } + } + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index 7f2dc603f..f3129c758 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -27,6 +27,7 @@ bitflags! { const UPGRADE = 0b0000_0100; const EXPECT = 0b0000_1000; const NO_CHUNKING = 0b0001_0000; + const CAMEL_CASE = 0b0010_0000; } } @@ -97,6 +98,23 @@ impl RequestHead { &mut self.headers } + /// Is to uppercase headers with Camel-Case. + /// Befault is `false` + #[inline] + pub fn upper_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) { + if val { + self.flags.insert(Flags::CAMEL_CASE); + } else { + self.flags.remove(Flags::CAMEL_CASE); + } + } + #[inline] /// Set connection type of the message pub fn set_connection_type(&mut self, ctype: ConnectionType) { diff --git a/awc/src/request.rs b/awc/src/request.rs index 2e6032649..d99a9418e 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -235,6 +235,20 @@ impl ClientRequest { self } + /// Is to uppercase headers with Camel-Case. + /// Befault is `false` + #[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); + self + } + /// Force close connection instead of returning it back to connections pool. /// This setting affect only http/1 connections. #[inline]