--- /dev/null
+varnishtest "H1 absolute-form request without Host header"
+
+# When an absolute-form request has no Host header, the authority of the URI
+# is the target. A Host header must be built from it so that host-based rules
+# apply to the actual target, like for H2/H3.
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen li1
+ bind "fd@${li1}"
+ http-request deny if { req.hdr(host) -m str admin.internal }
+ http-request return status 200 hdr x-host "%[req.hdr(host)]"
+} -start
+
+client c1 -connect ${h1_li1_sock} {
+ txreq -url "http://admin.internal/secret" -nohost
+ rxresp
+ expect resp.status == 403
+} -run
+
+client c2 -connect ${h1_li1_sock} {
+ txreq -url "http://u@public.example:8080/" -nohost
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-host == "public.example:8080"
+} -run
+
+client c3 -connect ${h1_li1_sock} {
+ txreq -url "/" -hdr "Host: public.example"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-host == "public.example"
+} -run
--- /dev/null
+varnishtest "H1 CONNECT requests without Host header"
+
+# A CONNECT request without Host header must get a Host header built from its
+# authority so that host-based rules apply to it.
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}"
+ http-request deny if { req.hdr(host) -i -m beg admin.internal }
+ http-request return status 200 hdr x-host "%[req.hdr(host)]"
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ txreq -req CONNECT -url "admin.internal:22" -nohost
+ rxresp
+ expect resp.status == 403
+} -run
+
+client c2 -connect ${h1_fe1_sock} {
+ txreq -req CONNECT -url "www.example.com:443" -nohost
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-host == "www.example.com"
+} -run
+
+client c6 -connect ${h1_fe1_sock} {
+ txreq -req CONNECT -url "www.example.com:8443" -nohost
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-host == "www.example.com:8443"
+} -run
if (!(h1m->flags & (H1_MF_HDRS_ONLY|H1_MF_RESP))) {
struct http_uri_parser parser = http_uri_parser_init(sl.rq.u);
struct ist scheme, authority = IST_NULL;
- int ret;
+ int ret = 0;
+ int bad_authority = 0;
/* WT: gcc seems to see a path where sl.rq.u.ptr was used
* uninitialized, but it doesn't know that the function is
case URI_PARSER_FORMAT_ABSURI_OR_AUTHORITY:
scheme = http_parse_scheme(&parser);
authority = http_parse_authority(&parser, 1);
- if (http_authority_has_forbidden_char(authority)) {
+ bad_authority = http_authority_has_forbidden_char(authority);
+ if (bad_authority) {
if (h1m->err_pos < -1) {
state = H1_MSG_LAST_LF;
/* WT: gcc seems to see a path where sl.rq.u.ptr was used
}
}
}
+
+ /* No Host header: the authority of the request target is
+ * the host (RFC9112#3.2.2 and 3.2.3). Add a Host header
+ * with its value A default port is removed.
+ */
+ if (!ret && host_idx == -1 && istlen(authority) && hdr && !skip_update &&
+ !bad_authority) {
+ struct ist new_host = authority;
+ struct ist port = http_get_host_port(authority);
+
+ if (unlikely(hdr_count >= hdr_num)) {
+ state = H1_MSG_LAST_LF;
+ goto http_output_full;
+ }
+ if (isttest(port) && http_is_default_port(scheme, port))
+ new_host.len -= istlen(port) + 1;
+ hdr[hdr_count] = hdr[hdr_count - 1];
+ http_set_hdr(&hdr[hdr_count - 1], ist("host"), new_host);
+ hdr_count++;
+ }
break;
default: