1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-13 02:39:32 +00:00

move typed headers and implement FromRequest (#2094)

Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
Ibraheem Ahmed 2021-04-01 11:42:18 -04:00 committed by GitHub
parent c8ed8dd1a4
commit 50dc13f280
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 445 additions and 248 deletions

View file

@ -2,6 +2,7 @@
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Added ### Added
* `Header` extractor for extracting common HTTP headers in handlers. [#2094]
* Added `TestServer::client_headers` method. [#2097] * Added `TestServer::client_headers` method. [#2097]
### Fixed ### Fixed
@ -17,6 +18,8 @@
[#2067]: https://github.com/actix/actix-web/pull/2067 [#2067]: https://github.com/actix/actix-web/pull/2067
[#2093]: https://github.com/actix/actix-web/pull/2093 [#2093]: https://github.com/actix/actix-web/pull/2093
[#2094]: https://github.com/actix/actix-web/pull/2094
[#2097]: https://github.com/actix/actix-web/pull/2097
## 4.0.0-beta.4 - 2021-03-09 ## 4.0.0-beta.4 - 2021-03-09

View file

@ -97,6 +97,8 @@ either = "1.5.3"
encoding_rs = "0.8" encoding_rs = "0.8"
futures-core = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false }
futures-util = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false }
language-tags = "0.2"
once_cell = "1.5"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
pin-project = "1.0.0" pin-project = "1.0.0"

View file

@ -10,10 +10,12 @@
* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] * `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063]
### Removed ### Removed
* Common HTTP headers were moved into actix-web. [2094]
* `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127] * `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127]
[#2063]: https://github.com/actix/actix-web/pull/2063 [#2063]: https://github.com/actix/actix-web/pull/2063
[#2081]: https://github.com/actix/actix-web/pull/2081 [#2081]: https://github.com/actix/actix-web/pull/2081
[#2094]: https://github.com/actix/actix-web/pull/2094
[#2127]: https://github.com/actix/actix-web/pull/2127 [#2127]: https://github.com/actix/actix-web/pull/2127

View file

@ -1,9 +1,6 @@
//! Typed HTTP headers, pre-defined `HeaderName`s, traits for parsing and conversion, and other //! Typed HTTP headers, pre-defined `HeaderName`s, traits for parsing and conversion, and other
//! header utility methods. //! header utility methods.
use std::fmt;
use bytes::{Bytes, BytesMut};
use percent_encoding::{AsciiSet, CONTROLS}; use percent_encoding::{AsciiSet, CONTROLS};
pub use http::header::*; pub use http::header::*;
@ -16,11 +13,9 @@ mod into_pair;
mod into_value; mod into_value;
mod utils; mod utils;
mod common;
pub(crate) mod map; pub(crate) mod map;
mod shared; mod shared;
pub use self::common::*;
#[doc(hidden)] #[doc(hidden)]
pub use self::shared::*; pub use self::shared::*;
@ -41,34 +36,6 @@ pub trait Header: IntoHeaderValue {
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError>; fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError>;
} }
#[derive(Debug, Default)]
pub(crate) struct Writer {
buf: BytesMut,
}
impl Writer {
fn new() -> Writer {
Writer::default()
}
fn take(&mut self) -> Bytes {
self.buf.split().freeze()
}
}
impl fmt::Write for Writer {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.buf.extend_from_slice(s.as_bytes());
Ok(())
}
#[inline]
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
fmt::write(self, args)
}
}
/// Convert `http::HeaderMap` to our `HeaderMap`. /// Convert `http::HeaderMap` to our `HeaderMap`.
impl From<http::HeaderMap> for HeaderMap { impl From<http::HeaderMap> for HeaderMap {
fn from(mut map: http::HeaderMap) -> HeaderMap { fn from(mut map: http::HeaderMap) -> HeaderMap {

View file

@ -1,15 +1,13 @@
//! Originally taken from `hyper::header::shared`. //! Originally taken from `hyper::header::shared`.
mod charset; mod charset;
mod encoding; mod content_encoding;
mod entity;
mod extended; mod extended;
mod httpdate; mod httpdate;
mod quality_item; mod quality_item;
pub use self::charset::Charset; pub use self::charset::Charset;
pub use self::encoding::Encoding; pub use self::content_encoding::ContentEncoding;
pub use self::entity::EntityTag;
pub use self::extended::{parse_extended_value, ExtendedValue}; pub use self::extended::{parse_extended_value, ExtendedValue};
pub use self::httpdate::HttpDate; pub use self::httpdate::HttpDate;
pub use self::quality_item::{q, qitem, Quality, QualityItem}; pub use self::quality_item::{q, qitem, Quality, QualityItem};

View file

@ -193,21 +193,69 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::encoding::*;
use super::*; use super::*;
// copy of encoding from actix-web headers
#[derive(Clone, PartialEq, Debug)]
pub enum Encoding {
Chunked,
Brotli,
Gzip,
Deflate,
Compress,
Identity,
Trailers,
EncodingExt(String),
}
impl fmt::Display for Encoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Encoding::*;
f.write_str(match *self {
Chunked => "chunked",
Brotli => "br",
Gzip => "gzip",
Deflate => "deflate",
Compress => "compress",
Identity => "identity",
Trailers => "trailers",
EncodingExt(ref s) => s.as_ref(),
})
}
}
impl str::FromStr for Encoding {
type Err = crate::error::ParseError;
fn from_str(s: &str) -> Result<Encoding, crate::error::ParseError> {
use Encoding::*;
match s {
"chunked" => Ok(Chunked),
"br" => Ok(Brotli),
"deflate" => Ok(Deflate),
"gzip" => Ok(Gzip),
"compress" => Ok(Compress),
"identity" => Ok(Identity),
"trailers" => Ok(Trailers),
_ => Ok(EncodingExt(s.to_owned())),
}
}
}
#[test] #[test]
fn test_quality_item_fmt_q_1() { fn test_quality_item_fmt_q_1() {
use Encoding::*;
let x = qitem(Chunked); let x = qitem(Chunked);
assert_eq!(format!("{}", x), "chunked"); assert_eq!(format!("{}", x), "chunked");
} }
#[test] #[test]
fn test_quality_item_fmt_q_0001() { fn test_quality_item_fmt_q_0001() {
use Encoding::*;
let x = QualityItem::new(Chunked, Quality(1)); let x = QualityItem::new(Chunked, Quality(1));
assert_eq!(format!("{}", x), "chunked; q=0.001"); assert_eq!(format!("{}", x), "chunked; q=0.001");
} }
#[test] #[test]
fn test_quality_item_fmt_q_05() { fn test_quality_item_fmt_q_05() {
use Encoding::*;
// Custom value // Custom value
let x = QualityItem { let x = QualityItem {
item: EncodingExt("identity".to_owned()), item: EncodingExt("identity".to_owned()),
@ -218,6 +266,7 @@ mod tests {
#[test] #[test]
fn test_quality_item_fmt_q_0() { fn test_quality_item_fmt_q_0() {
use Encoding::*;
// Custom value // Custom value
let x = QualityItem { let x = QualityItem {
item: EncodingExt("identity".to_owned()), item: EncodingExt("identity".to_owned()),
@ -228,6 +277,7 @@ mod tests {
#[test] #[test]
fn test_quality_item_from_str1() { fn test_quality_item_from_str1() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "chunked".parse(); let x: Result<QualityItem<Encoding>, _> = "chunked".parse();
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
@ -237,8 +287,10 @@ mod tests {
} }
); );
} }
#[test] #[test]
fn test_quality_item_from_str2() { fn test_quality_item_from_str2() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "chunked; q=1".parse(); let x: Result<QualityItem<Encoding>, _> = "chunked; q=1".parse();
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
@ -248,8 +300,10 @@ mod tests {
} }
); );
} }
#[test] #[test]
fn test_quality_item_from_str3() { fn test_quality_item_from_str3() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.5".parse(); let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.5".parse();
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
@ -259,8 +313,10 @@ mod tests {
} }
); );
} }
#[test] #[test]
fn test_quality_item_from_str4() { fn test_quality_item_from_str4() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.273".parse(); let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.273".parse();
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
@ -270,16 +326,19 @@ mod tests {
} }
); );
} }
#[test] #[test]
fn test_quality_item_from_str5() { fn test_quality_item_from_str5() {
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.2739999".parse(); let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.2739999".parse();
assert!(x.is_err()); assert!(x.is_err());
} }
#[test] #[test]
fn test_quality_item_from_str6() { fn test_quality_item_from_str6() {
let x: Result<QualityItem<Encoding>, _> = "gzip; q=2".parse(); let x: Result<QualityItem<Encoding>, _> = "gzip; q=2".parse();
assert!(x.is_err()); assert!(x.is_err());
} }
#[test] #[test]
fn test_quality_item_ordering() { fn test_quality_item_ordering() {
let x: QualityItem<Encoding> = "gzip; q=0.5".parse().ok().unwrap(); let x: QualityItem<Encoding> = "gzip; q=0.5".parse().ok().unwrap();

View file

@ -1,7 +1,6 @@
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};
use http::HeaderValue; use super::HeaderValue;
use crate::{error::ParseError, header::HTTP_VALUE}; use crate::{error::ParseError, header::HTTP_VALUE};
/// Reads a comma-delimited raw header into a Vec. /// Reads a comma-delimited raw header into a Vec.

View file

@ -359,10 +359,10 @@ impl ResponseBuilder {
/// ///
/// ``` /// ```
/// # use actix_http::Response; /// # use actix_http::Response;
/// use actix_http::http::header::ContentType; /// use actix_http::http::header;
/// ///
/// Response::Ok() /// Response::Ok()
/// .insert_header(ContentType(mime::APPLICATION_JSON)) /// .insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))
/// .insert_header(("X-TEST", "value")) /// .insert_header(("X-TEST", "value"))
/// .finish(); /// .finish();
/// ``` /// ```
@ -386,10 +386,10 @@ impl ResponseBuilder {
/// ///
/// ``` /// ```
/// # use actix_http::Response; /// # use actix_http::Response;
/// use actix_http::http::header::ContentType; /// use actix_http::http::header;
/// ///
/// Response::Ok() /// Response::Ok()
/// .append_header(ContentType(mime::APPLICATION_JSON)) /// .append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))
/// .append_header(("X-TEST", "value1")) /// .append_header(("X-TEST", "value1"))
/// .append_header(("X-TEST", "value2")) /// .append_header(("X-TEST", "value2"))
/// .finish(); /// .finish();
@ -682,7 +682,7 @@ impl ResponseBuilder {
}; };
if !contains { if !contains {
self.insert_header(header::ContentType(mime::APPLICATION_JSON)); self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
} }
self.body(Body::from(body)) self.body(Body::from(body))
@ -1133,7 +1133,7 @@ mod tests {
#[test] #[test]
fn response_builder_header_insert_typed() { fn response_builder_header_insert_typed() {
let mut res = Response::Ok(); let mut res = Response::Ok();
res.insert_header(header::ContentType(mime::APPLICATION_OCTET_STREAM)); res.insert_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
let res = res.finish(); let res = res.finish();
assert_eq!( assert_eq!(
@ -1158,8 +1158,8 @@ mod tests {
#[test] #[test]
fn response_builder_header_append_typed() { fn response_builder_header_append_typed() {
let mut res = Response::Ok(); let mut res = Response::Ok();
res.append_header(header::ContentType(mime::APPLICATION_OCTET_STREAM)); res.append_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
res.append_header(header::ContentType(mime::APPLICATION_JSON)); res.append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
let res = res.finish(); let res = res.finish();
let headers: Vec<_> = res.headers().get_all("Content-Type").cloned().collect(); let headers: Vec<_> = res.headers().get_all("Content-Type").cloned().collect();

View file

@ -7,8 +7,10 @@
### Changed ### Changed
* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] * `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
* Fix http/https encoding when enabling `compress` feature. [#2116] * Fix http/https encoding when enabling `compress` feature. [#2116]
* Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header methods now take `IntoHeaderPair` tuples. [#2094]
[#2081]: https://github.com/actix/actix-web/pull/2081 [#2081]: https://github.com/actix/actix-web/pull/2081
[#2094]: https://github.com/actix/actix-web/pull/2094
[#2114]: https://github.com/actix/actix-web/pull/2114 [#2114]: https://github.com/actix/actix-web/pull/2114
[#2116]: https://github.com/actix/actix-web/pull/2116 [#2116]: https://github.com/actix/actix-web/pull/2116

View file

@ -189,12 +189,12 @@ impl ClientRequest {
/// # #[actix_rt::main] /// # #[actix_rt::main]
/// # async fn main() { /// # async fn main() {
/// # use awc::Client; /// # use awc::Client;
/// use awc::http::header::ContentType; /// use awc::http::header::CONTENT_TYPE;
/// ///
/// Client::new() /// Client::new()
/// .get("http://www.rust-lang.org") /// .get("http://www.rust-lang.org")
/// .insert_header(("X-TEST", "value")) /// .insert_header(("X-TEST", "value"))
/// .insert_header(ContentType(mime::APPLICATION_JSON)); /// .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON));
/// # } /// # }
/// ``` /// ```
pub fn append_header<H>(mut self, header: H) -> Self pub fn append_header<H>(mut self, header: H) -> Self
@ -548,6 +548,8 @@ impl fmt::Debug for ClientRequest {
mod tests { mod tests {
use std::time::SystemTime; use std::time::SystemTime;
use actix_http::http::header::HttpDate;
use super::*; use super::*;
use crate::Client; use crate::Client;
@ -564,7 +566,7 @@ mod tests {
let req = Client::new() let req = Client::new()
.put("/") .put("/")
.version(Version::HTTP_2) .version(Version::HTTP_2)
.insert_header(header::Date(SystemTime::now().into())) .insert_header((header::DATE, HttpDate::from(SystemTime::now())))
.content_type("plain/text") .content_type("plain/text")
.append_header((header::SERVER, "awc")); .append_header((header::SERVER, "awc"));

View file

@ -449,13 +449,13 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_body() { async fn test_body() {
let mut req = TestResponse::with_header(header::CONTENT_LENGTH, "xxxx").finish(); let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "xxxx")).finish();
match req.body().await.err().unwrap() { match req.body().await.err().unwrap() {
PayloadError::UnknownLength => {} PayloadError::UnknownLength => {}
_ => unreachable!("error"), _ => unreachable!("error"),
} }
let mut req = TestResponse::with_header(header::CONTENT_LENGTH, "10000000").finish(); let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "10000000")).finish();
match req.body().await.err().unwrap() { match req.body().await.err().unwrap() {
PayloadError::Overflow => {} PayloadError::Overflow => {}
_ => unreachable!("error"), _ => unreachable!("error"),
@ -497,23 +497,23 @@ mod tests {
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let mut req = TestResponse::default() let mut req = TestResponse::default()
.header( .insert_header((
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/text"), header::HeaderValue::from_static("application/text"),
) ))
.finish(); .finish();
let json = JsonBody::<_, MyObject>::new(&mut req).await; let json = JsonBody::<_, MyObject>::new(&mut req).await;
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let mut req = TestResponse::default() let mut req = TestResponse::default()
.header( .insert_header((
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
) ))
.header( .insert_header((
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("10000"), header::HeaderValue::from_static("10000"),
) ))
.finish(); .finish();
let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await; let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await;
@ -523,14 +523,14 @@ mod tests {
)); ));
let mut req = TestResponse::default() let mut req = TestResponse::default()
.header( .insert_header((
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
) ))
.header( .insert_header((
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"), header::HeaderValue::from_static("16"),
) ))
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.finish(); .finish();

View file

@ -1,8 +1,6 @@
//! Test helpers for actix http client to use during testing. //! Test helpers for actix http client to use during testing.
use std::convert::TryFrom; use actix_http::http::header::IntoHeaderPair;
use actix_http::http::{StatusCode, Version};
use actix_http::http::header::{Header, IntoHeaderValue};
use actix_http::http::{Error as HttpError, HeaderName, StatusCode, Version};
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
use actix_http::{ use actix_http::{
cookie::{Cookie, CookieJar}, cookie::{Cookie, CookieJar},
@ -34,13 +32,11 @@ impl Default for TestResponse {
impl TestResponse { impl TestResponse {
/// Create TestResponse and set header /// Create TestResponse and set header
pub fn with_header<K, V>(key: K, value: V) -> Self pub fn with_header<H>(header: H) -> Self
where where
HeaderName: TryFrom<K>, H: IntoHeaderPair,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue,
{ {
Self::default().header(key, value) Self::default().insert_header(header)
} }
/// Set HTTP version of this response /// Set HTTP version of this response
@ -49,27 +45,26 @@ impl TestResponse {
self self
} }
/// Set a header /// Insert a header
pub fn set<H: Header>(mut self, hdr: H) -> Self { pub fn insert_header<H>(mut self, header: H) -> Self
if let Ok(value) = hdr.try_into_value() { where
self.head.headers.append(H::name(), value); H: IntoHeaderPair,
{
if let Ok((key, value)) = header.try_into_header_pair() {
self.head.headers.insert(key, value);
return self; return self;
} }
panic!("Can not set header"); panic!("Can not set header");
} }
/// Append a header /// Append a header
pub fn header<K, V>(mut self, key: K, value: V) -> Self pub fn append_header<H>(mut self, header: H) -> Self
where where
HeaderName: TryFrom<K>, H: IntoHeaderPair,
<HeaderName as TryFrom<K>>::Error: Into<HttpError>,
V: IntoHeaderValue,
{ {
if let Ok(key) = HeaderName::try_from(key) { if let Ok((key, value)) = header.try_into_header_pair() {
if let Ok(value) = value.try_into_value() { self.head.headers.append(key, value);
self.head.headers.append(key, value); return self;
return self;
}
} }
panic!("Can not create header"); panic!("Can not create header");
} }
@ -115,6 +110,8 @@ impl TestResponse {
mod tests { mod tests {
use std::time::SystemTime; use std::time::SystemTime;
use actix_http::http::header::HttpDate;
use super::*; use super::*;
use crate::{cookie, http::header}; use crate::{cookie, http::header};
@ -122,7 +119,7 @@ mod tests {
fn test_basics() { fn test_basics() {
let res = TestResponse::default() let res = TestResponse::default()
.version(Version::HTTP_2) .version(Version::HTTP_2)
.set(header::Date(SystemTime::now().into())) .insert_header((header::DATE, HttpDate::from(SystemTime::now())))
.cookie(cookie::Cookie::build("name", "value").finish()) .cookie(cookie::Cookie::build("name", "value").finish())
.finish(); .finish();
assert!(res.headers().contains_key(header::SET_COOKIE)); assert!(res.headers().contains_key(header::SET_COOKIE));

View file

@ -2,10 +2,10 @@ use std::cmp::Ordering;
use mime::Mime; use mime::Mime;
use crate::header::{qitem, QualityItem}; use super::{qitem, QualityItem};
use crate::http::header; use crate::http::header;
header! { crate::header! {
/// `Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2) /// `Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2)
/// ///
/// The `Accept` header field can be used by user agents to specify /// The `Accept` header field can be used by user agents to specify
@ -33,10 +33,10 @@ header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{Accept, qitem}; /// use actix_web::http::header::{Accept, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Accept(vec![ /// Accept(vec![
/// qitem(mime::TEXT_HTML), /// qitem(mime::TEXT_HTML),
@ -45,10 +45,10 @@ header! {
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{Accept, qitem}; /// use actix_web::http::header::{Accept, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Accept(vec![ /// Accept(vec![
/// qitem(mime::APPLICATION_JSON), /// qitem(mime::APPLICATION_JSON),
@ -57,10 +57,10 @@ header! {
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{Accept, QualityItem, q, qitem}; /// use actix_web::http::header::{Accept, QualityItem, q, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Accept(vec![ /// Accept(vec![
/// qitem(mime::TEXT_HTML), /// qitem(mime::TEXT_HTML),
@ -116,8 +116,8 @@ header! {
#[test] #[test]
fn test_fuzzing1() { fn test_fuzzing1() {
use crate::test::TestRequest; use actix_http::test::TestRequest;
let req = TestRequest::default().insert_header((crate::header::ACCEPT, "chunk#;e")).finish(); let req = TestRequest::default().insert_header((crate::http::header::ACCEPT, "chunk#;e")).finish();
let header = Accept::parse(&req); let header = Accept::parse(&req);
assert!(header.is_ok()); assert!(header.is_ok());
} }
@ -213,7 +213,7 @@ impl Accept {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::header::q; use crate::http::header::q;
#[test] #[test]
fn test_mime_precedence() { fn test_mime_precedence() {

View file

@ -1,6 +1,6 @@
use crate::header::{Charset, QualityItem, ACCEPT_CHARSET}; use super::{Charset, QualityItem, ACCEPT_CHARSET};
header! { crate::header! {
/// `Accept-Charset` header, defined in /// `Accept-Charset` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3)
/// ///
@ -22,20 +22,20 @@ header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptCharset, Charset, qitem}; /// use actix_web::http::header::{AcceptCharset, Charset, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptCharset(vec![qitem(Charset::Us_Ascii)]) /// AcceptCharset(vec![qitem(Charset::Us_Ascii)])
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptCharset, Charset, q, QualityItem}; /// use actix_web::http::header::{AcceptCharset, Charset, q, QualityItem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptCharset(vec![ /// AcceptCharset(vec![
/// QualityItem::new(Charset::Us_Ascii, q(900)), /// QualityItem::new(Charset::Us_Ascii, q(900)),
@ -45,10 +45,10 @@ header! {
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptCharset, Charset, qitem}; /// use actix_web::http::header::{AcceptCharset, Charset, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptCharset(vec![qitem(Charset::Ext("utf-8".to_owned()))]) /// AcceptCharset(vec![qitem(Charset::Ext("utf-8".to_owned()))])
/// ); /// );

View file

@ -26,18 +26,20 @@ header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use hyper::header::{Headers, AcceptEncoding, Encoding, qitem}; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptEncoding, Encoding, qitem};
/// ///
/// let mut headers = Headers::new(); /// let mut builder = HttpResponse::new();
/// headers.set( /// builder.insert_header(
/// AcceptEncoding(vec![qitem(Encoding::Chunked)]) /// AcceptEncoding(vec![qitem(Encoding::Chunked)])
/// ); /// );
/// ``` /// ```
/// ``` /// ```
/// use hyper::header::{Headers, AcceptEncoding, Encoding, qitem}; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptEncoding, Encoding, qitem};
/// ///
/// let mut headers = Headers::new(); /// let mut builder = HttpResponse::new();
/// headers.set( /// builder.insert_header(
/// AcceptEncoding(vec![ /// AcceptEncoding(vec![
/// qitem(Encoding::Chunked), /// qitem(Encoding::Chunked),
/// qitem(Encoding::Gzip), /// qitem(Encoding::Gzip),
@ -46,10 +48,11 @@ header! {
/// ); /// );
/// ``` /// ```
/// ``` /// ```
/// use hyper::header::{Headers, AcceptEncoding, Encoding, QualityItem, q, qitem}; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptEncoding, Encoding, QualityItem, q, qitem};
/// ///
/// let mut headers = Headers::new(); /// let mut builder = HttpResponse::new();
/// headers.set( /// builder.insert_header(
/// AcceptEncoding(vec![ /// AcceptEncoding(vec![
/// qitem(Encoding::Chunked), /// qitem(Encoding::Chunked),
/// QualityItem::new(Encoding::Gzip, q(600)), /// QualityItem::new(Encoding::Gzip, q(600)),

View file

@ -1,7 +1,7 @@
use crate::header::{QualityItem, ACCEPT_LANGUAGE}; use super::{QualityItem, ACCEPT_LANGUAGE};
use language_tags::LanguageTag; use language_tags::LanguageTag;
header! { crate::header! {
/// `Accept-Language` header, defined in /// `Accept-Language` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5)
/// ///
@ -24,10 +24,10 @@ header! {
/// ///
/// ``` /// ```
/// use language_tags::langtag; /// use language_tags::langtag;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptLanguage, LanguageTag, qitem}; /// use actix_web::http::header::{AcceptLanguage, LanguageTag, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let mut langtag: LanguageTag = Default::default(); /// let mut langtag: LanguageTag = Default::default();
/// langtag.language = Some("en".to_owned()); /// langtag.language = Some("en".to_owned());
/// langtag.region = Some("US".to_owned()); /// langtag.region = Some("US".to_owned());
@ -40,10 +40,10 @@ header! {
/// ///
/// ``` /// ```
/// use language_tags::langtag; /// use language_tags::langtag;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{AcceptLanguage, QualityItem, q, qitem}; /// use actix_web::http::header::{AcceptLanguage, QualityItem, q, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptLanguage(vec![ /// AcceptLanguage(vec![
/// qitem(langtag!(da)), /// qitem(langtag!(da)),

View file

@ -1,7 +1,7 @@
use http::header; use actix_http::http::Method;
use http::Method; use crate::http::header;
header! { crate::header! {
/// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1) /// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1)
/// ///
/// The `Allow` header field lists the set of methods advertised as /// The `Allow` header field lists the set of methods advertised as
@ -23,20 +23,20 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::{header::Allow, Method}; /// use actix_web::http::{header::Allow, Method};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Allow(vec![Method::GET]) /// Allow(vec![Method::GET])
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::{header::Allow, Method}; /// use actix_web::http::{header::Allow, Method};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Allow(vec![ /// Allow(vec![
/// Method::GET, /// Method::GET,

View file

@ -1,12 +1,12 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::str::FromStr; use std::str::FromStr;
use http::header; use super::{
use crate::header::{
fmt_comma_delimited, from_comma_delimited, Header, IntoHeaderValue, Writer, fmt_comma_delimited, from_comma_delimited, Header, IntoHeaderValue, Writer,
}; };
use crate::http::header;
/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2) /// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
/// ///
/// The `Cache-Control` header field is used to specify directives for /// The `Cache-Control` header field is used to specify directives for
@ -29,18 +29,18 @@ use crate::header::{
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{CacheControl, CacheDirective}; /// use actix_web::http::header::{CacheControl, CacheDirective};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header(CacheControl(vec![CacheDirective::MaxAge(86400u32)])); /// builder.insert_header(CacheControl(vec![CacheDirective::MaxAge(86400u32)]));
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{CacheControl, CacheDirective}; /// use actix_web::http::header::{CacheControl, CacheDirective};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header(CacheControl(vec![ /// builder.insert_header(CacheControl(vec![
/// CacheDirective::NoCache, /// CacheDirective::NoCache,
/// CacheDirective::Private, /// CacheDirective::Private,
@ -191,8 +191,8 @@ impl FromStr for CacheDirective {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::header::Header; use crate::http::header::Header;
use crate::test::TestRequest; use actix_http::test::TestRequest;
#[test] #[test]
fn test_parse_multiple_headers() { fn test_parse_multiple_headers() {

View file

@ -10,7 +10,8 @@ use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use crate::header::{self, ExtendedValue, Header, IntoHeaderValue, Writer}; use crate::http::header;
use super::{ExtendedValue, Header, IntoHeaderValue, Writer};
/// Split at the index of the first `needle` if it exists or at the end. /// Split at the index of the first `needle` if it exists or at the end.
fn split_once(haystack: &str, needle: char) -> (&str, &str) { fn split_once(haystack: &str, needle: char) -> (&str, &str) {
@ -63,7 +64,7 @@ impl<'a> From<&'a str> for DispositionType {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_http::http::header::DispositionParam; /// use actix_web::http::header::DispositionParam;
/// ///
/// let param = DispositionParam::Filename(String::from("sample.txt")); /// let param = DispositionParam::Filename(String::from("sample.txt"));
/// assert!(param.is_filename()); /// assert!(param.is_filename());
@ -240,7 +241,7 @@ impl DispositionParam {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use actix_http::http::header::{ /// use actix_web::http::header::{
/// Charset, ContentDisposition, DispositionParam, DispositionType, /// Charset, ContentDisposition, DispositionParam, DispositionType,
/// ExtendedValue, /// ExtendedValue,
/// }; /// };
@ -554,8 +555,8 @@ impl fmt::Display for ContentDisposition {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ContentDisposition, DispositionParam, DispositionType}; use super::{ContentDisposition, DispositionParam, DispositionType};
use crate::header::shared::Charset; use crate::http::header::Charset;
use crate::header::{ExtendedValue, HeaderValue}; use crate::http::header::{ExtendedValue, HeaderValue};
#[test] #[test]
fn test_from_raw_basic() { fn test_from_raw_basic() {

View file

@ -1,7 +1,7 @@
use crate::header::{QualityItem, CONTENT_LANGUAGE}; use super::{QualityItem, CONTENT_LANGUAGE};
use language_tags::LanguageTag; use language_tags::LanguageTag;
header! { crate::header! {
/// `Content-Language` header, defined in /// `Content-Language` header, defined in
/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2) /// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2)
/// ///
@ -25,10 +25,10 @@ header! {
/// ///
/// ``` /// ```
/// use language_tags::langtag; /// use language_tags::langtag;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{ContentLanguage, qitem}; /// use actix_web::http::header::{ContentLanguage, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentLanguage(vec![ /// ContentLanguage(vec![
/// qitem(langtag!(en)), /// qitem(langtag!(en)),
@ -38,10 +38,10 @@ header! {
/// ///
/// ``` /// ```
/// use language_tags::langtag; /// use language_tags::langtag;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{ContentLanguage, qitem}; /// use actix_web::http::header::{ContentLanguage, qitem};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentLanguage(vec![ /// ContentLanguage(vec![
/// qitem(langtag!(da)), /// qitem(langtag!(da)),

View file

@ -2,11 +2,11 @@ use std::fmt::{self, Display, Write};
use std::str::FromStr; use std::str::FromStr;
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::{ use super::{
HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE, HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE,
}; };
header! { crate::header! {
/// `Content-Range` header, defined in /// `Content-Range` header, defined in
/// [RFC7233](http://tools.ietf.org/html/rfc7233#section-4.2) /// [RFC7233](http://tools.ietf.org/html/rfc7233#section-4.2)
(ContentRange, CONTENT_RANGE) => [ContentRangeSpec] (ContentRange, CONTENT_RANGE) => [ContentRangeSpec]

View file

@ -1,7 +1,7 @@
use crate::header::CONTENT_TYPE; use super::CONTENT_TYPE;
use mime::Mime; use mime::Mime;
header! { crate::header! {
/// `Content-Type` header, defined in /// `Content-Type` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5) /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5)
/// ///
@ -31,20 +31,20 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::ContentType; /// use actix_web::http::header::ContentType;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentType::json() /// ContentType::json()
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::ContentType; /// use actix_web::http::header::ContentType;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentType(mime::TEXT_HTML) /// ContentType(mime::TEXT_HTML)
/// ); /// );

View file

@ -1,7 +1,7 @@
use crate::header::{HttpDate, DATE}; use super::{HttpDate, DATE};
use std::time::SystemTime; use std::time::SystemTime;
header! { crate::header! {
/// `Date` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.1.2) /// `Date` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.1.2)
/// ///
/// The `Date` header field represents the date and time at which the /// The `Date` header field represents the date and time at which the
@ -21,10 +21,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::SystemTime; /// use std::time::SystemTime;
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::Date; /// use actix_web::http::header::Date;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// Date(SystemTime::now().into()) /// Date(SystemTime::now().into())
/// ); /// );

View file

@ -1,7 +1,7 @@
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
use std::str::FromStr; use std::str::FromStr;
use crate::header::{HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer}; use super::{HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer};
/// check that each char in the slice is either: /// check that each char in the slice is either:
/// 1. `%x21`, or /// 1. `%x21`, or

View file

@ -1,6 +1,6 @@
use crate::header::{EntityTag, ETAG}; use super::{EntityTag, ETAG};
header! { crate::header! {
/// `ETag` header, defined in [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.3) /// `ETag` header, defined in [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.3)
/// ///
/// The `ETag` header field in a response provides the current entity-tag /// The `ETag` header field in a response provides the current entity-tag
@ -28,20 +28,20 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{ETag, EntityTag}; /// use actix_web::http::header::{ETag, EntityTag};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ETag(EntityTag::new(false, "xyzzy".to_owned())) /// ETag(EntityTag::new(false, "xyzzy".to_owned()))
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{ETag, EntityTag}; /// use actix_web::http::header::{ETag, EntityTag};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ETag(EntityTag::new(true, "xyzzy".to_owned())) /// ETag(EntityTag::new(true, "xyzzy".to_owned()))
/// ); /// );

View file

@ -1,6 +1,6 @@
use crate::header::{HttpDate, EXPIRES}; use super::{HttpDate, EXPIRES};
header! { crate::header! {
/// `Expires` header, defined in [RFC7234](http://tools.ietf.org/html/rfc7234#section-5.3) /// `Expires` header, defined in [RFC7234](http://tools.ietf.org/html/rfc7234#section-5.3)
/// ///
/// The `Expires` header field gives the date/time after which the /// The `Expires` header field gives the date/time after which the
@ -23,10 +23,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::Expires; /// use actix_web::http::header::Expires;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let expiration = SystemTime::now() + Duration::from_secs(60 * 60 * 24); /// let expiration = SystemTime::now() + Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// Expires(expiration.into()) /// Expires(expiration.into())

View file

@ -1,6 +1,6 @@
use crate::header::{EntityTag, IF_MATCH}; use super::{EntityTag, IF_MATCH};
header! { crate::header! {
/// `If-Match` header, defined in /// `If-Match` header, defined in
/// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1) /// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1)
/// ///
@ -30,18 +30,18 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::IfMatch; /// use actix_web::http::header::IfMatch;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header(IfMatch::Any); /// builder.insert_header(IfMatch::Any);
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{IfMatch, EntityTag}; /// use actix_web::http::header::{IfMatch, EntityTag};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// IfMatch::Items(vec![ /// IfMatch::Items(vec![
/// EntityTag::new(false, "xyzzy".to_owned()), /// EntityTag::new(false, "xyzzy".to_owned()),

View file

@ -1,6 +1,6 @@
use crate::header::{HttpDate, IF_MODIFIED_SINCE}; use super::{HttpDate, IF_MODIFIED_SINCE};
header! { crate::header! {
/// `If-Modified-Since` header, defined in /// `If-Modified-Since` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.3) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.3)
/// ///
@ -23,10 +23,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::IfModifiedSince; /// use actix_web::http::header::IfModifiedSince;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// IfModifiedSince(modified.into()) /// IfModifiedSince(modified.into())

View file

@ -1,6 +1,6 @@
use crate::header::{EntityTag, IF_NONE_MATCH}; use super::{EntityTag, IF_NONE_MATCH};
header! { crate::header! {
/// `If-None-Match` header, defined in /// `If-None-Match` header, defined in
/// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2) /// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2)
/// ///
@ -32,18 +32,18 @@ header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::IfNoneMatch; /// use actix_web::http::header::IfNoneMatch;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header(IfNoneMatch::Any); /// builder.insert_header(IfNoneMatch::Any);
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{IfNoneMatch, EntityTag}; /// use actix_web::http::header::{IfNoneMatch, EntityTag};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// IfNoneMatch::Items(vec![ /// IfNoneMatch::Items(vec![
/// EntityTag::new(false, "xyzzy".to_owned()), /// EntityTag::new(false, "xyzzy".to_owned()),
@ -66,8 +66,8 @@ header! {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::IfNoneMatch; use super::IfNoneMatch;
use crate::header::{EntityTag, Header, IF_NONE_MATCH}; use crate::http::header::{EntityTag, Header, IF_NONE_MATCH};
use crate::test::TestRequest; use actix_http::test::TestRequest;
#[test] #[test]
fn test_if_none_match() { fn test_if_none_match() {

View file

@ -1,8 +1,9 @@
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
use crate::http::header;
use crate::error::ParseError; use crate::error::ParseError;
use crate::header::{ use super::{
self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate,
IntoHeaderValue, InvalidHeaderValue, Writer, IntoHeaderValue, InvalidHeaderValue, Writer,
}; };
use crate::HttpMessage; use crate::HttpMessage;
@ -36,10 +37,10 @@ use crate::HttpMessage;
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::{EntityTag, IfRange}; /// use actix_web::http::header::{EntityTag, IfRange};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// IfRange::EntityTag( /// IfRange::EntityTag(
/// EntityTag::new(false, "abc".to_owned()) /// EntityTag::new(false, "abc".to_owned())
@ -49,9 +50,9 @@ use crate::HttpMessage;
/// ///
/// ``` /// ```
/// use std::time::{Duration, SystemTime}; /// use std::time::{Duration, SystemTime};
/// use actix_http::{http::header::IfRange, Response}; /// use actix_web::{http::header::IfRange, HttpResponse};
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// IfRange::Date(fetched.into()) /// IfRange::Date(fetched.into())
@ -111,7 +112,7 @@ impl IntoHeaderValue for IfRange {
#[cfg(test)] #[cfg(test)]
mod test_if_range { mod test_if_range {
use super::IfRange as HeaderField; use super::IfRange as HeaderField;
use crate::header::*; use crate::http::header::*;
use std::str; use std::str;
test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]); test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]);

View file

@ -1,6 +1,6 @@
use crate::header::{HttpDate, IF_UNMODIFIED_SINCE}; use super::{HttpDate, IF_UNMODIFIED_SINCE};
header! { crate::header! {
/// `If-Unmodified-Since` header, defined in /// `If-Unmodified-Since` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.4) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.4)
/// ///
@ -24,10 +24,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::IfUnmodifiedSince; /// use actix_web::http::header::IfUnmodifiedSince;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// IfUnmodifiedSince(modified.into()) /// IfUnmodifiedSince(modified.into())

View file

@ -1,6 +1,6 @@
use crate::header::{HttpDate, LAST_MODIFIED}; use super::{HttpDate, LAST_MODIFIED};
header! { crate::header! {
/// `Last-Modified` header, defined in /// `Last-Modified` header, defined in
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2) /// [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2)
/// ///
@ -23,10 +23,10 @@ header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_http::Response; /// use actix_web::HttpResponse;
/// use actix_http::http::header::LastModified; /// use actix_web::http::header::LastModified;
/// ///
/// let mut builder = Response::Ok(); /// let mut builder = HttpResponse::Ok();
/// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// builder.insert_header(
/// LastModified(modified.into()) /// LastModified(modified.into())

View file

@ -7,6 +7,10 @@
//! is used, such as `ContentType(pub Mime)`. //! is used, such as `ContentType(pub Mime)`.
#![cfg_attr(rustfmt, rustfmt_skip)] #![cfg_attr(rustfmt, rustfmt_skip)]
use std::fmt;
use bytes::{BytesMut, Bytes};
pub use actix_http::http::header::*;
pub use self::accept_charset::AcceptCharset; pub use self::accept_charset::AcceptCharset;
//pub use self::accept_encoding::AcceptEncoding; //pub use self::accept_encoding::AcceptEncoding;
pub use self::accept::Accept; pub use self::accept::Accept;
@ -18,7 +22,6 @@ pub use self::content_disposition::{
}; };
pub use self::content_language::ContentLanguage; pub use self::content_language::ContentLanguage;
pub use self::content_range::{ContentRange, ContentRangeSpec}; pub use self::content_range::{ContentRange, ContentRangeSpec};
pub use self::content_encoding::{ContentEncoding};
pub use self::content_type::ContentType; pub use self::content_type::ContentType;
pub use self::date::Date; pub use self::date::Date;
pub use self::etag::ETag; pub use self::etag::ETag;
@ -29,7 +32,39 @@ pub use self::if_none_match::IfNoneMatch;
pub use self::if_range::IfRange; pub use self::if_range::IfRange;
pub use self::if_unmodified_since::IfUnmodifiedSince; pub use self::if_unmodified_since::IfUnmodifiedSince;
pub use self::last_modified::LastModified; pub use self::last_modified::LastModified;
pub use self::encoding::Encoding;
pub use self::entity::EntityTag;
//pub use self::range::{Range, ByteRangeSpec}; //pub use self::range::{Range, ByteRangeSpec};
pub(crate) use actix_http::http::header::{fmt_comma_delimited, from_comma_delimited, from_one_raw_str};
#[derive(Debug, Default)]
struct Writer {
buf: BytesMut,
}
impl Writer {
pub fn new() -> Writer {
Writer::default()
}
pub fn take(&mut self) -> Bytes {
self.buf.split().freeze()
}
}
impl fmt::Write for Writer {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.buf.extend_from_slice(s.as_bytes());
Ok(())
}
#[inline]
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
fmt::write(self, args)
}
}
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
@ -61,9 +96,9 @@ macro_rules! __hyper__tm {
#[cfg(test)] #[cfg(test)]
mod $tm{ mod $tm{
use std::str; use std::str;
use http::Method; use actix_http::http::Method;
use mime::*; use mime::*;
use $crate::header::*; use $crate::http::header::*;
use super::$id as HeaderField; use super::$id as HeaderField;
$($tf)* $($tf)*
} }
@ -77,8 +112,7 @@ macro_rules! test_header {
($id:ident, $raw:expr) => { ($id:ident, $raw:expr) => {
#[test] #[test]
fn $id() { fn $id() {
use super::*; use actix_http::test;
use $crate::test;
let raw = $raw; let raw = $raw;
let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect(); let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
@ -106,7 +140,7 @@ macro_rules! test_header {
($id:ident, $raw:expr, $typed:expr) => { ($id:ident, $raw:expr, $typed:expr) => {
#[test] #[test]
fn $id() { fn $id() {
use $crate::test; use actix_http::test;
let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect(); let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect();
let mut req = test::TestRequest::default(); let mut req = test::TestRequest::default();
@ -134,6 +168,7 @@ macro_rules! test_header {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! header { macro_rules! header {
// $a:meta: Attributes associated with the header item (usually docs) // $a:meta: Attributes associated with the header item (usually docs)
@ -341,7 +376,6 @@ mod allow;
mod cache_control; mod cache_control;
mod content_disposition; mod content_disposition;
mod content_language; mod content_language;
mod content_encoding;
mod content_range; mod content_range;
mod content_type; mod content_type;
mod date; mod date;
@ -353,3 +387,5 @@ mod if_none_match;
mod if_range; mod if_range;
mod if_unmodified_since; mod if_unmodified_since;
mod last_modified; mod last_modified;
mod encoding;
mod entity;

View file

@ -1,8 +1,8 @@
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::str::FromStr; use std::str::FromStr;
use header::parsing::from_one_raw_str; use super::parsing::from_one_raw_str;
use header::{Header, Raw}; use super::{Header, Raw};
/// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1)
/// ///

2
src/http/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod header;
pub use actix_http::http::*;

View file

@ -84,6 +84,7 @@ pub mod error;
mod extract; mod extract;
pub mod guard; pub mod guard;
mod handler; mod handler;
pub mod http;
mod info; mod info;
pub mod middleware; pub mod middleware;
mod request; mod request;
@ -102,7 +103,7 @@ pub mod web;
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
pub use actix_http::cookie; pub use actix_http::cookie;
pub use actix_http::Response as HttpResponse; pub use actix_http::Response as HttpResponse;
pub use actix_http::{body, http, Error, HttpMessage, ResponseError, Result}; pub use actix_http::{body, Error, HttpMessage, ResponseError, Result};
pub use actix_rt as rt; pub use actix_rt as rt;
pub use actix_web_codegen::*; pub use actix_web_codegen::*;

View file

@ -8,8 +8,7 @@ use std::{fmt, net, thread, time};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
use actix_http::cookie::Cookie; use actix_http::cookie::Cookie;
use actix_http::http::header::{ContentType, HeaderMap, IntoHeaderPair}; use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
use actix_http::http::{Method, StatusCode, Uri, Version};
use actix_http::test::TestRequest as HttpTestRequest; use actix_http::test::TestRequest as HttpTestRequest;
use actix_http::{ws, Extensions, HttpService, Request}; use actix_http::{ws, Extensions, HttpService, Request};
use actix_router::{Path, ResourceDef, Url}; use actix_router::{Path, ResourceDef, Url};
@ -31,6 +30,7 @@ use crate::app_service::AppInitServiceState;
use crate::config::AppConfig; use crate::config::AppConfig;
use crate::data::Data; use crate::data::Data;
use crate::dev::{Body, MessageBody, Payload, Server}; use crate::dev::{Body, MessageBody, Payload, Server};
use crate::http::header::{ContentType, IntoHeaderPair};
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::{Error, HttpRequest, HttpResponse}; use crate::{Error, HttpRequest, HttpResponse};
@ -162,7 +162,7 @@ where
let mut resp = app let mut resp = app
.call(req) .call(req)
.await .await
.unwrap_or_else(|_| panic!("read_response failed at application call")); .unwrap_or_else(|e| panic!("read_response failed at application call: {}", e));
let mut body = resp.take_body(); let mut body = resp.take_body();
let mut bytes = BytesMut::new(); let mut bytes = BytesMut::new();
@ -254,8 +254,12 @@ where
{ {
let body = read_body(res).await; let body = read_body(res).await;
serde_json::from_slice(&body) serde_json::from_slice(&body).unwrap_or_else(|e| {
.unwrap_or_else(|e| panic!("read_response_json failed during deserialization: {}", e)) panic!(
"read_response_json failed during deserialization of body: {:?}, {}",
body, e
)
})
} }
pub async fn load_stream<S>(mut stream: S) -> Result<Bytes, Error> pub async fn load_stream<S>(mut stream: S) -> Result<Bytes, Error>
@ -311,8 +315,12 @@ where
{ {
let body = read_response(app, req).await; let body = read_response(app, req).await;
serde_json::from_slice(&body) serde_json::from_slice(&body).unwrap_or_else(|_| {
.unwrap_or_else(|_| panic!("read_response_json failed during deserialization")) panic!(
"read_response_json failed during deserialization of body: {:?}",
body
)
})
} }
/// Test `Request` builder. /// Test `Request` builder.

112
src/types/header.rs Normal file
View file

@ -0,0 +1,112 @@
//! For header extractor helper documentation, see [`Header`](crate::types::Header).
use std::{fmt, ops};
use actix_utils::future::{err, ok, Ready};
use crate::{
dev::Payload, error::ParseError, extract::FromRequest, http::header::Header as ParseHeader,
HttpRequest,
};
/// Extract typed headers from the request.
///
/// To extract a header, the inner type `T` must implement the
/// [`Header`](crate::http::header::Header) trait.
///
/// # Examples
/// ```
/// use actix_web::{get, web, http::header};
///
/// #[get("/")]
/// async fn index(date: web::Header<header::Date>) -> String {
/// format!("Request was sent at {}", date.to_string())
/// }
/// ```
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Header<T>(pub T);
impl<T> Header<T> {
/// Unwrap into the inner `T` value.
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> ops::Deref for Header<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> ops::DerefMut for Header<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> fmt::Debug for Header<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Header: {:?}", self.0)
}
}
impl<T> fmt::Display for Header<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<T> FromRequest for Header<T>
where
T: ParseHeader,
{
type Error = ParseError;
type Future = Ready<Result<Self, Self::Error>>;
type Config = ();
#[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
match ParseHeader::parse(req) {
Ok(header) => ok(Header(header)),
Err(e) => err(e),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::http::{header, Method};
use crate::test::TestRequest;
#[actix_rt::test]
async fn test_header_extract() {
let (req, mut pl) = TestRequest::default()
.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))
.insert_header((header::ALLOW, header::Allow(vec![Method::GET])))
.to_http_parts();
let s = Header::<header::ContentType>::from_request(&req, &mut pl)
.await
.unwrap();
assert_eq!(s.into_inner().0, mime::APPLICATION_JSON);
let s = Header::<header::Allow>::from_request(&req, &mut pl)
.await
.unwrap();
assert_eq!(s.into_inner().0, vec![Method::GET]);
assert!(Header::<header::Date>::from_request(&req, &mut pl)
.await
.is_err());
}
}

View file

@ -3,6 +3,7 @@
// TODO: review visibility // TODO: review visibility
mod either; mod either;
pub(crate) mod form; pub(crate) mod form;
mod header;
pub(crate) mod json; pub(crate) mod json;
mod path; mod path;
pub(crate) mod payload; pub(crate) mod payload;
@ -11,6 +12,7 @@ pub(crate) mod readlines;
pub use self::either::{Either, EitherExtractError}; pub use self::either::{Either, EitherExtractError};
pub use self::form::{Form, FormConfig}; pub use self::form::{Form, FormConfig};
pub use self::header::Header;
pub use self::json::{Json, JsonConfig}; pub use self::json::{Json, JsonConfig};
pub use self::path::{Path, PathConfig}; pub use self::path::{Path, PathConfig};
pub use self::payload::{Payload, PayloadConfig}; pub use self::payload::{Payload, PayloadConfig};