hyperium / hyperium/hyper

http1.1 server allows missing and duplicate Host header

Open
#3,777 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-http1 A-server B-rfc
Dominant language
Rust
Stars
16.3k
Forks
1.8k
Avg merge
1d 22h
Merged PRs (30d)
14

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read src/proto/h1/role.rs at the Server request-header processing entry point, then inspect the parse error definitions in src/error.rs. Compare the current missing and repeated Host handling with the linked RFC and clarify the proposed option semantics; done means the agreed behavior is enforced for HTTP/1.1 server requests.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, backend, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.