1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-27 09:30:39 +00:00

add tests for camel case headers rendering

This commit is contained in:
Nikolay Kim 2019-04-24 11:27:57 -07:00
parent 64f603b076
commit 2e19f572ee
7 changed files with 123 additions and 26 deletions

View file

@ -1,5 +1,10 @@
# Changes # Changes
### Added
* Allow to render h1 request headers in `Camel-Case`
## [0.1.3] - 2019-04-23 ## [0.1.3] - 2019-04-23
### Fixed ### Fixed

View file

@ -43,7 +43,7 @@ pub(crate) trait MessageType: Sized {
fn headers(&self) -> &HeaderMap; fn headers(&self) -> &HeaderMap;
fn upper_camel_case(&self) -> bool { fn camel_case(&self) -> bool {
false false
} }
@ -61,6 +61,7 @@ pub(crate) trait MessageType: Sized {
) -> io::Result<()> { ) -> io::Result<()> {
let chunked = self.chunked(); let chunked = self.chunked();
let mut skip_len = length != BodySize::Stream; let mut skip_len = length != BodySize::Stream;
let camel_case = self.camel_case();
// Content length // Content length
if let Some(status) = self.status() { if let Some(status) = self.status() {
@ -78,18 +79,30 @@ pub(crate) trait MessageType: Sized {
match length { match length {
BodySize::Stream => { BodySize::Stream => {
if chunked { 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 { } else {
skip_len = false; skip_len = false;
dst.put_slice(b"\r\n"); dst.put_slice(b"\r\n");
} }
} }
BodySize::Empty => { 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::Sized(len) => helpers::write_content_length(len, dst),
BodySize::Sized64(len) => { 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)?; write!(dst.writer(), "{}\r\n", len)?;
} }
BodySize::None => dst.put_slice(b"\r\n"), BodySize::None => dst.put_slice(b"\r\n"),
@ -99,10 +112,18 @@ pub(crate) trait MessageType: Sized {
match ctype { match ctype {
ConnectionType::Upgrade => dst.put_slice(b"connection: upgrade\r\n"), ConnectionType::Upgrade => dst.put_slice(b"connection: upgrade\r\n"),
ConnectionType::KeepAlive if version < Version::HTTP_11 => { 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 => { 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 = &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(); pos += k.len();
buf[pos..pos + 2].copy_from_slice(b": "); buf[pos..pos + 2].copy_from_slice(b": ");
pos += 2; pos += 2;
@ -162,7 +188,12 @@ pub(crate) trait MessageType: Sized {
buf = &mut *(dst.bytes_mut() as *mut _); 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(); pos += k.len();
buf[pos..pos + 2].copy_from_slice(b": "); buf[pos..pos + 2].copy_from_slice(b": ");
pos += 2; pos += 2;
@ -225,8 +256,8 @@ impl MessageType for RequestHead {
self.chunked() self.chunked()
} }
fn upper_camel_case(&self) -> bool { fn camel_case(&self) -> bool {
self.upper_camel_case_headers() RequestHead::camel_case_headers(self)
} }
fn headers(&self) -> &HeaderMap { 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 mut index = 0;
let key = value; let key = value;
let mut key_iter = key.iter(); let mut key_iter = key.iter();
@ -456,9 +487,11 @@ fn write_upper_camel_case(value: &[u8], buffer: &mut [u8]) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use bytes::Bytes; use bytes::Bytes;
use super::*;
use crate::http::header::{HeaderValue, CONTENT_TYPE};
#[test] #[test]
fn test_chunked_te() { fn test_chunked_te() {
let mut bytes = BytesMut::new(); let mut bytes = BytesMut::new();
@ -472,4 +505,64 @@ mod tests {
Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n") 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")
);
}
} }

View file

@ -101,13 +101,13 @@ impl RequestHead {
/// Is to uppercase headers with Camel-Case. /// Is to uppercase headers with Camel-Case.
/// Befault is `false` /// Befault is `false`
#[inline] #[inline]
pub fn upper_camel_case_headers(&self) -> bool { pub fn camel_case_headers(&self) -> bool {
self.flags.contains(Flags::CAMEL_CASE) self.flags.contains(Flags::CAMEL_CASE)
} }
/// Set `true` to send headers which are uppercased with Camel-Case. /// Set `true` to send headers which are uppercased with Camel-Case.
#[inline] #[inline]
pub fn set_upper_camel_case_headers(&mut self, val: bool) { pub fn set_camel_case_headers(&mut self, val: bool) {
if val { if val {
self.flags.insert(Flags::CAMEL_CASE); self.flags.insert(Flags::CAMEL_CASE);
} else { } else {

View file

@ -59,9 +59,7 @@ fn test_connection_close() {
.finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
.map(|_| ()) .map(|_| ())
}); });
println!("REQ: {:?}", srv.get("/").force_close());
let response = srv.block_on(srv.get("/").force_close().send()).unwrap(); let response = srv.block_on(srv.get("/").force_close().send()).unwrap();
println!("RES: {:?}", response);
assert!(response.status().is_success()); assert!(response.status().is_success());
} }

View file

@ -1,5 +1,9 @@
# Changes # Changes
### Added
* Allow to send headers in `Camel-Case` form.
## [0.1.1] - 2019-04-19 ## [0.1.1] - 2019-04-19
### Added ### Added

View file

@ -235,17 +235,10 @@ impl ClientRequest {
self self
} }
/// Is to uppercase headers with Camel-Case. /// Send headers in `Camel-Case` form.
/// Befault is `false`
#[inline] #[inline]
pub fn upper_camel_case_headers(&self) -> bool { pub fn camel_case(mut self) -> Self {
self.head.upper_camel_case_headers() self.head.set_camel_case_headers(true);
}
/// 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 self
} }

View file

@ -90,6 +90,10 @@ fn test_simple() {
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = srv.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); 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] #[test]