1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-06-25 06:39:22 +02:00

response header rework (#1869)

This commit is contained in:
Rob Ede
2021-01-15 02:11:10 +00:00
committed by GitHub
parent 4edeb5ce47
commit b1dd8d28bc
76 changed files with 1568 additions and 1347 deletions

View File

@ -1,6 +1,17 @@
# Changes
## Unreleased - 2021-xx-xx
### Added
* `ClientRequest::insert_header` method which allows using typed headers. [#1869]
* `ClientRequest::append_header` method which allows using typed headers. [#1869]
### Removed
* `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]
* `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]
* `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869]
* `ClientRequest::header`; use `ClientRequest::append_header`. [#1869]
[#1869]: https://github.com/actix/actix-web/pull/1869
## 3.0.0-beta.1 - 2021-01-07

View File

@ -133,7 +133,7 @@ impl ClientBuilder {
V::Error: fmt::Debug,
{
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(key) => match value.try_into_value() {
Ok(value) => {
self.headers.append(key, value);
}

View File

@ -144,7 +144,7 @@ impl FrozenSendBuilder {
V: IntoHeaderValue,
{
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(key) => match value.try_into_value() {
Ok(value) => self.extra_headers.insert(key, value),
Err(e) => self.err = Some(e.into()),
},

View File

@ -7,7 +7,7 @@
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
//! let mut client = awc::Client::default();
//! let response = client.get("http://www.rust-lang.org") // <- Create request builder
//! .header("User-Agent", "Actix-web")
//! .insert_header(("User-Agent", "Actix-web"))
//! .send() // <- Send http request
//! .await?;
//!
@ -134,7 +134,7 @@ use self::connect::{Connect, ConnectorWrapper};
/// let mut client = Client::default();
///
/// let res = client.get("http://www.rust-lang.org") // <- Create request builder
/// .header("User-Agent", "Actix-web")
/// .insert_header(("User-Agent", "Actix-web"))
/// .send() // <- Send http request
/// .await; // <- send request and wait for response
///
@ -182,8 +182,8 @@ impl Client {
{
let mut req = ClientRequest::new(method, url, self.0.clone());
for (key, value) in self.0.headers.iter() {
req = req.set_header_if_none(key.clone(), value.clone());
for header in self.0.headers.iter() {
req = req.insert_header_if_none(header);
}
req
}
@ -198,8 +198,8 @@ impl Client {
<Uri as TryFrom<U>>::Error: Into<HttpError>,
{
let mut req = self.request(head.method.clone(), url);
for (key, value) in head.headers.iter() {
req = req.set_header_if_none(key.clone(), value.clone());
for header in head.headers.iter() {
req = req.insert_header_if_none(header);
}
req
}

View File

@ -9,10 +9,10 @@ use serde::Serialize;
use actix_http::body::Body;
use actix_http::cookie::{Cookie, CookieJar};
use actix_http::http::header::{self, Header, IntoHeaderValue};
use actix_http::http::header::{self, IntoHeaderPair};
use actix_http::http::{
uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, Method,
Uri, Version,
uri, ConnectionType, Error as HttpError, HeaderMap, HeaderValue, Method, Uri,
Version,
};
use actix_http::{Error, RequestHead};
@ -37,13 +37,11 @@ cfg_if::cfg_if! {
/// builder-like pattern.
///
/// ```rust
/// use actix_rt::System;
///
/// #[actix_rt::main]
/// async fn main() {
/// let response = awc::Client::new()
/// .get("http://www.rust-lang.org") // <- Create request builder
/// .header("User-Agent", "Actix-web")
/// .insert_header(("User-Agent", "Actix-web"))
/// .send() // <- Send http request
/// .await;
///
@ -143,110 +141,71 @@ impl ClientRequest {
&self.head.peer_addr
}
#[inline]
/// Returns request's headers.
#[inline]
pub fn headers(&self) -> &HeaderMap {
&self.head.headers
}
#[inline]
/// Returns request's mutable headers.
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.head.headers
}
/// Set a header.
///
/// ```rust
/// fn main() {
/// # actix_rt::System::new("test").block_on(futures_util::future::lazy(|_| {
/// let req = awc::Client::new()
/// .get("http://www.rust-lang.org")
/// .set(awc::http::header::Date::now())
/// .set(awc::http::header::ContentType(mime::TEXT_HTML));
/// # Ok::<_, ()>(())
/// # }));
/// }
/// ```
pub fn set<H: Header>(mut self, hdr: H) -> Self {
match hdr.try_into() {
Ok(value) => {
self.head.headers.insert(H::name(), value);
}
Err(e) => self.err = Some(e.into()),
}
self
}
/// Append a header.
///
/// Header gets appended to existing header.
/// To override header use `set_header()` method.
///
/// ```rust
/// use awc::{http, Client};
///
/// fn main() {
/// # actix_rt::System::new("test").block_on(async {
/// let req = Client::new()
/// .get("http://www.rust-lang.org")
/// .header("X-TEST", "value")
/// .header(http::header::CONTENT_TYPE, "application/json");
/// # Ok::<_, ()>(())
/// # });
/// }
/// ```
pub fn header<K, V>(mut self, key: K, value: V) -> Self
/// Insert a header, replacing any that were set with an equivalent field name.
pub fn insert_header<H>(mut self, header: H) -> Self
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue,
H: IntoHeaderPair,
{
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(value) => self.head.headers.append(key, value),
Err(e) => self.err = Some(e.into()),
},
match header.try_into_header_pair() {
Ok((key, value)) => self.head.headers.insert(key, value),
Err(e) => self.err = Some(e.into()),
}
self
}
};
/// Insert a header, replaces existing header.
pub fn set_header<K, V>(mut self, key: K, value: V) -> Self
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue,
{
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(value) => self.head.headers.insert(key, value),
Err(e) => self.err = Some(e.into()),
},
Err(e) => self.err = Some(e.into()),
}
self
}
/// Insert a header only if it is not yet set.
pub fn set_header_if_none<K, V>(mut self, key: K, value: V) -> Self
pub fn insert_header_if_none<H>(mut self, header: H) -> Self
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue,
H: IntoHeaderPair,
{
match HeaderName::try_from(key) {
Ok(key) => {
match header.try_into_header_pair() {
Ok((key, value)) => {
if !self.head.headers.contains_key(&key) {
match value.try_into() {
Ok(value) => self.head.headers.insert(key, value),
Err(e) => self.err = Some(e.into()),
}
self.head.headers.insert(key, value);
}
}
Err(e) => self.err = Some(e.into()),
}
};
self
}
/// Append a header, keeping any that were set with an equivalent field name.
///
/// ```rust
/// # #[actix_rt::main]
/// # async fn main() {
/// # use awc::Client;
/// use awc::http::header::ContentType;
///
/// Client::new()
/// .get("http://www.rust-lang.org")
/// .insert_header(("X-TEST", "value"))
/// .insert_header(ContentType(mime::APPLICATION_JSON));
/// # }
/// ```
pub fn append_header<H>(mut self, header: H) -> Self
where
H: IntoHeaderPair,
{
match header.try_into_header_pair() {
Ok((key, value)) => self.head.headers.append(key, value),
Err(e) => self.err = Some(e.into()),
};
self
}
@ -282,7 +241,7 @@ impl ClientRequest {
/// Set content length
#[inline]
pub fn content_length(self, len: u64) -> Self {
self.header(header::CONTENT_LENGTH, len)
self.append_header((header::CONTENT_LENGTH, len))
}
/// Set HTTP basic authorization header
@ -294,10 +253,10 @@ impl ClientRequest {
Some(password) => format!("{}:{}", username, password),
None => format!("{}:", username),
};
self.header(
self.append_header((
header::AUTHORIZATION,
format!("Basic {}", base64::encode(&auth)),
)
))
}
/// Set HTTP bearer authentication header
@ -305,7 +264,7 @@ impl ClientRequest {
where
T: fmt::Display,
{
self.header(header::AUTHORIZATION, format!("Bearer {}", token))
self.append_header((header::AUTHORIZATION, format!("Bearer {}", token)))
}
/// Set a cookie
@ -557,12 +516,15 @@ impl ClientRequest {
.unwrap_or(true);
if https {
slf = slf.set_header_if_none(header::ACCEPT_ENCODING, HTTPS_ENCODING)
slf =
slf.insert_header_if_none((header::ACCEPT_ENCODING, HTTPS_ENCODING))
} else {
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
{
slf =
slf.set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate")
slf = slf.insert_header_if_none((
header::ACCEPT_ENCODING,
"gzip, deflate",
))
}
};
}
@ -595,7 +557,7 @@ mod tests {
#[actix_rt::test]
async fn test_debug() {
let request = Client::new().get("/").header("x-test", "111");
let request = Client::new().get("/").append_header(("x-test", "111"));
let repr = format!("{:?}", request);
assert!(repr.contains("ClientRequest"));
assert!(repr.contains("x-test"));
@ -606,18 +568,18 @@ mod tests {
let req = Client::new()
.put("/")
.version(Version::HTTP_2)
.set(header::Date(SystemTime::now().into()))
.insert_header(header::Date(SystemTime::now().into()))
.content_type("plain/text")
.header(header::SERVER, "awc");
.append_header((header::SERVER, "awc"));
let req = if let Some(val) = Some("server") {
req.header(header::USER_AGENT, val)
req.append_header((header::USER_AGENT, val))
} else {
req
};
let req = if let Some(_val) = Option::<&str>::None {
req.header(header::ALLOW, "1")
req.append_header((header::ALLOW, "1"))
} else {
req
};
@ -660,7 +622,7 @@ mod tests {
.header(header::CONTENT_TYPE, "111")
.finish()
.get("/")
.set_header(header::CONTENT_TYPE, "222");
.insert_header((header::CONTENT_TYPE, "222"));
assert_eq!(
req.head

View File

@ -296,7 +296,7 @@ impl RequestSender {
match self {
RequestSender::Owned(head) => {
if !head.headers.contains_key(&key) {
match value.try_into() {
match value.try_into_value() {
Ok(value) => head.headers.insert(key, value),
Err(e) => return Err(e.into()),
}
@ -306,7 +306,7 @@ impl RequestSender {
if !head.headers.contains_key(&key)
&& !extra_headers.iter().any(|h| h.contains_key(&key))
{
match value.try_into() {
match value.try_into_value() {
Ok(v) => {
let h = extra_headers.get_or_insert(HeaderMap::new());
h.insert(key, v)

View File

@ -45,7 +45,7 @@ impl TestResponse {
/// Set a header
pub fn set<H: Header>(mut self, hdr: H) -> Self {
if let Ok(value) = hdr.try_into() {
if let Ok(value) = hdr.try_into_value() {
self.head.headers.append(H::name(), value);
return self;
}
@ -60,7 +60,7 @@ impl TestResponse {
V: IntoHeaderValue,
{
if let Ok(key) = HeaderName::try_from(key) {
if let Ok(value) = value.try_into() {
if let Ok(value) = value.try_into_value() {
self.head.headers.append(key, value);
return self;
}

View File

@ -170,7 +170,7 @@ impl WebsocketsRequest {
V: IntoHeaderValue,
{
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(key) => match value.try_into_value() {
Ok(value) => {
self.head.headers.append(key, value);
}
@ -189,7 +189,7 @@ impl WebsocketsRequest {
V: IntoHeaderValue,
{
match HeaderName::try_from(key) {
Ok(key) => match value.try_into() {
Ok(key) => match value.try_into_value() {
Ok(value) => {
self.head.headers.insert(key, value);
}
@ -210,7 +210,7 @@ impl WebsocketsRequest {
match HeaderName::try_from(key) {
Ok(key) => {
if !self.head.headers.contains_key(&key) {
match value.try_into() {
match value.try_into_value() {
Ok(value) => {
self.head.headers.insert(key, value);
}

View File

@ -52,7 +52,7 @@ async fn test_simple() {
.service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
});
let request = srv.get("/").header("x-test", "111").send();
let request = srv.get("/").insert_header(("x-test", "111")).send();
let mut response = request.await.unwrap();
assert!(response.status().is_success());
@ -82,7 +82,7 @@ async fn test_json() {
let request = srv
.get("/")
.header("x-test", "111")
.insert_header(("x-test", "111"))
.send_json(&"TEST".to_string());
let response = request.await.unwrap();
assert!(response.status().is_success());
@ -99,7 +99,10 @@ async fn test_form() {
let mut data = HashMap::new();
let _ = data.insert("key".to_string(), "TEST".to_string());
let request = srv.get("/").header("x-test", "111").send_form(&data);
let request = srv
.get("/")
.append_header(("x-test", "111"))
.send_form(&data);
let response = request.await.unwrap();
assert!(response.status().is_success());
}
@ -438,7 +441,7 @@ async fn test_client_gzip_encoding() {
let data = e.finish().unwrap();
HttpResponse::Ok()
.header("content-encoding", "gzip")
.insert_header(("content-encoding", "gzip"))
.body(data)
})))
});
@ -461,7 +464,7 @@ async fn test_client_gzip_encoding_large() {
let data = e.finish().unwrap();
HttpResponse::Ok()
.header("content-encoding", "gzip")
.insert_header(("content-encoding", "gzip"))
.body(data)
})))
});
@ -489,7 +492,7 @@ async fn test_client_gzip_encoding_large_random() {
e.write_all(&data).unwrap();
let data = e.finish().unwrap();
HttpResponse::Ok()
.header("content-encoding", "gzip")
.insert_header(("content-encoding", "gzip"))
.body(data)
})))
});
@ -511,7 +514,7 @@ async fn test_client_brotli_encoding() {
e.write_all(&data).unwrap();
let data = e.finish().unwrap();
HttpResponse::Ok()
.header("content-encoding", "br")
.insert_header(("content-encoding", "br"))
.body(data)
})))
});
@ -539,7 +542,7 @@ async fn test_client_brotli_encoding_large_random() {
e.write_all(&data).unwrap();
let data = e.finish().unwrap();
HttpResponse::Ok()
.header("content-encoding", "br")
.insert_header(("content-encoding", "br"))
.body(data)
})))
});