This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new ae4e19e994 header_rewrite: add POST_REMAP_HOOK support (#13426)
ae4e19e994 is described below
commit ae4e19e99416762c3887d483bb7866800995a521
Author: Juan Posadas <[email protected]>
AuthorDate: Tue Aug 4 09:30:03 2026 -0600
header_rewrite: add POST_REMAP_HOOK support (#13426)
header_rewrite can attach rulesets to most transaction hooks, but not to
TS_HTTP_POST_REMAP_HOOK. For a global (plugin.config) configuration, that
leaves no hook that sees the remapped request before the cache lookup:
READ_REQUEST_HDR_HOOK / READ_REQUEST_PRE_REMAP_HOOK run before remapping,
so they only ever see the pristine request.
REMAP_PSEUDO_HOOK sees the remapped request before the lookup, but is valid
only in a remap context.
SEND_REQUEST_HDR_HOOK sees the remapped request, but fires after the lookup
and only when the request is forwarded to an origin. It cannot influence the
lookup, and it never runs on a cache hit.
That window is where anything feeding the cache lookup has to run. The
cachekey
plugin registers TS_HTTP_POST_REMAP_HOOK for exactly this reason
(plugins/cachekey/plugin.cc:114): its remap-time path (TSRemapDoRemap)
covers the per-remap case, and a global instance needs the remapped request
before TSCacheUrlSet is consumed by the lookup.
This is the hook a planned header_rewrite cache-key operator needs, for the
same reason: setting the cache key is only meaningful between remapping and
the
cache lookup, and SEND_REQUEST_HDR_HOOK is already too late.
This PR adds a POST_REMAP_HOOK hook condition and wires it end to end:
Recognize the POST_REMAP_HOOK keyword in the parser.
Handle the POST_REMAP event so rulesets run at that hook.
Gather the post-remap request headers for the hook.
Allow operators and conditions on the hook.
Covered by a parser unit test and an end-to-end autest.
---
doc/admin-guide/plugins/header_rewrite.en.rst | 16 +++
plugins/header_rewrite/header_rewrite.cc | 3 +
plugins/header_rewrite/header_rewrite_test.cc | 13 +++
plugins/header_rewrite/parser.cc | 4 +
plugins/header_rewrite/resources.cc | 3 +-
plugins/header_rewrite/statement.cc | 1 +
.../header_rewrite_post_remap.replay.yaml | 123 +++++++++++++++++++++
.../header_rewrite_post_remap.test.py | 24 ++++
.../pluginTest/header_rewrite/post_remap.conf | 27 +++++
9 files changed, 213 insertions(+), 1 deletion(-)
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst
b/doc/admin-guide/plugins/header_rewrite.en.rst
index da44687320..cb3d53c341 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -1740,6 +1740,22 @@ files shared by both the global :file:`plugin.config`
and individual remapping
entries in :file:`remap.config`, this hook condition will force the subsequent
ruleset(s) to be valid only for remapped transactions.
+POST_REMAP_HOOK
+~~~~~~~~~~~~~~~
+
+Forces evaluation of the ruleset immediately after remapping has completed, but
+before |TS| looks the request up in the cache. There is no response data yet,
so
+context-adapting conditions and operators match against the request, which at
+this point is the remapped request.
+
+For rulesets in :file:`remap.config`, `REMAP_PSEUDO_HOOK`_ already covers this
+window. This hook exists for globally-configured rulesets, which otherwise have
+no hook that sees the remapped request before the cache lookup:
+`READ_REQUEST_HDR_HOOK`_ and `READ_REQUEST_PRE_REMAP_HOOK`_ run before
+remapping, and `SEND_REQUEST_HDR_HOOK`_ runs after the lookup, only when the
+request is forwarded to an origin. Anything that has to influence the lookup
+itself belongs at this hook.
+
SEND_REQUEST_HDR_HOOK
~~~~~~~~~~~~~~~~~~~~~
diff --git a/plugins/header_rewrite/header_rewrite.cc
b/plugins/header_rewrite/header_rewrite.cc
index 9a43a73813..b8c4f3c6db 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -494,6 +494,9 @@ cont_rewrite_headers(TSCont contp, TSEvent event, void
*edata)
case TS_EVENT_HTTP_READ_REQUEST_PRE_REMAP:
hook = TS_HTTP_PRE_REMAP_HOOK;
break;
+ case TS_EVENT_HTTP_POST_REMAP:
+ hook = TS_HTTP_POST_REMAP_HOOK;
+ break;
case TS_EVENT_HTTP_SEND_REQUEST_HDR:
hook = TS_HTTP_SEND_REQUEST_HDR_HOOK;
break;
diff --git a/plugins/header_rewrite/header_rewrite_test.cc
b/plugins/header_rewrite/header_rewrite_test.cc
index 68997debd0..cb166fd704 100644
--- a/plugins/header_rewrite/header_rewrite_test.cc
+++ b/plugins/header_rewrite/header_rewrite_test.cc
@@ -129,6 +129,19 @@ test_parsing()
END_TEST();
}
+ {
+ ParserTest p("cond %{POST_REMAP_HOOK}");
+ TSHttpHookID hook = TS_HTTP_LAST_HOOK;
+
+ CHECK_EQ(p.getTokens().size(), 2U);
+ CHECK_EQ(p.getTokens()[0], "cond");
+ CHECK_EQ(p.getTokens()[1], "%{POST_REMAP_HOOK}");
+ CHECK_EQ(p.cond_is_hook(hook), true);
+ CHECK_EQ(hook, TS_HTTP_POST_REMAP_HOOK);
+
+ END_TEST();
+ }
+
{
ParserTest p("cond %{CLIENT-HEADER:Host} =a");
diff --git a/plugins/header_rewrite/parser.cc b/plugins/header_rewrite/parser.cc
index 745f7d0882..c1f467d39a 100644
--- a/plugins/header_rewrite/parser.cc
+++ b/plugins/header_rewrite/parser.cc
@@ -296,6 +296,10 @@ Parser::cond_is_hook(TSHttpHookID &hook) const
hook = TS_REMAP_PSEUDO_HOOK;
return true;
}
+ if ("POST_REMAP_HOOK" == _op) {
+ hook = TS_HTTP_POST_REMAP_HOOK;
+ return true;
+ }
if ("TXN_START_HOOK" == _op) {
hook = TS_HTTP_TXN_START_HOOK;
return true;
diff --git a/plugins/header_rewrite/resources.cc
b/plugins/header_rewrite/resources.cc
index ed8d4ffe6a..5ccabde62f 100644
--- a/plugins/header_rewrite/resources.cc
+++ b/plugins/header_rewrite/resources.cc
@@ -87,7 +87,8 @@ Resources::gather(const ResourceIDs ids, TSHttpHookID hook)
case TS_HTTP_READ_REQUEST_HDR_HOOK:
case TS_HTTP_PRE_REMAP_HOOK:
- // Read request from client
+ case TS_HTTP_POST_REMAP_HOOK:
+ // Read request from client (post-remap this is the remapped request)
if (ids & RSRC_CLIENT_REQUEST_HEADERS) {
bufp = client_bufp;
hdr_loc = client_hdr_loc;
diff --git a/plugins/header_rewrite/statement.cc
b/plugins/header_rewrite/statement.cc
index 351f5d572e..0c699af09c 100644
--- a/plugins/header_rewrite/statement.cc
+++ b/plugins/header_rewrite/statement.cc
@@ -81,6 +81,7 @@ Statement::initialize_hooks()
add_allowed_hook(TS_HTTP_SEND_REQUEST_HDR_HOOK);
add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
add_allowed_hook(TS_REMAP_PSEUDO_HOOK);
+ add_allowed_hook(TS_HTTP_POST_REMAP_HOOK);
add_allowed_hook(TS_HTTP_TXN_START_HOOK);
add_allowed_hook(TS_HTTP_TXN_CLOSE_HOOK);
}
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_post_remap.replay.yaml
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_post_remap.replay.yaml
new file mode 100644
index 0000000000..d3cf64c259
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_post_remap.replay.yaml
@@ -0,0 +1,123 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+meta:
+ version: "1.0"
+
+autest:
+ description: 'Test header_rewrite POST_REMAP_HOOK support (global plugin)'
+
+ dns:
+ name: 'dns'
+
+ server:
+ name: 'server'
+
+ client:
+ name: 'client'
+
+ ats:
+ name: 'ts'
+
+ process_config:
+ enable_cache: true
+
+ copy_to_config_dir:
+ - 'post_remap.conf'
+
+ records_config:
+ proxy.config.diags.debug.enabled: 1
+ proxy.config.diags.debug.tags: 'header_rewrite'
+
+ # header_rewrite loaded as a GLOBAL plugin. The conf resolves relative to
+ # the ATS config dir, where copy_to_config_dir places it.
+ plugin_config:
+ - 'header_rewrite.so post_remap.conf'
+ - 'xdebug.so --enable=x-cache'
+
+ remap_config:
+ - from: "http://www.example.com/"
+ to: "http://backend.ex:{SERVER_HTTP_PORT}/"
+
+sessions:
+- transactions:
+
+ #############################################################################
+ # Cache miss: the header set at POST_REMAP reaches the origin, and the echo
+ # rule reports it on the response.
+ #############################################################################
+ - client-request:
+ method: "GET"
+ version: "1.1"
+ url: /post_remap/
+ headers:
+ fields:
+ - [ Host, www.example.com ]
+ - [ x-debug, "x-cache" ]
+ - [ uuid, post-remap-miss ]
+
+ proxy-request:
+ headers:
+ fields:
+ - [ X-Post-Remap-Host, { value: "backend.ex", as: equal } ]
+
+ server-response:
+ status: 200
+ reason: OK
+ headers:
+ fields:
+ - [ Content-Type, text/plain ]
+ - [ Content-Length, "3" ]
+ - [ Cache-Control, "max-age=300" ]
+ content:
+ encoding: plain
+ data: xxx
+
+ proxy-response:
+ status: 200
+ headers:
+ fields:
+ - [ X-Cache, { value: "miss", as: equal } ]
+ - [ X-Post-Remap-Echo, { value: "backend.ex", as: equal } ]
+
+ #############################################################################
+ # Cache hit: nothing is forwarded to the origin, so SEND_REQUEST_HDR_HOOK
+ # never runs. The rule still fires, because POST_REMAP is before the lookup.
+ #############################################################################
+ - client-request:
+ delay: 100ms
+ method: "GET"
+ version: "1.1"
+ url: /post_remap/
+ headers:
+ fields:
+ - [ Host, www.example.com ]
+ - [ x-debug, "x-cache" ]
+ - [ uuid, post-remap-hit ]
+
+ proxy-request:
+ expect: absent
+
+ server-response:
+ status: 404
+ reason: Not Found
+
+ proxy-response:
+ status: 200
+ headers:
+ fields:
+ - [ X-Cache, { value: "hit-fresh", as: equal } ]
+ - [ X-Post-Remap-Echo, { value: "backend.ex", as: equal } ]
diff --git
a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_post_remap.test.py
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_post_remap.test.py
new file mode 100644
index 0000000000..3ea824b4b1
--- /dev/null
+++
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_post_remap.test.py
@@ -0,0 +1,24 @@
+'''
+Test header_rewrite POST_REMAP_HOOK support.
+'''
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+Test.Summary = '''
+Test header_rewrite attaching a ruleset to the POST_REMAP_HOOK.
+'''
+
+Test.ATSReplayTest(replay_file="header_rewrite_post_remap.replay.yaml",)
diff --git a/tests/gold_tests/pluginTest/header_rewrite/post_remap.conf
b/tests/gold_tests/pluginTest/header_rewrite/post_remap.conf
new file mode 100644
index 0000000000..2788617372
--- /dev/null
+++ b/tests/gold_tests/pluginTest/header_rewrite/post_remap.conf
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Global header_rewrite ruleset that fires after remapping, on the remapped
+# request, before the cache lookup. The value is the remapped host, so an
+# earlier hook would record the pristine host instead.
+cond %{POST_REMAP_HOOK}
+ set-header X-Post-Remap-Host "%{URL:HOST}"
+
+# Echo the post-remap header into the client response so the rule above can be
+# observed on a cache hit, where no request is forwarded to the origin.
+cond %{SEND_RESPONSE_HDR_HOOK}
+ set-header X-Post-Remap-Echo "%{CLIENT-HEADER:X-Post-Remap-Host}"