1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-05-20 09:18:26 +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
### Added
* `Header` extractor for extracting common HTTP headers in handlers. [#2094]
* Added `TestServer::client_headers` method. [#2097]
### Fixed
@ -17,6 +18,8 @@
[#2067]: https://github.com/actix/actix-web/pull/2067
[#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

View file

@ -97,6 +97,8 @@ either = "1.5.3"
encoding_rs = "0.8"
futures-core = { 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"
mime = "0.3"
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]
### Removed
* Common HTTP headers were moved into actix-web. [2094]
* `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127]
[#2063]: https://github.com/actix/actix-web/pull/2063
[#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

View file

@ -1,9 +1,6 @@
//! Typed HTTP headers, pre-defined `HeaderName`s, traits for parsing and conversion, and other
//! header utility methods.
use std::fmt;
use bytes::{Bytes, BytesMut};
use percent_encoding::{AsciiSet, CONTROLS};
pub use http::header::*;
@ -16,11 +13,9 @@ mod into_pair;
mod into_value;
mod utils;
mod common;
pub(crate) mod map;
mod shared;
pub use self::common::*;
#[doc(hidden)]
pub use self::shared::*;
@ -41,34 +36,6 @@ pub trait Header: IntoHeaderValue {
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`.
impl From<http::HeaderMap> for HeaderMap {
fn from(mut map: http::HeaderMap) -> HeaderMap {

View file

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

View file

@ -193,21 +193,69 @@ where
#[cfg(test)]
mod tests {
use super::super::encoding::*;
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]
fn test_quality_item_fmt_q_1() {
use Encoding::*;
let x = qitem(Chunked);
assert_eq!(format!("{}", x), "chunked");
}
#[test]
fn test_quality_item_fmt_q_0001() {
use Encoding::*;
let x = QualityItem::new(Chunked, Quality(1));
assert_eq!(format!("{}", x), "chunked; q=0.001");
}
#[test]
fn test_quality_item_fmt_q_05() {
use Encoding::*;
// Custom value
let x = QualityItem {
item: EncodingExt("identity".to_owned()),
@ -218,6 +266,7 @@ mod tests {
#[test]
fn test_quality_item_fmt_q_0() {
use Encoding::*;
// Custom value
let x = QualityItem {
item: EncodingExt("identity".to_owned()),
@ -228,6 +277,7 @@ mod tests {
#[test]
fn test_quality_item_from_str1() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "chunked".parse();
assert_eq!(
x.unwrap(),
@ -237,8 +287,10 @@ mod tests {
}
);
}
#[test]
fn test_quality_item_from_str2() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "chunked; q=1".parse();
assert_eq!(
x.unwrap(),
@ -248,8 +300,10 @@ mod tests {
}
);
}
#[test]
fn test_quality_item_from_str3() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.5".parse();
assert_eq!(
x.unwrap(),
@ -259,8 +313,10 @@ mod tests {
}
);
}
#[test]
fn test_quality_item_from_str4() {
use Encoding::*;
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.273".parse();
assert_eq!(
x.unwrap(),
@ -270,16 +326,19 @@ mod tests {
}
);
}
#[test]
fn test_quality_item_from_str5() {
let x: Result<QualityItem<Encoding>, _> = "gzip; q=0.2739999".parse();
assert!(x.is_err());
}
#[test]
fn test_quality_item_from_str6() {
let x: Result<QualityItem<Encoding>, _> = "gzip; q=2".parse();
assert!(x.is_err());
}
#[test]
fn test_quality_item_ordering() {
let x: QualityItem<Encoding> = "gzip; q=0.5".parse().ok().unwrap();

View file

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

View file

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

View file

@ -7,8 +7,10 @@
### Changed
* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
* 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
[#2094]: https://github.com/actix/actix-web/pull/2094
[#2114]: https://github.com/actix/actix-web/pull/2114
[#2116]: https://github.com/actix/actix-web/pull/2116

View file

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

View file

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

View file

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

View file

@ -2,10 +2,10 @@ use std::cmp::Ordering;
use mime::Mime;
use crate::header::{qitem, QualityItem};
use super::{qitem, QualityItem};
use crate::http::header;
header! {
crate::header! {
/// `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
@ -33,10 +33,10 @@ header! {
///
/// # Examples
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{Accept, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{Accept, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// Accept(vec![
/// qitem(mime::TEXT_HTML),
@ -45,10 +45,10 @@ header! {
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{Accept, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{Accept, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// Accept(vec![
/// qitem(mime::APPLICATION_JSON),
@ -57,10 +57,10 @@ header! {
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{Accept, QualityItem, q, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{Accept, QualityItem, q, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// Accept(vec![
/// qitem(mime::TEXT_HTML),
@ -116,8 +116,8 @@ header! {
#[test]
fn test_fuzzing1() {
use crate::test::TestRequest;
let req = TestRequest::default().insert_header((crate::header::ACCEPT, "chunk#;e")).finish();
use actix_http::test::TestRequest;
let req = TestRequest::default().insert_header((crate::http::header::ACCEPT, "chunk#;e")).finish();
let header = Accept::parse(&req);
assert!(header.is_ok());
}
@ -213,7 +213,7 @@ impl Accept {
#[cfg(test)]
mod tests {
use super::*;
use crate::header::q;
use crate::http::header::q;
#[test]
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
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3)
///
@ -22,20 +22,20 @@ header! {
///
/// # Examples
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{AcceptCharset, Charset, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptCharset, Charset, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// AcceptCharset(vec![qitem(Charset::Us_Ascii)])
/// );
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{AcceptCharset, Charset, q, QualityItem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptCharset, Charset, q, QualityItem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// AcceptCharset(vec![
/// QualityItem::new(Charset::Us_Ascii, q(900)),
@ -45,10 +45,10 @@ header! {
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{AcceptCharset, Charset, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptCharset, Charset, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// AcceptCharset(vec![qitem(Charset::Ext("utf-8".to_owned()))])
/// );

View file

@ -26,18 +26,20 @@ header! {
///
/// # 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();
/// headers.set(
/// let mut builder = HttpResponse::new();
/// builder.insert_header(
/// 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();
/// headers.set(
/// let mut builder = HttpResponse::new();
/// builder.insert_header(
/// AcceptEncoding(vec![
/// qitem(Encoding::Chunked),
/// 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();
/// headers.set(
/// let mut builder = HttpResponse::new();
/// builder.insert_header(
/// AcceptEncoding(vec![
/// qitem(Encoding::Chunked),
/// 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;
header! {
crate::header! {
/// `Accept-Language` header, defined in
/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5)
///
@ -24,10 +24,10 @@ header! {
///
/// ```
/// use language_tags::langtag;
/// use actix_http::Response;
/// use actix_http::http::header::{AcceptLanguage, LanguageTag, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptLanguage, LanguageTag, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// let mut langtag: LanguageTag = Default::default();
/// langtag.language = Some("en".to_owned());
/// langtag.region = Some("US".to_owned());
@ -40,10 +40,10 @@ header! {
///
/// ```
/// use language_tags::langtag;
/// use actix_http::Response;
/// use actix_http::http::header::{AcceptLanguage, QualityItem, q, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptLanguage, QualityItem, q, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// AcceptLanguage(vec![
/// qitem(langtag!(da)),

View file

@ -1,7 +1,7 @@
use http::header;
use http::Method;
use actix_http::http::Method;
use crate::http::header;
header! {
crate::header! {
/// `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
@ -23,20 +23,20 @@ header! {
/// # Examples
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::{header::Allow, Method};
/// use actix_web::HttpResponse;
/// use actix_web::http::{header::Allow, Method};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// Allow(vec![Method::GET])
/// );
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::{header::Allow, Method};
/// use actix_web::HttpResponse;
/// use actix_web::http::{header::Allow, Method};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// Allow(vec![
/// Method::GET,

View file

@ -1,12 +1,12 @@
use std::fmt::{self, Write};
use std::str::FromStr;
use http::header;
use crate::header::{
use super::{
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)
///
/// The `Cache-Control` header field is used to specify directives for
@ -29,18 +29,18 @@ use crate::header::{
///
/// # Examples
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{CacheControl, CacheDirective};
/// use actix_web::HttpResponse;
/// 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)]));
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{CacheControl, CacheDirective};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{CacheControl, CacheDirective};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(CacheControl(vec![
/// CacheDirective::NoCache,
/// CacheDirective::Private,
@ -191,8 +191,8 @@ impl FromStr for CacheDirective {
#[cfg(test)]
mod tests {
use super::*;
use crate::header::Header;
use crate::test::TestRequest;
use crate::http::header::Header;
use actix_http::test::TestRequest;
#[test]
fn test_parse_multiple_headers() {

View file

@ -10,7 +10,8 @@ use once_cell::sync::Lazy;
use regex::Regex;
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.
fn split_once(haystack: &str, needle: char) -> (&str, &str) {
@ -63,7 +64,7 @@ impl<'a> From<&'a str> for DispositionType {
///
/// # Examples
/// ```
/// use actix_http::http::header::DispositionParam;
/// use actix_web::http::header::DispositionParam;
///
/// let param = DispositionParam::Filename(String::from("sample.txt"));
/// assert!(param.is_filename());
@ -240,7 +241,7 @@ impl DispositionParam {
/// # Example
///
/// ```
/// use actix_http::http::header::{
/// use actix_web::http::header::{
/// Charset, ContentDisposition, DispositionParam, DispositionType,
/// ExtendedValue,
/// };
@ -554,8 +555,8 @@ impl fmt::Display for ContentDisposition {
#[cfg(test)]
mod tests {
use super::{ContentDisposition, DispositionParam, DispositionType};
use crate::header::shared::Charset;
use crate::header::{ExtendedValue, HeaderValue};
use crate::http::header::Charset;
use crate::http::header::{ExtendedValue, HeaderValue};
#[test]
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;
header! {
crate::header! {
/// `Content-Language` header, defined in
/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2)
///
@ -25,10 +25,10 @@ header! {
///
/// ```
/// use language_tags::langtag;
/// use actix_http::Response;
/// use actix_http::http::header::{ContentLanguage, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{ContentLanguage, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// ContentLanguage(vec![
/// qitem(langtag!(en)),
@ -38,10 +38,10 @@ header! {
///
/// ```
/// use language_tags::langtag;
/// use actix_http::Response;
/// use actix_http::http::header::{ContentLanguage, qitem};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{ContentLanguage, qitem};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// ContentLanguage(vec![
/// qitem(langtag!(da)),

View file

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

View file

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

View file

@ -1,7 +1,7 @@
use crate::header::{HttpDate, DATE};
use super::{HttpDate, DATE};
use std::time::SystemTime;
header! {
crate::header! {
/// `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
@ -21,10 +21,10 @@ header! {
///
/// ```
/// use std::time::SystemTime;
/// use actix_http::Response;
/// use actix_http::http::header::Date;
/// use actix_web::HttpResponse;
/// use actix_web::http::header::Date;
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// Date(SystemTime::now().into())
/// );

View file

@ -1,7 +1,7 @@
use std::fmt::{self, Display, Write};
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:
/// 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)
///
/// The `ETag` header field in a response provides the current entity-tag
@ -28,20 +28,20 @@ header! {
/// # Examples
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{ETag, EntityTag};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{ETag, EntityTag};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// ETag(EntityTag::new(false, "xyzzy".to_owned()))
/// );
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{ETag, EntityTag};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{ETag, EntityTag};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// 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)
///
/// The `Expires` header field gives the date/time after which the
@ -23,10 +23,10 @@ header! {
///
/// ```
/// use std::time::{SystemTime, Duration};
/// use actix_http::Response;
/// use actix_http::http::header::Expires;
/// use actix_web::HttpResponse;
/// 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);
/// builder.insert_header(
/// 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
/// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.1)
///
@ -30,18 +30,18 @@ header! {
/// # Examples
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::IfMatch;
/// use actix_web::HttpResponse;
/// use actix_web::http::header::IfMatch;
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(IfMatch::Any);
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{IfMatch, EntityTag};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{IfMatch, EntityTag};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// IfMatch::Items(vec![
/// 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
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.3)
///
@ -23,10 +23,10 @@ header! {
///
/// ```
/// use std::time::{SystemTime, Duration};
/// use actix_http::Response;
/// use actix_http::http::header::IfModifiedSince;
/// use actix_web::HttpResponse;
/// 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);
/// builder.insert_header(
/// 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
/// [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2)
///
@ -32,18 +32,18 @@ header! {
/// # Examples
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::IfNoneMatch;
/// use actix_web::HttpResponse;
/// use actix_web::http::header::IfNoneMatch;
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(IfNoneMatch::Any);
/// ```
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{IfNoneMatch, EntityTag};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{IfNoneMatch, EntityTag};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// IfNoneMatch::Items(vec![
/// EntityTag::new(false, "xyzzy".to_owned()),
@ -66,8 +66,8 @@ header! {
#[cfg(test)]
mod tests {
use super::IfNoneMatch;
use crate::header::{EntityTag, Header, IF_NONE_MATCH};
use crate::test::TestRequest;
use crate::http::header::{EntityTag, Header, IF_NONE_MATCH};
use actix_http::test::TestRequest;
#[test]
fn test_if_none_match() {

View file

@ -1,8 +1,9 @@
use std::fmt::{self, Display, Write};
use crate::http::header;
use crate::error::ParseError;
use crate::header::{
self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate,
use super::{
from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate,
IntoHeaderValue, InvalidHeaderValue, Writer,
};
use crate::HttpMessage;
@ -36,10 +37,10 @@ use crate::HttpMessage;
/// # Examples
///
/// ```
/// use actix_http::Response;
/// use actix_http::http::header::{EntityTag, IfRange};
/// use actix_web::HttpResponse;
/// use actix_web::http::header::{EntityTag, IfRange};
///
/// let mut builder = Response::Ok();
/// let mut builder = HttpResponse::Ok();
/// builder.insert_header(
/// IfRange::EntityTag(
/// EntityTag::new(false, "abc".to_owned())
@ -49,9 +50,9 @@ use crate::HttpMessage;
///
/// ```
/// 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);
/// builder.insert_header(
/// IfRange::Date(fetched.into())
@ -111,7 +112,7 @@ impl IntoHeaderValue for IfRange {
#[cfg(test)]
mod test_if_range {
use super::IfRange as HeaderField;
use crate::header::*;
use crate::http::header::*;
use std::str;
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
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-3.4)
///
@ -24,10 +24,10 @@ header! {
///
/// ```
/// use std::time::{SystemTime, Duration};
/// use actix_http::Response;
/// use actix_http::http::header::IfUnmodifiedSince;
/// use actix_web::HttpResponse;
/// 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);
/// builder.insert_header(
/// 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
/// [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2)
///
@ -23,10 +23,10 @@ header! {
///
/// ```
/// use std::time::{SystemTime, Duration};
/// use actix_http::Response;
/// use actix_http::http::header::LastModified;
/// use actix_web::HttpResponse;
/// 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);
/// builder.insert_header(
/// LastModified(modified.into())

View file

@ -7,6 +7,10 @@
//! is used, such as `ContentType(pub Mime)`.
#![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_encoding::AcceptEncoding;
pub use self::accept::Accept;
@ -18,7 +22,6 @@ pub use self::content_disposition::{
};
pub use self::content_language::ContentLanguage;
pub use self::content_range::{ContentRange, ContentRangeSpec};
pub use self::content_encoding::{ContentEncoding};
pub use self::content_type::ContentType;
pub use self::date::Date;
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_unmodified_since::IfUnmodifiedSince;
pub use self::last_modified::LastModified;
pub use self::encoding::Encoding;
pub use self::entity::EntityTag;
//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)]
#[macro_export]
@ -61,9 +96,9 @@ macro_rules! __hyper__tm {
#[cfg(test)]
mod $tm{
use std::str;
use http::Method;
use actix_http::http::Method;
use mime::*;
use $crate::header::*;
use $crate::http::header::*;
use super::$id as HeaderField;
$($tf)*
}
@ -77,8 +112,7 @@ macro_rules! test_header {
($id:ident, $raw:expr) => {
#[test]
fn $id() {
use super::*;
use $crate::test;
use actix_http::test;
let raw = $raw;
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) => {
#[test]
fn $id() {
use $crate::test;
use actix_http::test;
let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect();
let mut req = test::TestRequest::default();
@ -134,6 +168,7 @@ macro_rules! test_header {
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! header {
// $a:meta: Attributes associated with the header item (usually docs)
@ -341,7 +376,6 @@ mod allow;
mod cache_control;
mod content_disposition;
mod content_language;
mod content_encoding;
mod content_range;
mod content_type;
mod date;
@ -353,3 +387,5 @@ mod if_none_match;
mod if_range;
mod if_unmodified_since;
mod last_modified;
mod encoding;
mod entity;

View file

@ -1,8 +1,8 @@
use std::fmt::{self, Display};
use std::str::FromStr;
use header::parsing::from_one_raw_str;
use header::{Header, Raw};
use super::parsing::from_one_raw_str;
use super::{Header, Raw};
/// `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;
pub mod guard;
mod handler;
pub mod http;
mod info;
pub mod middleware;
mod request;
@ -102,7 +103,7 @@ pub mod web;
#[cfg(feature = "cookies")]
pub use actix_http::cookie;
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_web_codegen::*;

View file

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