>From 7ae9b567e18f9622fa6d58cd56b9066837c60481 Mon Sep 17 00:00:00 2001
From: Mani Goyal <[email protected]>
Date: Wed, 12 Aug 2026 12:26:22 +0530
Subject: [PATCH] BUG/MEDIUM: http: fix authority parsing for absolute-form URI
 with empty path

http_parse_authority() only stopped scanning at '/', not '?'. For an
absolute-form request-target with no path but a query string (e.g.
"http://host?token=...";), the authority scan ran to the end of the
URI and swallowed the query string into the authority. This caused
http_scheme_based_normalize() to see an empty path and append '/'
after the query string instead of between the host and the query,
corrupting the request on the wire.

Per RFC 3986, authority terminates at '/', '?', or the end of the
URI, so also stop at '?'.

Add two cases to h1_host_normalization.vtc covering an empty path
with a query string, with and without a port needing normalization.

This should be backported to all stable versions.

Should fix issue #3460.
---
 .../http-messaging/h1_host_normalization.vtc  | 38 +++++++++++++++++++
 src/http.c                                    |  4 +-
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/reg-tests/http-messaging/h1_host_normalization.vtc
b/reg-tests/http-messaging/h1_host_normalization.vtc
index 0d6112705..048ad95bd 100644
--- a/reg-tests/http-messaging/h1_host_normalization.vtc
+++ b/reg-tests/http-messaging/h1_host_normalization.vtc
@@ -231,6 +231,16 @@ syslog S1 -level info {
     # C43
     recv
     expect ~ "^.* uri: GET https://hostname:444/ HTTP/1.1; host:
{hostname:444}$"
+    barrier b1 sync
+
+    # C44
+    recv
+    expect ~ "^.* uri: GET http://hostname\\?a=b HTTP/1.1; host: {hostname}$"
+    barrier b1 sync
+
+    # C45
+    recv
+    expect ~ "^.* uri: GET http://hostname\\?c=d HTTP/1.1; host: {hostname}$"

 } -start

@@ -908,4 +918,32 @@ client c43 -connect ${h1_fe_sock} {
     expect resp.status == 200
 } -run

+# Wait matching on log message
+barrier b1 sync
+
+# empty path with a query string, no port => query must be preserved
+client c44 -connect ${h1_fe_sock} {
+    txreq \
+      -req "GET" \
+      -url "http://hostname?a=b"; \
+      -hdr "host: hostname"
+
+    rxresp
+    expect resp.status == 200
+} -run
+
+# Wait matching on log message
+barrier b1 sync
+
+# empty path with a query string and a default port => no stray '/' after query
+client c45 -connect ${h1_fe_sock} {
+    txreq \
+      -req "GET" \
+      -url "http://hostname:80?c=d"; \
+      -hdr "host: hostname:80"
+
+    rxresp
+    expect resp.status == 200
+} -run
+
 syslog S1 -wait
diff --git a/src/http.c b/src/http.c
index e7e78300d..66f14653f 100644
--- a/src/http.c
+++ b/src/http.c
@@ -682,12 +682,12 @@ struct ist http_parse_authority(struct
http_uri_parser *parser, int no_userinfo)
        ptr = start = istptr(parser->uri);
        end = istend(parser->uri);

-       while (ptr < end && *ptr != '/') {
+       while (ptr < end && *ptr != '/' && *ptr != '?') {
                if (*ptr++ == '@' && no_userinfo)
                        start = ptr;
        }

-       /* OK, ptr point on the '/' or the end */
+       /* OK, ptr point on the '/', the '?' or the end */

   authority:
        parser->uri = ist2(ptr, end - ptr);
-- 
2.34.1


Reply via email to