Index: server/vhost.c
===================================================================
--- server/vhost.c	(revision 1663701)
+++ server/vhost.c	(working copy)
@@ -685,6 +685,40 @@ static int vhost_check_config(apr_pool_t *p, apr_p
  * run-time vhost matching functions
  */
 
+static char *construct_host_header(request_rec *r, int is_v6literal)
+{
+    struct iovec iov[5];
+    apr_size_t nvec = 0;
+
+    /*
+     * We cannot use ap_get_server_name/port here, because we must
+     * ignore UseCanonicalName/Port.
+     */
+    if (is_v6literal) {
+        iov[nvec].iov_base = "[";
+        iov[nvec].iov_len = 1;
+        nvec++;
+    }
+    iov[nvec].iov_base = (void *)r->hostname;
+    iov[nvec].iov_len = strlen(r->hostname);
+    nvec++;
+    if (is_v6literal) {
+        iov[nvec].iov_base = "]";
+        iov[nvec].iov_len = 1;
+        nvec++;
+    }
+    if (r->parsed_uri.port_str
+            && !ap_is_default_port(r->parsed_uri.port, r)) {
+        iov[nvec].iov_base = ":";
+        iov[nvec].iov_len = 1;
+        nvec++;
+        iov[nvec].iov_base = r->parsed_uri.port_str;
+        iov[nvec].iov_len = strlen(r->parsed_uri.port_str);
+        nvec++;
+    }
+    return apr_pstrcatv(r->pool, iov, nvec, NULL);
+}
+
 static apr_status_t fix_hostname_v6_literal(request_rec *r, char *host)
 {
     char *dst;
@@ -811,23 +845,47 @@ bad:
  * Instead we just check for filesystem metacharacters: directory
  * separators / and \ and sequences of more than one dot.
  */
-static int fix_hostname(request_rec *r, const char *host_header,
-                        unsigned http_conformance)
+static int fix_hostname(request_rec *r)
 {
     const char *src;
-    char *host, *scope_id;
+    char *host = NULL, *scope_id;
     apr_port_t port;
     apr_status_t rv;
     const char *c;
-    int is_v6literal = 0;
-    int strict = http_conformance & AP_HTTP_CONFORMANCE_STRICT;
-    int strict_logonly = http_conformance & AP_HTTP_CONFORMANCE_LOGONLY;
+    int is_v6literal;
+    core_server_config *conf =
+        ap_get_core_module_config(r->server->module_config);
+    int strict = conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT;
+    int strict_logonly = conf->http_conformance & AP_HTTP_CONFORMANCE_LOGONLY;
 
-    src = host_header ? host_header : r->hostname;
+    src = apr_table_get(r->headers_in, "Host");
 
+    /* RFC 7230 (5.4), one single Host header.
+     * Since r->headers is apr_table_compress()ed, multiple Host headers
+     * is now a single one with values separated by a comma.
+     */
+    if (src && ap_strchr_c(src, ',')) {
+        goto bad;
+    }
+    /* RFC 7230 (5.4) still, if we have both hostname from an absoluteURI
+     * and a Host header, we must ignore the Host header.
+     */
+    if (r->hostname) {
+        src = r->hostname;
+        if (ap_strchr_c(src, ',')) {
+            goto bad;
+        }
+    }
+
     /* According to RFC 2616, Host header field CAN be blank */
-    if (!*src) {
-        return is_v6literal;
+    if (!src || !*src) {
+        /* XXX: shouldn't r->hostname be empty too?
+        if (src && !r->hostname) {
+            r->hostname = src;
+            return 0;
+        }
+        */
+        goto good;
     }
 
     /* apr_parse_addr_port will interpret a bare integer as a port
@@ -844,11 +902,14 @@ bad:
             if (!strict_logonly)
                 goto bad_nolog;
         }
-        r->hostname = src;
-        return is_v6literal;
+        if (!r->hostname) {
+            r->hostname = src;
+            return 0;
+        }
+        goto good;
     }
 
-    if (host_header) {
+    if (!r->hostname) {
         rv = apr_parse_addr_port(&host, &scope_id, &port, src, r->pool);
         if (rv != APR_SUCCESS || scope_id)
             goto bad;
@@ -861,8 +922,7 @@ bad:
             r->parsed_uri.port = port;
             r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
         }
-        if (host_header[0] == '[')
-            is_v6literal = 1;
+        is_v6literal = (src[0] == '[');
     }
     else {
         /*
@@ -870,8 +930,7 @@ bad:
          * already been removed.
          */
         host = apr_pstrdup(r->pool, r->hostname);
-        if (ap_strchr(host, ':') != NULL)
-            is_v6literal = 1;
+        is_v6literal = (ap_strchr_c(host, ':') != NULL);
     }
 
     if (is_v6literal) {
@@ -885,7 +944,25 @@ bad:
     if (rv != APR_SUCCESS)
         goto bad;
 
-    r->hostname = host;
+good:
+    /* Enforce RFC 7230 (5.4) by rewriting the Host header to the value
+     * from the request line (if any).
+     */
+    if (r->hostname) {
+        char *dst = construct_host_header(r, is_v6literal);
+        if (!src || src == r->hostname || strcasecmp(src, dst) != 0) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02417)
+                          "Replacing Host header '%s' with host from "
+                          "request uri: '%s'", src ? src : "<none>", dst);
+            if (src) {
+                apr_table_setn(r->notes, "original-host", src);
+            }
+            apr_table_setn(r->headers_in, "Host", dst);
+        }
+    }
+    if (host) {
+        r->hostname = host;
+    }
     return is_v6literal;
 
 bad:
@@ -1106,80 +1183,12 @@ static void check_serverpath(request_rec *r)
     }
 }
 
