mirror of https://github.com/fafhrd91/actix-web
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`.
This commit is contained in:
parent
6d11ee683f
commit
5ec6eaef1b
|
@ -54,6 +54,7 @@ pub struct ClientRequest {
|
|||
method: Method,
|
||||
version: Version,
|
||||
headers: HeaderMap,
|
||||
upper_camel_case_headers: bool,
|
||||
body: Body,
|
||||
chunked: bool,
|
||||
upgrade: bool,
|
||||
|
@ -77,6 +78,7 @@ impl Default for ClientRequest {
|
|||
method: Method::default(),
|
||||
version: Version::HTTP_11,
|
||||
headers: HeaderMap::with_capacity(16),
|
||||
upper_camel_case_headers: false,
|
||||
body: Body::Empty,
|
||||
chunked: false,
|
||||
upgrade: false,
|
||||
|
@ -190,6 +192,19 @@ impl ClientRequest {
|
|||
&mut self.headers
|
||||
}
|
||||
|
||||
/// is to uppercase headers with Camel-Case
|
||||
/// default is `false`
|
||||
#[inline]
|
||||
pub fn upper_camel_case_headers(&self) -> bool {
|
||||
self.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) {
|
||||
self.upper_camel_case_headers = value;
|
||||
}
|
||||
|
||||
/// is chunked encoding enabled
|
||||
#[inline]
|
||||
pub fn chunked(&self) -> bool {
|
||||
|
@ -439,6 +454,15 @@ impl ClientRequestBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
if let Some(parts) = parts(&mut self.request, &self.err) {
|
||||
parts.upper_camel_case_headers = value;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Set content encoding.
|
||||
///
|
||||
/// By default `ContentEncoding::Identity` is used.
|
||||
|
|
|
@ -16,7 +16,7 @@ use flate2::write::{GzEncoder, ZlibEncoder};
|
|||
use flate2::Compression;
|
||||
use futures::{Async, Poll};
|
||||
use http::header::{
|
||||
HeaderValue, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
|
||||
HeaderName, HeaderValue, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
|
||||
};
|
||||
use http::{HttpTryFrom, Version};
|
||||
use time::{self, Duration};
|
||||
|
@ -142,14 +142,27 @@ impl HttpClientWriter {
|
|||
buffer.reserve(msg.headers().len() * AVERAGE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
for (key, value) in msg.headers() {
|
||||
let v = value.as_ref();
|
||||
let k = key.as_str().as_bytes();
|
||||
buffer.reserve(k.len() + v.len() + 4);
|
||||
buffer.put_slice(k);
|
||||
buffer.put_slice(b": ");
|
||||
buffer.put_slice(v);
|
||||
buffer.put_slice(b"\r\n");
|
||||
// to use Camel-Case headers
|
||||
if msg.upper_camel_case_headers() {
|
||||
for (key, value) in msg.headers() {
|
||||
let v = value.as_ref();
|
||||
let k = key.as_str().as_bytes();
|
||||
buffer.reserve(k.len() + v.len() + 4);
|
||||
key.to_upper_camel_case(buffer);
|
||||
buffer.put_slice(b": ");
|
||||
buffer.put_slice(v);
|
||||
buffer.put_slice(b"\r\n");
|
||||
}
|
||||
} else {
|
||||
for (key, value) in msg.headers() {
|
||||
let v = value.as_ref();
|
||||
let k = key.as_str().as_bytes();
|
||||
buffer.reserve(k.len() + v.len() + 4);
|
||||
buffer.put_slice(k);
|
||||
buffer.put_slice(b": ");
|
||||
buffer.put_slice(v);
|
||||
buffer.put_slice(b"\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
// set date header
|
||||
|
@ -216,6 +229,38 @@ impl HttpClientWriter {
|
|||
}
|
||||
}
|
||||
|
||||
trait UpperCamelCaseHeader {
|
||||
fn to_upper_camel_case(&self, buffer: &mut BytesMut);
|
||||
}
|
||||
|
||||
impl UpperCamelCaseHeader for HeaderName {
|
||||
|
||||
/// Let header name to be as upper Camel-Case, then write it to buffer.
|
||||
fn to_upper_camel_case(&self, buffer: &mut BytesMut) {
|
||||
let key = self.as_str().as_bytes();
|
||||
let mut key_iter = key.iter();
|
||||
|
||||
if let Some(c) = key_iter.next() {
|
||||
if *c >= b'a' && *c <= b'z' {
|
||||
buffer.put_slice(&[*c ^ b' ']);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
while let Some(c) = key_iter.next() {
|
||||
buffer.put_slice(&[*c]);
|
||||
if *c == b'-' {
|
||||
if let Some(c) = key_iter.next() {
|
||||
if *c >= b'a' && *c <= b'z' {
|
||||
buffer.put_slice(&[*c ^ b' ']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn content_encoder(buf: BytesMut, req: &mut ClientRequest) -> Output {
|
||||
let version = req.version();
|
||||
let mut body = req.replace_body(Body::Empty);
|
||||
|
|
|
@ -11,7 +11,7 @@ extern crate tokio_uds;
|
|||
use std::io::{Read, Write};
|
||||
use std::{net, thread};
|
||||
|
||||
use bytes::Bytes;
|
||||
use bytes::{Bytes, BytesMut, BufMut};
|
||||
use flate2::read::GzDecoder;
|
||||
use futures::stream::once;
|
||||
use futures::Future;
|
||||
|
@ -477,6 +477,42 @@ fn test_default_headers() {
|
|||
)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_upper_camel_case_headers() {
|
||||
let addr = test::TestServer::unused_addr();
|
||||
|
||||
thread::spawn(move || {
|
||||
let lst = net::TcpListener::bind(addr).unwrap();
|
||||
|
||||
for stream in lst.incoming() {
|
||||
let mut stream = stream.unwrap();
|
||||
let mut b = [0; 1000];
|
||||
let _ = stream.read(&mut b).unwrap();
|
||||
let mut v = BytesMut::from("HTTP/1.1 200 OK\r\nconnection: close\r\n\r\n");
|
||||
v.reserve(b.len());
|
||||
v.put_slice(&b);
|
||||
let _ = stream.write_all(&v);
|
||||
}
|
||||
});
|
||||
|
||||
let mut sys = actix::System::new("test");
|
||||
|
||||
// client request
|
||||
let req = client::ClientRequest::get(format!("http://{}/", addr).as_str())
|
||||
.header("User-Agent", "test")
|
||||
.header("Accept-Encoding", "over_test")
|
||||
.no_default_headers()
|
||||
.set_upper_camel_case_headers(true)
|
||||
.finish()
|
||||
.unwrap();
|
||||
let response = sys.block_on(req.send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = sys.block_on(response.body()).unwrap();
|
||||
assert_eq!(&&bytes[..62], &&b"GET / HTTP/1.1\r\nUser-Agent: test\r\nAccept-Encoding: over_test\r\n"[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_read_until_eof() {
|
||||
let addr = test::TestServer::unused_addr();
|
||||
|
|
Loading…
Reference in New Issue