This is an automated email from the ASF dual-hosted git repository.

maskit 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 d76fe3bb90 header_rewrite: Add set-effective-address operator (#12521)
d76fe3bb90 is described below

commit d76fe3bb906956df644d563669fdeb213b4e0b4d
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Wed Sep 24 12:24:26 2025 -0600

    header_rewrite: Add set-effective-address operator (#12521)
    
    * header_rewrite: Add set-effective-address operator
    
    * Add documentation
    
    * Fix typo
    
    * Fix rest syntax
    
    * Apply the change for private_slot
    
    * Fix the debug message
    
    * Update factory.cc
    
    * Use get_arg instead of get_value
    
    * Add tests
    
    * Remove unnecessary set-header for debugging
    
    * Use the default ready condtions that micorserver has
---
 doc/admin-guide/plugins/header_rewrite.en.rst      | 13 +++++
 plugins/header_rewrite/factory.cc                  |  2 +
 plugins/header_rewrite/operators.cc                | 42 ++++++++++++++++
 plugins/header_rewrite/operators.h                 | 25 ++++++++++
 .../gold/header_rewrite_effective_address.gold     |  7 +++
 .../header_rewrite_effective_address.test.py       | 56 ++++++++++++++++++++++
 .../rules/rule_effective_address.conf              | 23 +++++++++
 7 files changed, 168 insertions(+)

diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst 
b/doc/admin-guide/plugins/header_rewrite.en.rst
index de2b1cbecf..1711337964 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -1241,6 +1241,19 @@ If ``PROXY`` is set, and PROXY protocol is used, the 
source IP address provided
 .. note::
     The conditions return an empty string if the source is set to ``PROXY`` 
but PROXY protocol header does not present.
 
+set-effective-address
+~~~~~~~~~~~~~~~~~~~~~
+::
+
+  set-effective-address <address>
+
+This operator allows you to set client's effective address for a transaction. 
The address will be used on other conditions and
+operators that use client's IP address.
+
+.. note::
+    This operator also changes `INBOUND_IP_SOURCE` to `PLUGIN` to make the 
address available for other conditions and operators.
+    See `set-plugin-cntl`_ for the detail.
+
 Operator Flags
 --------------
 
diff --git a/plugins/header_rewrite/factory.cc 
b/plugins/header_rewrite/factory.cc
index f06029d24e..3317b49b33 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -87,6 +87,8 @@ operator_factory(const std::string &op)
     o = new OperatorSetStateInt8();
   } else if (op == "set-state-int16") {
     o = new OperatorSetStateInt16();
+  } else if (op == "set-effective-address") {
+    o = new OperatorSetEffectiveAddress();
   } else {
     TSError("[%s] Unknown operator: %s", PLUGIN_NAME, op.c_str());
     return nullptr;
diff --git a/plugins/header_rewrite/operators.cc 
b/plugins/header_rewrite/operators.cc
index f6fff45540..cb7ac53c12 100644
--- a/plugins/header_rewrite/operators.cc
+++ b/plugins/header_rewrite/operators.cc
@@ -1585,3 +1585,45 @@ OperatorSetStateInt16::exec(const Resources &res) const
 
   return true;
 }
+
+void
+OperatorSetEffectiveAddress::initialize(Parser &p)
+{
+  Operator::initialize(p);
+
+  _value.set_value(p.get_arg(), this);
+}
+
+void
+OperatorSetEffectiveAddress::initialize_hooks()
+{
+  add_allowed_hook(TS_HTTP_READ_REQUEST_HDR_HOOK);
+  add_allowed_hook(TS_REMAP_PSEUDO_HOOK);
+}
+
+bool
+OperatorSetEffectiveAddress::exec(const Resources &res) const
+{
+  std::string value;
+  _value.append_value(value, res);
+
+  // Never set an empty address
+  if (value.empty()) {
+    Dbg(pi_dbg_ctl, "Address is empty, skipping");
+    return true;
+  }
+
+  auto                    addr = swoc::IPAddr(value);
+  struct sockaddr_storage storage;
+  addr.copy_to(reinterpret_cast<struct sockaddr *>(&storage));
+  TSHttpTxnVerifiedAddrSet(res.state.txnp, reinterpret_cast<struct sockaddr 
*>(&storage));
+
+  PrivateSlotData private_data;
+  private_data.raw       = 
reinterpret_cast<uint64_t>(TSUserArgGet(res.state.txnp, _txn_private_slot));
+  private_data.ip_source = IP_SRC_PLUGIN;
+
+  Dbg(pi_dbg_ctl, "   Setting plugin control INBOUND_IP_SOURCE to 
IP_SRC_PLUGIN");
+  TSUserArgSet(res.state.txnp, _txn_private_slot, reinterpret_cast<void 
*>(private_data.raw));
+
+  return true;
+}
diff --git a/plugins/header_rewrite/operators.h 
b/plugins/header_rewrite/operators.h
index b0943b7e19..4ff4f0edcb 100644
--- a/plugins/header_rewrite/operators.h
+++ b/plugins/header_rewrite/operators.h
@@ -626,3 +626,28 @@ protected:
 private:
   Value _value;
 };
+
+class OperatorSetEffectiveAddress : public Operator
+{
+public:
+  OperatorSetEffectiveAddress() { Dbg(dbg_ctl, "Calling CTOR for 
OperatorSetEffectiveAddress"); }
+
+  // noncopyable
+  OperatorSetEffectiveAddress(const OperatorSetEffectiveAddress &) = delete;
+  void operator=(const OperatorSetEffectiveAddress &)              = delete;
+
+  void initialize(Parser &p) override;
+
+protected:
+  void initialize_hooks() override;
+  bool exec(const Resources &res) const override;
+
+  bool
+  need_txn_private_slot() const override
+  {
+    return true;
+  }
+
+private:
+  Value _value;
+};
diff --git 
a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite_effective_address.gold
 
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite_effective_address.gold
new file mode 100644
index 0000000000..063298216b
--- /dev/null
+++ 
b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite_effective_address.gold
@@ -0,0 +1,7 @@
+``
+> Real-IP: 1.2.3.4
+``
+< HTTP/1.1 200 OK
+``
+< Effective-IP: 1.2.3.4
+``
diff --git 
a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_effective_address.test.py
 
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_effective_address.test.py
new file mode 100644
index 0000000000..47aa54002d
--- /dev/null
+++ 
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_effective_address.test.py
@@ -0,0 +1,56 @@
+'''
+'''
+#  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 set-effective-address operator.
+'''
+
+Test.ContinueOnFail = True
+# Define default ATS
+ts = Test.MakeATSProcess("ts")
+server = Test.MakeOriginServer("server")
+
+Test.testName = "header_rewrite_effective_address"
+request_get = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+# expected response from the origin server
+response = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+
+# add response to the server dictionary
+session_file = "sessionfile.log"
+server.addResponse(session_file, request_get, response)
+ts.Disk.records_config.update(
+    {
+        'proxy.config.diags.debug.enabled': 1,
+        'proxy.config.diags.debug.tags': 'header_rewrite|dbg_header_rewrite',
+    })
+# The following rule inserts a via header if the request method is a GET or 
DELETE
+conf_name = "rule_effective_address.conf"
+ts.Setup.CopyAs(f'rules/{conf_name}', Test.RunDirectory)
+ts.Disk.plugin_config.AddLine(f'header_rewrite.so 
{Test.RunDirectory}/{conf_name}')
+ts.Disk.remap_config.AddLine(f'map http://www.example.com 
http://127.0.0.1:{server.Variables.Port}')
+
+# Test that the IP address in Real-IP request header is returned in 
Effective-IP response header.
+expected_output = "gold/header_rewrite_effective_address.gold"
+tr = Test.AddTestRun()
+tr.MakeCurlCommand(
+    f'--http1.1 -H "Host: www.example.com" -H "Real-IP: 1.2.3.4" --verbose 
"http://127.0.0.1:{ts.Variables.port}";', ts=ts)
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.Streams.stderr = expected_output
+tr.StillRunningAfter = server
diff --git 
a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.conf 
b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.conf
new file mode 100644
index 0000000000..143a2c3b43
--- /dev/null
+++ 
b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+
+cond %{READ_REQUEST_HDR_HOOK}
+  set-effective-address %{HEADER:Real-IP}
+
+cond %{SEND_RESPONSE_HDR_HOOK}
+  set-header Effective-IP %{INBOUND:REMOTE-ADDR}
+

Reply via email to