-static APR_INLINE const char *construct_host_header(request_rec *r,
-                                                    int is_v6literal)
-{
-    struct iovec iov[5];
-    apr_size_t nvec = 0;
-    /*
-     * We cannot use ap_get_server_name/port here, because we must
-     * ignore UseCanonicalName/Port.
-     */
-    if (is_v6literal) {
-        iov[nvec].iov_base = "[";
-        iov[nvec].iov_len = 1;
-        nvec++;
-    }
-    iov[nvec].iov_base = (void *)r->hostname;
-    iov[nvec].iov_len = strlen(r->hostname);
-    nvec++;
-    if (is_v6literal) {
-        iov[nvec].iov_base = "]";
-        iov[nvec].iov_len = 1;
-        nvec++;
-    }
-    if (r->parsed_uri.port_str) {
-        iov[nvec].iov_base = ":";
-        iov[nvec].iov_len = 1;
-        nvec++;
-        iov[nvec].iov_base = r->parsed_uri.port_str;
-        iov[nvec].iov_len = strlen(r->parsed_uri.port_str);
-        nvec++;
-    }
-    return apr_pstrcatv(r->pool, iov, nvec, NULL);
-}
-
 AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
 {
-    core_server_config *conf = ap_get_core_module_config(r->server->module_config);
-    const char *host_header = apr_table_get(r->headers_in, "Host");
-    int is_v6literal = 0;
-    int have_hostname_from_url = 0;
-
-    if (r->hostname) {
-        /*
-         * If there was a host part in the Request-URI, ignore the 'Host'
-         * header.
-         */
-        have_hostname_from_url = 1;
-        is_v6literal = fix_hostname(r, NULL, conf->http_conformance);
-    }
-    else if (host_header != NULL) {
-        is_v6literal = fix_hostname(r, host_header, conf->http_conformance);
-    }
+    fix_hostname(r);
     if (r->status != HTTP_OK)
         return;
 
-    if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
-        /*
-         * If we have both hostname from an absoluteURI and a Host header,
-         * we must ignore the Host header (RFC 2616 5.2).
-         * To enforce this, we reset the Host header to the value from the
-         * request line.
-         */
-        if (have_hostname_from_url && host_header != NULL) {
-            const char *info = "Would replace";
-            const char *new = construct_host_header(r, is_v6literal);
-            if (!(conf->http_conformance & AP_HTTP_CONFORMANCE_LOGONLY)) {
-                apr_table_set(r->headers_in, "Host", r->hostname);
-                info = "Replacing";
-            }
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02417)
-                          "%s Host header '%s' with host from request uri: "
-                          "'%s'", info, host_header, new);
-        }
-    }
-
     /* check if we tucked away a name_chain */
     if (r->connection->vhost_lookup_data) {
         if (r->hostname)
