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

zwoop 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 29b0e93062 hrw: fix lost [OR] modifiers, add more autests (#12904)
29b0e93062 is described below

commit 29b0e93062cf7a45ad6a55958c8551a9cc95a39b
Author: Leif Hedstrom <[email protected]>
AuthorDate: Sat Feb 21 00:54:14 2026 -0700

    hrw: fix lost [OR] modifiers, add more autests (#12904)
---
 plugins/header_rewrite/condition.h                 |   6 +
 plugins/header_rewrite/header_rewrite.cc           |   5 +-
 .../header_rewrite_bundle.replay.yaml              | 669 +++++++++++++++++++++
 .../header_rewrite/rules/complex_logics.conf       | 158 +++++
 4 files changed, 836 insertions(+), 2 deletions(-)

diff --git a/plugins/header_rewrite/condition.h 
b/plugins/header_rewrite/condition.h
index 1404a747b6..1400f3433a 100644
--- a/plugins/header_rewrite/condition.h
+++ b/plugins/header_rewrite/condition.h
@@ -85,6 +85,12 @@ public:
     return _mods;
   }
 
+  void
+  set_mods(CondModifiers mods)
+  {
+    _mods = mods;
+  }
+
   // Setters
   virtual void
   set_qualifier(const std::string &q)
diff --git a/plugins/header_rewrite/header_rewrite.cc 
b/plugins/header_rewrite/header_rewrite.cc
index 9378f6022a..09ab73a74c 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -329,8 +329,9 @@ RulesConfig::parse_config(const std::string &fname, 
TSHttpHookID default_hook, c
               if (group_stack.empty()) {
                 throw std::runtime_error("unmatched %{GROUP}");
               } else {
-                delete cond; // We don't care about the closing group 
condition, it's a no-op
-                ngrp  = group;
+                ngrp = group;
+                ngrp->set_mods(cond->mods());
+                delete cond;
                 group = group_stack.top();
                 group_stack.pop();
                 group->add_condition(ngrp); // Add the previous group to the 
current group's conditions
diff --git 
a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml 
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml
index ea1c966185..8e6e9a995c 100644
--- 
a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml
+++ 
b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml
@@ -155,6 +155,13 @@ autest:
             args:
               - "rules/run_plugin.conf"
 
+      - from: "http://www.example.com/from_15/";
+        to: "http://backend.ex:{SERVER_HTTP_PORT}/to_15/";
+        plugins:
+          - name: "header_rewrite.so"
+            args:
+              - "rules/complex_logics.conf"
+
     log_validation:
       traffic_out:
         excludes:
@@ -1114,3 +1121,665 @@ sessions:
 
     proxy-response:
       status: 200
+
+# ==========================================================================
+# Tests 31-52: Complex GROUP logic tests (rules/complex_logics.conf)
+# ==========================================================================
+
+# Test 31: GROUP [OR] - only A header present (should match via group)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-or
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Group-A, "yes" ]
+        - [ uuid, 37 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-Or-Result, { value: "matched", as: equal } ]
+
+# Test 32: GROUP [OR] - only B header present (should match via OR)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-or
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Group-B, "yes" ]
+        - [ uuid, 38 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-Or-Result, { value: "matched", as: equal } ]
+
+# Test 33: GROUP [OR] - neither header present (should not match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-or
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ uuid, 39 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-Or-Result, { as: absent } ]
+
+# Test 34: GROUP [AND] - both headers present (should match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-and
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-And-A, "yes" ]
+        - [ X-And-B, "yes" ]
+        - [ uuid, 40 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-And-Result, { value: "matched", as: equal } ]
+
+# Test 35: GROUP [AND] - only A present (should not match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-and
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-And-A, "yes" ]
+        - [ uuid, 41 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-And-Result, { as: absent } ]
+
+# Test 36: GROUP [NOT] - neither header present -> group=(false) -> 
NOT(false)=true
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-not
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ uuid, 42 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-Not-Result, { value: "matched", as: equal } ]
+
+# Test 37: GROUP [NOT] - both headers present -> group=(true) -> 
NOT(true)=false
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-not
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Not-A, "yes" ]
+        - [ X-Not-B, "yes" ]
+        - [ uuid, 43 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-Not-Result, { as: absent } ]
+
+# Test 38: Nested GROUP - outer + inner-A present (should match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/nested-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Outer-A, "yes" ]
+        - [ X-Inner-A, "yes" ]
+        - [ uuid, 44 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Nested-Group-Result, { value: "matched", as: equal } ]
+
+# Test 39: Nested GROUP - outer + inner-B present (should match via inner OR)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/nested-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Outer-A, "yes" ]
+        - [ X-Inner-B, "yes" ]
+        - [ uuid, 45 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Nested-Group-Result, { value: "matched", as: equal } ]
+
+# Test 40: Nested GROUP - no outer header (should not match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/nested-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Inner-A, "yes" ]
+        - [ uuid, 46 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Nested-Group-Result, { as: absent } ]
+
+# Test 41: Two GROUPs with OR - left group matches
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/two-groups
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Left-A, "yes" ]
+        - [ X-Left-B, "yes" ]
+        - [ uuid, 47 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Two-Groups-Result, { value: "matched", as: equal } ]
+
+# Test 42: Two GROUPs with OR - right group matches
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/two-groups
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Right-A, "yes" ]
+        - [ X-Right-B, "yes" ]
+        - [ uuid, 48 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Two-Groups-Result, { value: "matched", as: equal } ]
+
+# Test 43: Two GROUPs with OR - neither group matches (partial left)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/two-groups
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Left-A, "yes" ]
+        - [ X-Right-B, "yes" ]
+        - [ uuid, 49 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Two-Groups-Result, { as: absent } ]
+
+# Test 44: GROUP inside if - alpha branch with X-Sub-A (should match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/if-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Branch, "alpha" ]
+        - [ X-Sub-A, "yes" ]
+        - [ uuid, 50 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-If-Group-Result, { value: "alpha", as: equal } ]
+
+# Test 45: GROUP inside if - alpha branch without sub headers (falls to else)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/if-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Branch, "alpha" ]
+        - [ uuid, 51 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-If-Group-Result, { value: "other", as: equal } ]
+
+# Test 46: GROUP inside if - beta branch
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/if-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Branch, "beta" ]
+        - [ uuid, 52 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-If-Group-Result, { value: "beta", as: equal } ]
+
+# Test 47: Nested if with GROUP OR - outer yes, case-A present
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/nested-if-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Outer, "yes" ]
+        - [ X-Case-A, "yes" ]
+        - [ uuid, 53 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Nested-If-Result, { value: "inner-match", as: equal } ]
+
+# Test 48: Nested if with GROUP OR - outer yes, neither case header
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/nested-if-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Outer, "yes" ]
+        - [ uuid, 54 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Nested-If-Result, { value: "inner-miss", as: equal } ]
+
+# Test 49: Nested if with GROUP OR - outer miss
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/nested-if-group
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Outer, "no" ]
+        - [ uuid, 55 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Nested-If-Result, { value: "outer-miss", as: equal } ]
+
+# Test 50: Complex (A OR B) AND (C OR D) - P+R present (should match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/complex-and-or
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-P, "yes" ]
+        - [ X-R, "yes" ]
+        - [ uuid, 56 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Complex-Result, { value: "matched", as: equal } ]
+
+# Test 51: Complex (A OR B) AND (C OR D) - Q+S present (should match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/complex-and-or
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Q, "yes" ]
+        - [ X-S, "yes" ]
+        - [ uuid, 57 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Complex-Result, { value: "matched", as: equal } ]
+
+# Test 52: Complex (A OR B) AND (C OR D) - only P present (first group
+# matches but second doesn't, should not match)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/complex-and-or
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-P, "yes" ]
+        - [ uuid, 58 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Complex-Result, { as: absent } ]
+
+# Test 53: IP:CLIENT in GROUP with [OR] - IP matches (client is 127.0.0.1)
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/ip-group-or
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ uuid, 59 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Ip-Group-Or-Result, { value: "matched", as: equal } ]
+
+# Test 54: IP:CLIENT in GROUP with [OR] - header X-Outer matches too
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/ip-group-or
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-Outer, "true" ]
+        - [ uuid, 60 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Ip-Group-Or-Result, { value: "matched", as: equal } ]
+
+# Test 55: GROUP as first condition after hook - X-First-A present, should 
match
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-first
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ X-First-A, "yes" ]
+        - [ uuid, 61 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-First-Result, { value: "matched", as: equal } ]
+
+# Test 56: GROUP as first condition after hook - no X-First-A, should not match
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /from_15/logic/group-first
+      headers:
+        fields:
+        - [ Host, www.example.com ]
+        - [ uuid, 62 ]
+
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Connection, close ]
+
+    proxy-response:
+      status: 200
+      headers:
+        fields:
+        - [ X-Group-First-Result, { as: absent } ]
diff --git 
a/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf 
b/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf
new file mode 100644
index 0000000000..b4f4b92518
--- /dev/null
+++ b/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf
@@ -0,0 +1,158 @@
+#
+# 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.
+
+# All rules use SEND_RESPONSE_HDR_HOOK so set-header operates on the
+# response that the client sees.  CLIENT-HEADER and CLIENT-URL:PATH
+# conditions always refer to the original client request regardless of hook.
+
+# ---- Test: GROUP with [OR] on GROUP:END ----
+# hrw4u: if (inbound.req.X-Group-A) || inbound.req.X-Group-B
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/group-or$/ [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Group-A} ="" [NOT]
+cond %{GROUP:END} [OR]
+cond %{CLIENT-HEADER:X-Group-B} ="" [NOT]
+    set-header X-Group-Or-Result "matched"
+
+# ---- Test: GROUP with [AND] on GROUP:END (explicit) ----
+# hrw4u: if (inbound.req.X-And-A) && inbound.req.X-And-B
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/group-and$/ [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-And-A} ="" [NOT]
+cond %{GROUP:END} [AND]
+cond %{CLIENT-HEADER:X-And-B} ="" [NOT]
+    set-header X-Group-And-Result "matched"
+
+# ---- Test: GROUP with [NOT] on GROUP:END ----
+# hrw4u: if !(inbound.req.X-Not-A && inbound.req.X-Not-B)
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/group-not$/ [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Not-A} ="" [NOT]
+    cond %{CLIENT-HEADER:X-Not-B} ="" [NOT]
+cond %{GROUP:END} [NOT]
+    set-header X-Group-Not-Result "matched"
+
+# ---- Test: Nested GROUPs with mixed OR/AND ----
+# hrw4u: if inbound.req.X-Outer-A && (inbound.req.X-Inner-A || 
inbound.req.X-Inner-B)
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/nested-group$/ [AND]
+cond %{CLIENT-HEADER:X-Outer-A} ="" [NOT]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Inner-A} ="" [NOT,OR]
+    cond %{CLIENT-HEADER:X-Inner-B} ="" [NOT]
+cond %{GROUP:END}
+    set-header X-Nested-Group-Result "matched"
+
+# ---- Test: Two GROUPs with OR between them ----
+# hrw4u: if (inbound.req.X-Left-A && inbound.req.X-Left-B) || 
(inbound.req.X-Right-A && inbound.req.X-Right-B)
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/two-groups$/ [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Left-A} ="" [NOT]
+    cond %{CLIENT-HEADER:X-Left-B} ="" [NOT]
+cond %{GROUP:END} [OR]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Right-A} ="" [NOT]
+    cond %{CLIENT-HEADER:X-Right-B} ="" [NOT]
+cond %{GROUP:END}
+    set-header X-Two-Groups-Result "matched"
+
+# ---- Test: GROUP inside if/elif/else ----
+# hrw4u:
+#   if inbound.req.X-Branch == "alpha" && (inbound.req.X-Sub-A || 
inbound.req.X-Sub-B)
+#     -> "alpha"
+#   elif inbound.req.X-Branch == "beta"
+#     -> "beta"
+#   else
+#     -> "other"
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/if-group$/ [AND]
+  if
+    cond %{CLIENT-HEADER:X-Branch} ="alpha"
+    cond %{GROUP}
+        cond %{CLIENT-HEADER:X-Sub-A} ="" [NOT,OR]
+        cond %{CLIENT-HEADER:X-Sub-B} ="" [NOT]
+    cond %{GROUP:END}
+      set-header X-If-Group-Result "alpha"
+  elif
+    cond %{CLIENT-HEADER:X-Branch} ="beta"
+      set-header X-If-Group-Result "beta"
+  else
+    set-header X-If-Group-Result "other"
+  endif
+
+# ---- Test: GROUP with OR inside nested if ----
+# hrw4u:
+#   if inbound.req.X-Outer == "yes"
+#     if (inbound.req.X-Case-A || inbound.req.X-Case-B)
+#       -> "inner-match"
+#     else
+#       -> "inner-miss"
+#   else
+#     -> "outer-miss"
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/nested-if-group$/ [AND]
+  if
+    cond %{CLIENT-HEADER:X-Outer} ="yes"
+      if
+        cond %{GROUP}
+            cond %{CLIENT-HEADER:X-Case-A} ="" [NOT,OR]
+            cond %{CLIENT-HEADER:X-Case-B} ="" [NOT]
+        cond %{GROUP:END}
+          set-header X-Nested-If-Result "inner-match"
+      else
+        set-header X-Nested-If-Result "inner-miss"
+      endif
+  else
+    set-header X-Nested-If-Result "outer-miss"
+  endif
+
+# ---- Test: Complex expression: (A OR B) AND (C OR D) ----
+# hrw4u: if (inbound.req.X-P || inbound.req.X-Q) && (inbound.req.X-R || 
inbound.req.X-S)
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/complex-and-or$/ [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-P} ="" [NOT,OR]
+    cond %{CLIENT-HEADER:X-Q} ="" [NOT]
+cond %{GROUP:END} [AND]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-R} ="" [NOT,OR]
+    cond %{CLIENT-HEADER:X-S} ="" [NOT]
+cond %{GROUP:END}
+    set-header X-Complex-Result "matched"
+
+# ---- Test: IP:CLIENT in GROUP with [OR]
+# hrw4u: if (inbound.ip in {127.0.0.0/8}) || inbound.req.X-Outer == "true"
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{CLIENT-URL:PATH} /\/logic\/ip-group-or$/ [AND]
+cond %{GROUP}
+    cond %{IP:CLIENT} {127.0.0.0/8}
+cond %{GROUP:END} [OR]
+cond %{CLIENT-HEADER:X-Outer} ="true"
+    set-header X-Ip-Group-Or-Result "matched"
+
+# ---- Test: GROUP as first condition after hook ----
+# hrw4u: if (inbound.url.path ~ /\/logic\/group-first$/) && 
inbound.req.X-First-A
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %{GROUP}
+    cond %{CLIENT-URL:PATH} /\/logic\/group-first$/
+cond %{GROUP:END} [AND]
+cond %{CLIENT-HEADER:X-First-A} ="" [NOT]
+    set-header X-Group-First-Result "matched"

Reply via email to