1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-11-26 03:21:08 +00:00

allow camel case response headers (#2587)

This commit is contained in:
Rob Ede 2022-01-16 03:16:26 +00:00 committed by GitHub
parent 3c7ccf5521
commit 7b8a392ef5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 1 deletions

View file

@ -1,10 +1,14 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Added
- Response headers can be sent as camel case using `res.head_mut().set_camel_case_headers(true)`. [#2587]
### Changed ### Changed
- Brotli (de)compression support is now provided by the `brotli` crate. [#2538] - Brotli (de)compression support is now provided by the `brotli` crate. [#2538]
[#2538]: https://github.com/actix/actix-web/pull/2538 [#2538]: https://github.com/actix/actix-web/pull/2538
[#2587]: https://github.com/actix/actix-web/pull/2587
## 3.0.0-beta.18 - 2022-01-04 ## 3.0.0-beta.18 - 2022-01-04

View file

@ -88,6 +88,7 @@ async-stream = "0.3"
criterion = { version = "0.3", features = ["html_reports"] } criterion = { version = "0.3", features = ["html_reports"] }
env_logger = "0.9" env_logger = "0.9"
futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] }
memchr = "2.4"
rcgen = "0.8" rcgen = "0.8"
regex = "1.3" regex = "1.3"
rustls-pemfile = "0.2" rustls-pemfile = "0.2"

View file

@ -258,6 +258,12 @@ impl MessageType for Response<()> {
None None
} }
fn camel_case(&self) -> bool {
self.head()
.flags
.contains(crate::message::Flags::CAMEL_CASE)
}
fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()> { fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()> {
let head = self.head(); let head = self.head();
let reason = head.reason().as_bytes(); let reason = head.reason().as_bytes();

View file

@ -20,7 +20,7 @@ pub struct ResponseHead {
pub headers: HeaderMap, pub headers: HeaderMap,
pub reason: Option<&'static str>, pub reason: Option<&'static str>,
pub(crate) extensions: RefCell<Extensions>, pub(crate) extensions: RefCell<Extensions>,
flags: Flags, pub(crate) flags: Flags,
} }
impl ResponseHead { impl ResponseHead {
@ -49,6 +49,18 @@ impl ResponseHead {
&mut self.headers &mut self.headers
} }
/// Sets the flag that controls wether to send headers formatted as Camel-Case.
///
/// Only applicable to HTTP/1.x responses; HTTP/2 header names are always lowercase.
#[inline]
pub fn set_camel_case_headers(&mut self, camel_case: bool) {
if camel_case {
self.flags.insert(Flags::CAMEL_CASE);
} else {
self.flags.remove(Flags::CAMEL_CASE);
}
}
/// Message extensions /// Message extensions
#[inline] #[inline]
pub fn extensions(&self) -> Ref<'_, Extensions> { pub fn extensions(&self) -> Ref<'_, Extensions> {
@ -206,3 +218,57 @@ impl BoxedResponsePool {
} }
} }
} }
#[cfg(test)]
mod tests {
use std::{
io::{Read as _, Write as _},
net,
};
use memchr::memmem;
use crate::{
header::{HeaderName, HeaderValue},
Error, HttpService, Request, Response,
};
#[actix_rt::test]
async fn camel_case_headers() {
let mut srv = actix_http_test::test_server(|| {
HttpService::new(|req: Request| async move {
let mut res = Response::ok();
if req.path().contains("camel") {
res.head_mut().set_camel_case_headers(true);
}
res.headers_mut().insert(
HeaderName::from_static("foo-bar"),
HeaderValue::from_static("baz"),
);
Ok::<_, Error>(res)
})
.tcp()
})
.await;
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
let _ = stream.write_all(b"GET /camel 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_some());
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_some());
srv.stop().await;
}
}