mirror of https://github.com/fafhrd91/actix-web
add IntoHeaderPair trait
This commit is contained in:
parent
22749150ae
commit
dacf8f7b6b
|
@ -1,7 +1,10 @@
|
|||
# Changes
|
||||
|
||||
## Unreleased - 2021-xx-xx
|
||||
### Changed
|
||||
* `Response::content_type` now takes an `impl IntoHeaderValue` to support `mime` types. [#1894]
|
||||
* `Response::set` and `Response::header` methods; use the respective `Response::set_header` and
|
||||
`Response::append_header` methods. [#1869]
|
||||
|
||||
[#1894]: https://github.com/actix/actix-web/pull/1894
|
||||
|
||||
|
@ -32,7 +35,12 @@
|
|||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||
[#1857]: https://github.com/actix/actix-web/pull/1857
|
||||
[#1864]: https://github.com/actix/actix-web/pull/1864
|
||||
<<<<<<< HEAD
|
||||
[#1878]: https://github.com/actix/actix-web/pull/1878
|
||||
=======
|
||||
[#1869]: https://github.com/actix/actix-web/pull/1869
|
||||
|
||||
>>>>>>> 587000a2... add IntoHeaderPair trait
|
||||
|
||||
## 2.2.0 - 2020-11-25
|
||||
### Added
|
||||
|
|
|
@ -42,14 +42,13 @@ use crate::httpmessage::HttpMessage;
|
|||
/// let mut builder = Response::Ok();
|
||||
/// builder.set(IfRange::EntityTag(EntityTag::new(
|
||||
/// false,
|
||||
/// "xyzzy".to_owned(),
|
||||
/// "abc".to_owned(),
|
||||
/// )));
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// use actix_http::Response;
|
||||
/// use actix_http::http::header::IfRange;
|
||||
/// use std::time::{Duration, SystemTime};
|
||||
/// use actix_http::{http::header::IfRange, Response};
|
||||
///
|
||||
/// let mut builder = Response::Ok();
|
||||
/// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
|
||||
|
@ -57,9 +56,10 @@ use crate::httpmessage::HttpMessage;
|
|||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum IfRange {
|
||||
/// The entity-tag the client has of the resource
|
||||
/// The entity-tag the client has of the resource.
|
||||
EntityTag(EntityTag),
|
||||
/// The date when the client retrieved the resource
|
||||
|
||||
/// The date when the client retrieved the resource.
|
||||
Date(HttpDate),
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
use std::convert::{Infallible, TryFrom};
|
||||
|
||||
use either::Either;
|
||||
use http::{
|
||||
header::{HeaderName, InvalidHeaderName, InvalidHeaderValue},
|
||||
HeaderValue,
|
||||
};
|
||||
|
||||
/// A trait for transforming things
|
||||
pub trait IntoHeaderPair: Sized {
|
||||
type Error;
|
||||
|
||||
fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error>;
|
||||
}
|
||||
|
||||
impl IntoHeaderPair for (HeaderName, HeaderValue) {
|
||||
type Error = Infallible;
|
||||
|
||||
fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderPair for (HeaderName, &str) {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {
|
||||
let (name, value) = self;
|
||||
let value = HeaderValue::try_from(value)?;
|
||||
Ok((name, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderPair for (&str, HeaderValue) {
|
||||
type Error = InvalidHeaderName;
|
||||
|
||||
fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {
|
||||
let (name, value) = self;
|
||||
let name = HeaderName::try_from(name)?;
|
||||
Ok((name, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderPair for (&str, &str) {
|
||||
type Error = Either<InvalidHeaderName, InvalidHeaderValue>;
|
||||
|
||||
fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {
|
||||
let (name, value) = self;
|
||||
let name = HeaderName::try_from(name).map_err(Either::Left)?;
|
||||
let value = HeaderValue::try_from(value).map_err(Either::Right)?;
|
||||
Ok((name, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderPair for (HeaderName, String) {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {
|
||||
let (name, value) = self;
|
||||
let value = HeaderValue::try_from(&value)?;
|
||||
Ok((name, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderPair for (String, HeaderValue) {
|
||||
type Error = InvalidHeaderName;
|
||||
|
||||
fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {
|
||||
let (name, value) = self;
|
||||
let name = HeaderName::try_from(&name)?;
|
||||
Ok((name, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderPair for (String, String) {
|
||||
type Error = Either<InvalidHeaderName, InvalidHeaderValue>;
|
||||
|
||||
fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {
|
||||
let (name, value) = self;
|
||||
let name = HeaderName::try_from(&name).map_err(Either::Left)?;
|
||||
let value = HeaderValue::try_from(&value).map_err(Either::Right)?;
|
||||
Ok((name, value))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use bytes::Bytes;
|
||||
use http::{header::InvalidHeaderValue, Error as HttpError, HeaderValue};
|
||||
use mime::Mime;
|
||||
|
||||
/// A trait for any object that can be Converted to a `HeaderValue`
|
||||
pub trait IntoHeaderValue: Sized {
|
||||
/// The type returned in the event of a conversion error.
|
||||
type Error: Into<HttpError>;
|
||||
|
||||
/// Try to convert value to a Header pair value.
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error>;
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoHeaderValue for &'a str {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
self.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoHeaderValue for &'a [u8] {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::from_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for Bytes {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::from_maybe_shared(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for Vec<u8> {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::try_from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for String {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::try_from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for usize {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
let s = format!("{}", self);
|
||||
HeaderValue::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for u64 {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
let s = format!("{}", self);
|
||||
HeaderValue::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for Mime {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::try_from(format!("{}", self))
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ use http::header::{HeaderName, HeaderValue};
|
|||
/// A set of HTTP headers
|
||||
///
|
||||
/// `HeaderMap` is an multi-map of [`HeaderName`] to values.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct HeaderMap {
|
||||
pub(crate) inner: AHashMap<HeaderName, Value>,
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
//! Various HTTP headers.
|
||||
|
||||
use std::{convert::TryFrom, fmt, str::FromStr};
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use http::Error as HttpError;
|
||||
use mime::Mime;
|
||||
use percent_encoding::{AsciiSet, CONTROLS};
|
||||
|
||||
pub use http::header::*;
|
||||
|
@ -12,6 +10,9 @@ pub use http::header::*;
|
|||
use crate::error::ParseError;
|
||||
use crate::httpmessage::HttpMessage;
|
||||
|
||||
mod into_pair;
|
||||
mod into_value;
|
||||
|
||||
mod common;
|
||||
pub(crate) mod map;
|
||||
mod shared;
|
||||
|
@ -19,15 +20,14 @@ pub use self::common::*;
|
|||
#[doc(hidden)]
|
||||
pub use self::shared::*;
|
||||
|
||||
pub use self::into_pair::IntoHeaderPair;
|
||||
pub use self::into_value::IntoHeaderValue;
|
||||
#[doc(hidden)]
|
||||
pub use self::map::GetAll;
|
||||
pub use self::map::HeaderMap;
|
||||
|
||||
/// A trait for any object that will represent a header field and value.
|
||||
pub trait Header
|
||||
where
|
||||
Self: IntoHeaderValue,
|
||||
{
|
||||
/// A trait for any object that already represents a valid header field and value.
|
||||
pub trait Header: IntoHeaderValue {
|
||||
/// Returns the name of the header field
|
||||
fn name() -> HeaderName;
|
||||
|
||||
|
@ -35,98 +35,6 @@ where
|
|||
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError>;
|
||||
}
|
||||
|
||||
/// A trait for any object that can be Converted to a `HeaderValue`
|
||||
pub trait IntoHeaderValue: Sized {
|
||||
/// The type returned in the event of a conversion error.
|
||||
type Error: Into<HttpError>;
|
||||
|
||||
/// Try to convert value to a Header value.
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error>;
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoHeaderValue for &'a str {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
self.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoHeaderValue for &'a [u8] {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::from_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for Bytes {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::from_maybe_shared(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for Vec<u8> {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::try_from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for String {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::try_from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for usize {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
let s = format!("{}", self);
|
||||
HeaderValue::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for u64 {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
let s = format!("{}", self);
|
||||
HeaderValue::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoHeaderValue for Mime {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<HeaderValue, Self::Error> {
|
||||
HeaderValue::try_from(format!("{}", self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents supported types of content encodings
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum ContentEncoding {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Copied for `hyper::header::shared`;
|
||||
//! Originally taken from `hyper::header::shared`.
|
||||
|
||||
pub use self::charset::Charset;
|
||||
pub use self::encoding::Encoding;
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::payload::Payload;
|
|||
|
||||
struct Cookies(Vec<Cookie<'static>>);
|
||||
|
||||
/// Trait that implements general purpose operations on http messages
|
||||
/// Trait that implements general purpose operations on HTTP messages.
|
||||
pub trait HttpMessage: Sized {
|
||||
/// Type of message payload stream
|
||||
type Stream;
|
||||
|
@ -30,8 +30,8 @@ pub trait HttpMessage: Sized {
|
|||
/// Mutable reference to a the request's extensions container
|
||||
fn extensions_mut(&self) -> RefMut<'_, Extensions>;
|
||||
|
||||
/// Get a header.
|
||||
#[doc(hidden)]
|
||||
/// Get a header
|
||||
fn get_header<H: Header>(&self) -> Option<H>
|
||||
where
|
||||
Self: Sized,
|
||||
|
@ -43,8 +43,8 @@ pub trait HttpMessage: Sized {
|
|||
}
|
||||
}
|
||||
|
||||
/// Read the request content type. If request does not contain
|
||||
/// *Content-Type* header, empty str get returned.
|
||||
/// Read the request content type. If request did not contain a *Content-Type* header, an empty
|
||||
/// string is returned.
|
||||
fn content_type(&self) -> &str {
|
||||
if let Some(content_type) = self.headers().get(header::CONTENT_TYPE) {
|
||||
if let Ok(content_type) = content_type.to_str() {
|
||||
|
@ -90,7 +90,7 @@ pub trait HttpMessage: Sized {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
/// Check if request has chunked transfer encoding
|
||||
/// Check if request has chunked transfer encoding.
|
||||
fn chunked(&self) -> Result<bool, ParseError> {
|
||||
if let Some(encodings) = self.headers().get(header::TRANSFER_ENCODING) {
|
||||
if let Ok(s) = encodings.to_str() {
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::body::{Body, BodyStream, MessageBody, ResponseBody};
|
|||
use crate::cookie::{Cookie, CookieJar};
|
||||
use crate::error::Error;
|
||||
use crate::extensions::Extensions;
|
||||
use crate::header::{Header, IntoHeaderValue};
|
||||
use crate::header::{Header, IntoHeaderPair, IntoHeaderValue};
|
||||
use crate::http::header::{self, HeaderName, HeaderValue};
|
||||
use crate::http::{Error as HttpError, HeaderMap, StatusCode};
|
||||
use crate::message::{BoxedResponseHead, ConnectionType, ResponseHead};
|
||||
|
@ -399,35 +399,57 @@ impl ResponseBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set a header.
|
||||
/// Insert a header, replacing any that existed with an equivalent field name.
|
||||
///
|
||||
/// ```rust
|
||||
/// use actix_http::{http, Request, Response};
|
||||
///
|
||||
/// fn index(req: Request) -> Response {
|
||||
/// Response::Ok()
|
||||
/// .set_header("X-TEST", "value")
|
||||
/// .set_header(http::header::CONTENT_TYPE, "application/json")
|
||||
/// .set_header(("X-TEST", "value"))
|
||||
/// .set_header(ContentType(mime::APPLICATION_JSON))
|
||||
/// .finish()
|
||||
/// }
|
||||
/// ```
|
||||
pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
|
||||
pub fn insert_header<H>(&mut self, header: H) -> &mut Self
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
|
||||
V: IntoHeaderValue,
|
||||
H: IntoHeaderPair,
|
||||
H::Error: Into<HttpError>,
|
||||
{
|
||||
if let Some(parts) = parts(&mut self.head, &self.err) {
|
||||
match HeaderName::try_from(key) {
|
||||
Ok(key) => match value.try_into() {
|
||||
Ok(value) => {
|
||||
parts.headers.insert(key, value);
|
||||
}
|
||||
Err(e) => self.err = Some(e.into()),
|
||||
},
|
||||
match header.try_into_header_pair() {
|
||||
Ok((key, value)) => parts.headers.insert(key, value),
|
||||
Err(e) => self.err = Some(e.into()),
|
||||
};
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Append a header, keeping any that existed with an equivalent field name.
|
||||
///
|
||||
/// ```rust
|
||||
/// use actix_http::{http, Request, Response};
|
||||
///
|
||||
/// fn index(req: Request) -> Response {
|
||||
/// Response::Ok()
|
||||
/// .append_header(("X-TEST", "value"))
|
||||
/// .append_header(ContentType(mime::APPLICATION_JSON))
|
||||
/// .finish()
|
||||
/// }
|
||||
/// ```
|
||||
pub fn append_header<H>(&mut self, header: H) -> &mut Self
|
||||
where
|
||||
H: IntoHeaderPair,
|
||||
H::Error: Into<HttpError>,
|
||||
{
|
||||
if let Some(parts) = parts(&mut self.head, &self.err) {
|
||||
match header.try_into_header_pair() {
|
||||
Ok((key, value)) => parts.headers.append(key, value),
|
||||
Err(e) => self.err = Some(e.into()),
|
||||
};
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -458,7 +480,13 @@ impl ResponseBuilder {
|
|||
if let Some(parts) = parts(&mut self.head, &self.err) {
|
||||
parts.set_connection_type(ConnectionType::Upgrade);
|
||||
}
|
||||
self.set_header(header::UPGRADE, value)
|
||||
|
||||
// TODO: fix signature
|
||||
if let Ok(value) = value.try_into() {
|
||||
self.insert_header((header::UPGRADE, value));
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Force close connection, even if it is marked as keep-alive
|
||||
|
|
Loading…
Reference in New Issue