Open
Description
Hyper currently allows processing requests without and with multiple Host headers.
As per rfc7230 3.3.3:
A server MUST respond with a 400 (Bad Request) status code to any HTTP/1.1 request message that lacks a Host header field and to any request message that contains more than one Host header field or a Host header field with an invalid field-value
I'm guessing the spec deviation is intentional to support some legacy clients. Would you be open to adding a check_host_header
option?
diff --git a/src/error.rs b/src/error.rs
index 9ad4c0e5..229b6274 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -113,6 +113,10 @@ pub(super) enum Header {
ContentLengthInvalid,
#[cfg(feature = "server")]
TransferEncodingInvalid,
+ #[cfg(feature = "server")]
+ HostMissing,
+ #[cfg(feature = "server")]
+ HostDuplicate,
#[cfg(any(feature = "client", feature = "server"))]
TransferEncodingUnexpected,
}
@@ -435,6 +439,10 @@ impl Error {
Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => {
"invalid transfer-encoding parsed"
}
+ #[cfg(all(feature = "http1", feature = "server"))]
+ Kind::Parse(Parse::Header(Header::HostMissing)) => "missing host header",
+ #[cfg(all(feature = "http1", feature = "server"))]
+ Kind::Parse(Parse::Header(Header::HostDuplicate)) => "duplicate host header",
#[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
"unexpected transfer-encoding parsed"
@@ -557,6 +565,16 @@ impl Parse {
Parse::Header(Header::TransferEncodingInvalid)
}
+ #[cfg(feature = "server")]
+ pub(crate) fn host_missing() -> Self {
+ Parse::Header(Header::HostMissing)
+ }
+
+ #[cfg(feature = "server")]
+ pub(crate) fn host_duplicate() -> Self {
+ Parse::Header(Header::HostDuplicate)
+ }
+
#[cfg(any(feature = "client", feature = "server"))]
pub(crate) fn transfer_encoding_unexpected() -> Self {
Parse::Header(Header::TransferEncodingUnexpected)
diff --git a/src/proto/h1/role.rs b/src/proto/h1/role.rs
index 4f04acec..5c22518f 100644
--- a/src/proto/h1/role.rs
+++ b/src/proto/h1/role.rs
@@ -229,6 +229,7 @@ impl Http1Transaction for Server {
let mut con_len = None;
let mut is_te = false;
let mut is_te_chunked = false;
+ let mut has_host = false;
let mut wants_upgrade = subject.0 == Method::CONNECT;
let mut header_case_map = if ctx.preserve_header_case {
@@ -312,6 +313,13 @@ impl Http1Transaction for Server {
// Upgrades are only allowed with HTTP/1.1
wants_upgrade = is_http_11;
}
+ header::HOST => {
+ if has_host {
+ debug!("multiple Host headers");
+ return Err(Parse::host_duplicate());
+ }
+ has_host = true;
+ }
_ => (),
}
@@ -333,6 +341,11 @@ impl Http1Transaction for Server {
return Err(Parse::transfer_encoding_invalid());
}
+ if !has_host {
+ debug!("request without host header");
+ return Err(Parse::host_missing());
+ }
+
let mut extensions = http::Extensions::default();
if let Some(header_case_map) = header_case_map {
As prior art: Node.js changed it's behavior to follow spec strictly and added option to disable host header checks.