From c284eae66692b9fbfe56263761f36fc44e03d260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Steen=20M=C3=B8ller?= Date: Wed, 10 Feb 2021 02:35:52 +0100 Subject: [PATCH 1/4] Fix #1067 by importing typed headers from hyperx --- .../ui-fail-stable/responder-types.stderr | 33 ++++--- core/http/Cargo.toml | 1 + core/http/src/hyper.rs | 99 ++++++++++++++++++- 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/core/codegen/tests/ui-fail-stable/responder-types.stderr b/core/codegen/tests/ui-fail-stable/responder-types.stderr index e728180758..614371f5f7 100644 --- a/core/codegen/tests/ui-fail-stable/responder-types.stderr +++ b/core/codegen/tests/ui-fail-stable/responder-types.stderr @@ -6,15 +6,18 @@ error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied | = note: required by `respond_to` -error[E0277]: the trait bound `Header<'_>: From` is not satisfied +error[E0277]: the trait bound `Header<'_>: std::convert::From` is not satisfied --> $DIR/responder-types.rs:11:5 | 11 | other: u8, - | ^^^^^ the trait `From` is not implemented for `Header<'_>` + | ^^^^^ the trait `std::convert::From` is not implemented for `Header<'_>` | = help: the following implementations were found: - as From<&Cookie<'_>>> - as From>> + as std::convert::From<&rocket::http::Cookie<'_>>> + as std::convert::From> + as std::convert::From> + as std::convert::From> + and 55 others = note: required because of the requirements on the impl of `Into>` for `u8` error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied @@ -25,26 +28,32 @@ error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied | = note: required by `respond_to` -error[E0277]: the trait bound `Header<'_>: From` is not satisfied +error[E0277]: the trait bound `Header<'_>: std::convert::From` is not satisfied --> $DIR/responder-types.rs:17:5 | 17 | other: u8, - | ^^^^^ the trait `From` is not implemented for `Header<'_>` + | ^^^^^ the trait `std::convert::From` is not implemented for `Header<'_>` | = help: the following implementations were found: - as From<&Cookie<'_>>> - as From>> + as std::convert::From<&rocket::http::Cookie<'_>>> + as std::convert::From> + as std::convert::From> + as std::convert::From> + and 55 others = note: required because of the requirements on the impl of `Into>` for `u8` -error[E0277]: the trait bound `Header<'_>: From` is not satisfied +error[E0277]: the trait bound `Header<'_>: std::convert::From` is not satisfied --> $DIR/responder-types.rs:24:5 | 24 | then: String, - | ^^^^ the trait `From` is not implemented for `Header<'_>` + | ^^^^ the trait `std::convert::From` is not implemented for `Header<'_>` | = help: the following implementations were found: - as From<&Cookie<'_>>> - as From>> + as std::convert::From<&rocket::http::Cookie<'_>>> + as std::convert::From> + as std::convert::From> + as std::convert::From> + and 55 others = note: required because of the requirements on the impl of `Into>` for `std::string::String` error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index fad87ea4c5..3a816336a6 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -23,6 +23,7 @@ private-cookies = ["cookie/private", "cookie/key-expansion"] smallvec = "1.0" percent-encoding = "2" hyper = { version = "0.14", default-features = false, features = ["http1", "http2", "runtime", "server", "stream"] } +hyperx = { version = "1.3.0" } http = "0.2" mime = "0.3.13" time = "0.2.11" diff --git a/core/http/src/hyper.rs b/core/http/src/hyper.rs index cb007e8179..2b6691b0ef 100644 --- a/core/http/src/hyper.rs +++ b/core/http/src/hyper.rs @@ -1,4 +1,4 @@ -//! Re-exported hyper HTTP library types. +//! Re-exported hyper HTTP library types and hyperx typed headers. //! //! All types that are re-exported from Hyper reside inside of this module. //! These types will, with certainty, be removed with time, but they reside here @@ -21,6 +21,9 @@ /// Reexported http header types. pub mod header { + use super::super::header::Header; + pub use hyperx::header::Header as HyperxHeaderTrait; + macro_rules! import_http_headers { ($($name:ident),*) => ($( pub use http::header::$name as $name; @@ -43,4 +46,98 @@ pub mod header { STRICT_TRANSPORT_SECURITY, TE, TRANSFER_ENCODING, UPGRADE, USER_AGENT, VARY } + + macro_rules! import_hyperx_items { + ($($item:ident),*) => ($(pub use hyperx::header::$item as $item;)*) + } + + macro_rules! import_hyperx_headers { + ($($name:ident),*) => ($( + impl ::std::convert::From for Header<'static> { + fn from(header: self::$name) -> Header<'static> { + Header::new($name::header_name(), header.to_string()) + } + } + )*) + } + + macro_rules! import_generic_hyperx_headers { + ($($name:ident<$bound:ident>),*) => ($( + impl ::std::convert::From> + for Header<'static> { + fn from(header: self::$name) -> Header<'static> { + Header::new($name::::header_name(), header.to_string()) + } + } + )*) + } + + import_hyperx_items! { + Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, AcceptRanges, + AccessControlAllowCredentials, AccessControlAllowHeaders, + AccessControlAllowMethods, AccessControlAllowOrigin, + AccessControlExposeHeaders, AccessControlMaxAge, + AccessControlRequestHeaders, AccessControlRequestMethod, Allow, + Authorization, Basic, Bearer, ByteRangeSpec, CacheControl, + CacheDirective, Charset, Connection, ConnectionOption, + ContentDisposition, ContentEncoding, ContentLanguage, ContentLength, + ContentLocation, ContentRange, ContentRangeSpec, ContentType, Cookie, + Date, DispositionParam, DispositionType, Encoding, EntityTag, ETag, + Expect, Expires, From, Host, HttpDate, IfMatch, IfModifiedSince, + IfNoneMatch, IfRange, IfUnmodifiedSince, LastEventId, LastModified, + Link, LinkValue, Location, Origin, Pragma, Prefer, Preference, + PreferenceApplied, Protocol, ProtocolName, ProxyAuthorization, Quality, + QualityItem, Range, RangeUnit, Referer, ReferrerPolicy, RetryAfter, + Scheme, Server, SetCookie, StrictTransportSecurity, + Te, TransferEncoding, Upgrade, UserAgent, Vary, Warning, q, qitem + } + + import_hyperx_headers! { + Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, AcceptRanges, + AccessControlAllowCredentials, AccessControlAllowHeaders, + AccessControlAllowMethods, AccessControlAllowOrigin, + AccessControlExposeHeaders, AccessControlMaxAge, + AccessControlRequestHeaders, AccessControlRequestMethod, Allow, + CacheControl, Connection, ContentDisposition, ContentEncoding, + ContentLanguage, ContentLength, ContentLocation, ContentRange, + ContentType, Cookie, Date, ETag, Expires, Expect, From, Host, IfMatch, + IfModifiedSince, IfNoneMatch, IfUnmodifiedSince, IfRange, LastEventId, + LastModified, Link, Location, Origin, Pragma, Prefer, PreferenceApplied, + Range, Referer, ReferrerPolicy, RetryAfter, Server, + StrictTransportSecurity, Te, TransferEncoding, Upgrade, UserAgent, Vary, + Warning + } + import_generic_hyperx_headers! { + Authorization, + ProxyAuthorization + } + // Note: SetCookie is missing, since it must be formatted as separate header lines... +} + +#[cfg(test)] +mod tests { + use crate::header::HeaderMap; + use super::header::HyperxHeaderTrait; // Needed for Accept::header_name() below?!?! + + #[test] + fn add_typed_header() { + use super::header::{Accept, QualityItem, q, qitem}; + let mut map = HeaderMap::new(); + map.add(Accept(vec![ + QualityItem::new("audio/*".parse().unwrap(), q(200)), + qitem("audio/basic".parse().unwrap()), + ])); + assert_eq!(map.get_one(Accept::header_name()), Some("audio/*; q=0.2, audio/basic")); + } + + #[test] + fn add_typed_header_with_type_params() { + use super::header::{Authorization, Basic}; + let mut map = HeaderMap::new(); + map.add(Authorization(Basic { + username: "admin".to_owned(), + password: Some("12345678".to_owned())})); + assert_eq!(map.get_one("Authorization"), Some("Basic YWRtaW46MTIzNDU2Nzg=")); + } + } From 9be50c7ef7316748b17d404ea4cb94935542e47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Steen=20M=C3=B8ller?= Date: Wed, 10 Feb 2021 02:54:38 +0100 Subject: [PATCH 2/4] Fix trailing whitespace --- core/http/src/hyper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/http/src/hyper.rs b/core/http/src/hyper.rs index 2b6691b0ef..5d80975f7a 100644 --- a/core/http/src/hyper.rs +++ b/core/http/src/hyper.rs @@ -118,7 +118,7 @@ pub mod header { mod tests { use crate::header::HeaderMap; use super::header::HyperxHeaderTrait; // Needed for Accept::header_name() below?!?! - + #[test] fn add_typed_header() { use super::header::{Accept, QualityItem, q, qitem}; From b708da448759dfd1efca0442084649614c14a29a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Steen=20M=C3=B8ller?= Date: Wed, 10 Feb 2021 08:55:11 +0100 Subject: [PATCH 3/4] Fix expected compilation errors in nightly --- .../ui-fail-nightly/responder-types.stderr | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/core/codegen/tests/ui-fail-nightly/responder-types.stderr b/core/codegen/tests/ui-fail-nightly/responder-types.stderr index 4d87185001..c6fa10ad63 100644 --- a/core/codegen/tests/ui-fail-nightly/responder-types.stderr +++ b/core/codegen/tests/ui-fail-nightly/responder-types.stderr @@ -6,15 +6,18 @@ error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied | = note: required by `respond_to` -error[E0277]: the trait bound `Header<'_>: From` is not satisfied +error[E0277]: the trait bound `Header<'_>: std::convert::From` is not satisfied --> $DIR/responder-types.rs:11:5 | 11 | other: u8, - | ^^^^^^^^^ the trait `From` is not implemented for `Header<'_>` + | ^^^^^^^^^ the trait `std::convert::From` is not implemented for `Header<'_>` | = help: the following implementations were found: - as From<&Cookie<'_>>> - as From>> + as std::convert::From<&rocket::http::Cookie<'_>>> + as std::convert::From> + as std::convert::From> + as std::convert::From> + and 55 others = note: required because of the requirements on the impl of `Into>` for `u8` error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied @@ -25,26 +28,32 @@ error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied | = note: required by `respond_to` -error[E0277]: the trait bound `Header<'_>: From` is not satisfied +error[E0277]: the trait bound `Header<'_>: std::convert::From` is not satisfied --> $DIR/responder-types.rs:17:5 | 17 | other: u8, - | ^^^^^^^^^ the trait `From` is not implemented for `Header<'_>` + | ^^^^^^^^^ the trait `std::convert::From` is not implemented for `Header<'_>` | = help: the following implementations were found: - as From<&Cookie<'_>>> - as From>> + as std::convert::From<&rocket::http::Cookie<'_>>> + as std::convert::From> + as std::convert::From> + as std::convert::From> + and 55 others = note: required because of the requirements on the impl of `Into>` for `u8` -error[E0277]: the trait bound `Header<'_>: From` is not satisfied +error[E0277]: the trait bound `Header<'_>: std::convert::From` is not satisfied --> $DIR/responder-types.rs:24:5 | 24 | then: String, - | ^^^^^^^^^^^^ the trait `From` is not implemented for `Header<'_>` + | ^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `Header<'_>` | = help: the following implementations were found: - as From<&Cookie<'_>>> - as From>> + as std::convert::From<&rocket::http::Cookie<'_>>> + as std::convert::From> + as std::convert::From> + as std::convert::From> + and 55 others = note: required because of the requirements on the impl of `Into>` for `std::string::String` error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied @@ -53,4 +62,4 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied 28 | fn foo() -> usize { 0 } | ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize` | - = note: required by `handler::, Status, rocket::Data>>::from` + = note: required by `handler::, Status, rocket::Data>>::from` From 5f834c11a3b742a3d24434e5f6f2f1d58d201a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Steen=20M=C3=B8ller?= Date: Wed, 10 Feb 2021 22:44:33 +0100 Subject: [PATCH 4/4] Add test case for responders with typed headers. --- core/lib/Cargo.toml | 1 + .../tests/respond-with-typed-headers-1067.rs | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 core/lib/tests/respond-with-typed-headers-1067.rs diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index c9efc33602..6ee0f4c6e1 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -56,6 +56,7 @@ version_check = "0.9.1" [dev-dependencies] bencher = "0.1" figment = { version = "0.10", features = ["test"] } +language-tags = { version=">=0.2, <0.3" } [[bench]] name = "format-routing" diff --git a/core/lib/tests/respond-with-typed-headers-1067.rs b/core/lib/tests/respond-with-typed-headers-1067.rs new file mode 100644 index 0000000000..6baa4ec822 --- /dev/null +++ b/core/lib/tests/respond-with-typed-headers-1067.rs @@ -0,0 +1,35 @@ +use rocket; + +use rocket::{get, routes}; +use rocket::response::Responder; +use rocket::http::hyper::header::{ContentLanguage, qitem}; +use language_tags::langtag; + +#[derive(Responder)] +struct DerivedResponder { + text: &'static str, + lang_header: ContentLanguage, +} + +#[get("/")] +fn index() -> DerivedResponder { + DerivedResponder { + text: "Fyr raketten af!", + lang_header: ContentLanguage(vec![ + qitem(langtag!(da)) + ]) + } +} + +#[test] +fn test_derive_reexports() { + use rocket::local::blocking::Client; + + let rocket = rocket::ignite().mount("/", routes![index]); + let client = Client::tracked(rocket).unwrap(); + + let response = client.get("/").dispatch(); + let content_languages = response.headers().get_one("content-language"); + assert_eq!(content_languages, Some("da")); + assert_eq!(response.into_string().unwrap(), "Fyr raketten af!"); +}