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:
parent
3c7ccf5521
commit
7b8a392ef5
4 changed files with 78 additions and 1 deletions
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue