From d4a491074bcb0a3b52aea2e57c11a3aae5b414dc Mon Sep 17 00:00:00 2001 From: bneradt Date: Mon, 27 Jul 2026 13:14:14 -0500 Subject: [PATCH] Restore host in unmapped URL logs Intercepted requests that skip remapping preserve an unmapped URL before the Host header is promoted. The valid but incomplete snapshot prevents logging from using its fallback and produces URLs such as http:///_stats. This promotes the request target into the unmapped URL when the snapshot is created, regardless of whether remapping will run. It also adds stats_over_http coverage for hostless cquuc output. Fixes: #11378 --- src/proxy/http/HttpSM.cc | 22 ------------------- src/proxy/http/HttpTransact.cc | 3 ++- .../stats_over_http/stats_over_http.test.py | 19 +++++++++++++++- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc index 08ebd7b1019..93488b5b9b0 100644 --- a/src/proxy/http/HttpSM.cc +++ b/src/proxy/http/HttpSM.cc @@ -4598,28 +4598,6 @@ HttpSM::do_remap_request(bool run_inline) check_sni_host(); - // Depending on a variety of factors the HOST field may or may not have been promoted to the - // client request URL. The unmapped URL should always have that promotion done. If the HOST field - // is not already there, promote it only in the unmapped_url. This avoids breaking any logic that - // depends on the lack of promotion in the client request URL. - if (!t_state.unmapped_url.m_url_impl->m_ptr_host) { - MIMEField *host_field = t_state.hdr_info.client_request.field_find(static_cast(MIME_FIELD_HOST)); - if (host_field) { - auto host_name{host_field->value_get()}; - if (!host_name.empty()) { - int port = 0; - bool has_port = false; - - if (http_parse_host_header(host_name, host_name, port, has_port)) { - t_state.unmapped_url.host_set(host_name); - } - if (has_port) { - t_state.unmapped_url.port_set(port); - } - } - } - } - if (!ret) { SMDbg(dbg_ctl_url_rewrite, "Could not find a valid remapping entry for this request"); Metrics::Counter::increment(http_rsb.no_remap_matched); diff --git a/src/proxy/http/HttpTransact.cc b/src/proxy/http/HttpTransact.cc index cea93328fb0..5012009bd0f 100644 --- a/src/proxy/http/HttpTransact.cc +++ b/src/proxy/http/HttpTransact.cc @@ -1022,9 +1022,10 @@ HttpTransact::HandleBlindTunnel(State *s) void HttpTransact::StartRemapRequest(State *s) { - // Preserve effective url before remap, regardless of actual need for remap + // Preserve the effective URL before remap, regardless of the actual need for remap. s->unmapped_url.create(s->hdr_info.client_request.url_get()->m_heap); s->unmapped_url.copy(s->hdr_info.client_request.url_get()); + s->hdr_info.client_request.set_url_target_from_host_field(&s->unmapped_url); if (s->api_skip_all_remapping) { TxnDbg(dbg_ctl_http_trans, "API request to skip remapping"); diff --git a/tests/gold_tests/pluginTest/stats_over_http/stats_over_http.test.py b/tests/gold_tests/pluginTest/stats_over_http/stats_over_http.test.py index ab4b450373c..7106e3f673f 100644 --- a/tests/gold_tests/pluginTest/stats_over_http/stats_over_http.test.py +++ b/tests/gold_tests/pluginTest/stats_over_http/stats_over_http.test.py @@ -17,6 +17,7 @@ # limitations under the License. from enum import Enum +import os import sys Test.Summary = 'Exercise stats-over-http plugin' @@ -47,12 +48,28 @@ def __setupTS(self): self.ts = Test.MakeATSProcess("ts") self.ts.Disk.plugin_config.AddLine('stats_over_http.so _stats') + self.ts.Disk.logging_yaml.AddLines( + ''' +logging: + formats: + - name: unmapped_url + format: "%" + logs: + - filename: stats_over_http_url + format: unmapped_url +'''.split("\n")) + self.ts.Disk.File(os.path.join(self.ts.Variables.LOGDIR, 'stats_over_http_url.log'), id='unmapped_url_log') + self.ts.Disk.unmapped_url_log.Content += Testers.ContainsExpression( + rf'http://127\.0\.0\.1:{self.ts.Variables.port}/_stats', 'The unmapped URL should contain the Host header value.') + self.ts.Disk.unmapped_url_log.Content += Testers.ExcludesExpression( + 'http:///', 'The unmapped URL should not omit the request host.') self.ts.Disk.records_config.update( { "proxy.config.http.server_ports": f"{self.ts.Variables.port} {self.ts.Variables.uds_path}", "proxy.config.diags.debug.enabled": 1, - "proxy.config.diags.debug.tags": "stats_over_http" + "proxy.config.diags.debug.tags": "stats_over_http", + "proxy.config.log.max_secs_per_buffer": 1, }) def __checkProcessBefore(self, tr):