1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-02 13:29:24 +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
## Unreleased - 2021-xx-xx
### Added
- Response headers can be sent as camel case using `res.head_mut().set_camel_case_headers(true)`. [#2587]
### Changed
- Brotli (de)compression support is now provided by the `brotli` crate. [#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

View file

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

View file

@ -258,6 +258,12 @@ impl MessageType for Response<()> {
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<()> {
let head = self.head();
let reason = head.reason().as_bytes();

View file

@ -20,7 +20,7 @@ pub struct ResponseHead {
pub headers: HeaderMap,
pub reason: Option<&'static str>,
pub(crate) extensions: RefCell<Extensions>,
flags: Flags,
pub(crate) flags: Flags,
}
impl ResponseHead {
@ -49,6 +49,18 @@ impl ResponseHead {
&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
#[inline]
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;
}
}