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 fbccde0ceb Hrw4U:   Multi cond fixes +  allow conds to have a default 
expression of !="" (#12346)
fbccde0ceb is described below

commit fbccde0ceb6679224336379ccd055fc601aa7da3
Author: Leif Hedstrom <[email protected]>
AuthorDate: Thu Jul 10 11:32:06 2025 -0500

    Hrw4U:   Multi cond fixes +  allow conds to have a default expression of 
!="" (#12346)
    
    * Fixes conditions with > 2 expressions
    
    * Enable some conds to have a default expression of !=""
---
 tools/hrw4u/src/symbols.py                        | 70 +++++++++++------------
 tools/hrw4u/src/visitor.py                        | 32 +++++++----
 tools/hrw4u/tests/data/conds/cookie.output.txt    |  2 +-
 tools/hrw4u/tests/data/conds/impl-expr.ast.txt    |  1 +
 tools/hrw4u/tests/data/conds/impl-expr.input.txt  |  5 ++
 tools/hrw4u/tests/data/conds/impl-expr.output.txt |  4 ++
 tools/hrw4u/tests/data/conds/long-if.ast.txt      |  1 +
 tools/hrw4u/tests/data/conds/long-if.input.txt    | 11 ++++
 tools/hrw4u/tests/data/conds/long-if.output.txt   | 15 +++++
 9 files changed, 93 insertions(+), 48 deletions(-)

diff --git a/tools/hrw4u/src/symbols.py b/tools/hrw4u/src/symbols.py
index bdcfeb7531..a44d50d486 100644
--- a/tools/hrw4u/src/symbols.py
+++ b/tools/hrw4u/src/symbols.py
@@ -74,39 +74,39 @@ class SymbolResolver:
         "txn-count": ("TXN-COUNT", Validator.arg_count(0)),
     }
 
-    _CONDITION_MAP: Dict[str, Tuple[str, Optional[Callable[[str], None]], 
bool, Optional[Set[str]]]] = {
+    _CONDITION_MAP: Dict[str, Tuple[str, Optional[Callable[[str], None]], 
bool, Optional[Set[str]], bool]] = {
         # Exact matches
-        "inbound.ip": ("%{IP:CLIENT}", None, False, None),
-        "inbound.method": ("%{METHOD}", None, False, None),
-        "inbound.server": ("%{IP:INBOUND}", None, False, None),
-        "inbound.status": ("%{STATUS}", None, False, None),
-        "now": ("%{NOW}", None, False, None),
-        "outbound.ip": ("%{IP:SERVER}", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST"}),
-        "outbound.method": ("%{METHOD}", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST"}),
-        "outbound.server": ("%{IP:OUTBOUND}", None, False, {"PRE_REMAP", 
"REMAP", "READ_REQUEST"}),
-        "outbound.status": ("%{STATUS}", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST"}),
-        "tcp.info": ("%{TCP-INFO}", None, False, None),
+        "inbound.ip": ("%{IP:CLIENT}", None, False, None, False),
+        "inbound.method": ("%{METHOD}", None, False, None, False),
+        "inbound.server": ("%{IP:INBOUND}", None, False, None, False),
+        "inbound.status": ("%{STATUS}", None, False, None, False),
+        "now": ("%{NOW}", None, False, None, False),
+        "outbound.ip": ("%{IP:SERVER}", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST"}, False),
+        "outbound.method": ("%{METHOD}", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST"}, False),
+        "outbound.server": ("%{IP:OUTBOUND}", None, False, {"PRE_REMAP", 
"REMAP", "READ_REQUEST"}, False),
+        "outbound.status": ("%{STATUS}", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST"}, False),
+        "tcp.info": ("%{TCP-INFO}", None, False, None, False),
 
         # Prefix matches
-        "capture.": ("LAST-CAPTURE", Validator.range(0, 9), False, None),
-        "client.cert.": ("CLIENT-CERT", None, True, None),
-        "from.url.": ("FROM-URL", 
Validator.suffix_group(types.SuffixGroup.URL_FIELDS), True, None),
-        "geo.": ("GEO", Validator.suffix_group(types.SuffixGroup.GEO_FIELDS), 
True, None),
-        "http.cntl.": ("HTTP-CNTL", 
Validator.suffix_group(types.SuffixGroup.HTTP_CNTL_FIELDS), True, None),
-        "id.": ("ID", Validator.suffix_group(types.SuffixGroup.ID_FIELDS), 
True, None),
-        "inbound.conn.": ("INBOUND", 
Validator.suffix_group(types.SuffixGroup.CONN_FIELDS), True, None),
-        "inbound.cookie.": ("COOKIE", Validator.quoted_or_simple(), False, 
None),
-        "inbound.req.": ("CLIENT-HEADER", None, False, None),
-        "inbound.resp.": ("HEADER", None, False, None),
-        "inbound.url.": ("CLIENT-URL", 
Validator.suffix_group(types.SuffixGroup.URL_FIELDS), True, None),
-        "now.": ("NOW", Validator.suffix_group(types.SuffixGroup.DATE_FIELDS), 
True, None),
-        "outbound.conn.": ("OUTBOUND", 
Validator.suffix_group(types.SuffixGroup.CONN_FIELDS), True, None),
-        "outbound.cookie.": ("COOKIE", Validator.quoted_or_simple(), False, 
None),
-        "outbound.req.": ("HEADER", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST"}),
-        "outbound.resp.": ("HEADER", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST", "SEND_REQUEST"}),
+        "capture.": ("LAST-CAPTURE", Validator.range(0, 9), False, None, True),
+        "client.cert.": ("CLIENT-CERT", None, True, None, True),
+        "from.url.": ("FROM-URL", 
Validator.suffix_group(types.SuffixGroup.URL_FIELDS), True, None, True),
+        "geo.": ("GEO", Validator.suffix_group(types.SuffixGroup.GEO_FIELDS), 
True, None, True),
+        "http.cntl.": ("HTTP-CNTL", 
Validator.suffix_group(types.SuffixGroup.HTTP_CNTL_FIELDS), True, None, False),
+        "id.": ("ID", Validator.suffix_group(types.SuffixGroup.ID_FIELDS), 
True, None, False),
+        "inbound.conn.": ("INBOUND", 
Validator.suffix_group(types.SuffixGroup.CONN_FIELDS), True, None, False),
+        "inbound.cookie.": ("COOKIE", Validator.quoted_or_simple(), False, 
None, True),
+        "inbound.req.": ("CLIENT-HEADER", None, False, None, True),
+        "inbound.resp.": ("HEADER", None, False, None, True),
+        "inbound.url.": ("CLIENT-URL", 
Validator.suffix_group(types.SuffixGroup.URL_FIELDS), True, None, True),
+        "now.": ("NOW", Validator.suffix_group(types.SuffixGroup.DATE_FIELDS), 
True, None, False),
+        "outbound.conn.": ("OUTBOUND", 
Validator.suffix_group(types.SuffixGroup.CONN_FIELDS), True, None, False),
+        "outbound.cookie.": ("COOKIE", Validator.quoted_or_simple(), False, 
None, True),
+        "outbound.req.": ("HEADER", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST"}, True),
+        "outbound.resp.": ("HEADER", None, False, {"PRE_REMAP", "REMAP", 
"READ_REQUEST", "SEND_REQUEST"}, True),
         "outbound.url.":
-            ("NEXT-HOP", Validator.suffix_group(types.SuffixGroup.URL_FIELDS), 
True, {"PRE_REMAP", "REMAP", "READ_REQUEST"}),
-        "to.url.": ("TO-URL", 
Validator.suffix_group(types.SuffixGroup.URL_FIELDS), True, None),
+            ("NEXT-HOP", Validator.suffix_group(types.SuffixGroup.URL_FIELDS), 
True, {"PRE_REMAP", "REMAP", "READ_REQUEST"}, True),
+        "to.url.": ("TO-URL", 
Validator.suffix_group(types.SuffixGroup.URL_FIELDS), True, None, True),
     }
 
     _SECTION_MAP = {
@@ -206,23 +206,23 @@ class SymbolResolver:
         self._debug_exit("=> not found")
         raise SymbolResolutionError(name, "Unknown assignment symbol")
 
-    def resolve_condition(self, name: str, section: Optional[str] = None) -> 
str:
+    def resolve_condition(self, name: str, section: Optional[str] = None) -> 
Tuple[str, bool]:
         self._debug_enter(f"resolve_condition: {name} (section={section})")
 
         symbol = self.symbol_for(name)
         if symbol:
             self._debug_exit(f"=> symbol_table: {symbol.as_cond()}")
-            return symbol.as_cond()
+            return symbol.as_cond(), False
 
         # Exact match
         if name in self._CONDITION_MAP:
-            tag, _, _, restricted = self._CONDITION_MAP[name]
+            tag, _, _, restricted, default_expr = self._CONDITION_MAP[name]
             self.check_section(name, section, restricted)
             self._debug_exit(f"=> exact condition_map: {tag}")
-            return tag
+            return tag, default_expr
 
         # Prefix match
-        for prefix, (tag, validator, uppercase, restricted) in 
self._CONDITION_MAP.items():
+        for prefix, (tag, validator, uppercase, restricted, default_expr) in 
self._CONDITION_MAP.items():
             if prefix.endswith(".") and name.startswith(prefix):
                 self.check_section(name, section, restricted)
                 suffix = name[len(prefix):]
@@ -231,7 +231,7 @@ class SymbolResolver:
                     validator(suf_norm)
                 resolved = f"%{{{tag}:{suf_norm}}}"
                 self._debug_exit(f"=> prefix condition_map: {resolved}")
-                return resolved
+                return resolved, default_expr
 
         self._debug_exit("=> not found")
         raise SymbolResolutionError(name, "Unknown condition symbol")
diff --git a/tools/hrw4u/src/visitor.py b/tools/hrw4u/src/visitor.py
index cf51c5ec4c..38f089a55f 100644
--- a/tools/hrw4u/src/visitor.py
+++ b/tools/hrw4u/src/visitor.py
@@ -114,7 +114,7 @@ class HRW4UVisitor(hrw4uVisitor):
                     self._debug(f"substitute: {{{func_name}({arg_str})}} -> 
{replacement}")
                 elif m.group("var"):
                     var_name = m.group("var").strip()
-                    replacement = 
self.symbol_resolver.resolve_condition(var_name, self.current_section)
+                    replacement, _ = 
self.symbol_resolver.resolve_condition(var_name, self.current_section)
                     self._debug(f"substitute: {{{var_name}}} -> {replacement}")
                 else:
                     raise SymbolResolutionError(m.group(0), "Unrecognized 
substitution format")
@@ -306,7 +306,7 @@ class HRW4UVisitor(hrw4uVisitor):
         comp = ctx.comparable()
         try:
             if comp.ident:
-                lhs = self.symbol_resolver.resolve_condition(comp.ident.text, 
self.current_section)
+                lhs, _ = 
self.symbol_resolver.resolve_condition(comp.ident.text, self.current_section)
             else:
                 lhs = self.visitFunctionCall(comp.functionCall())
             operator = ctx.getChild(1)
@@ -382,25 +382,22 @@ class HRW4UVisitor(hrw4uVisitor):
         self._flush_condition()
         self.output.append(" " * (self._stmt_indent * self.INDENT_SPACES) + 
line)
 
-    def emit_expression(self, ctx, *, nested: bool = False, last: bool = 
False):
+    def emit_expression(self, ctx, *, nested: bool = False, last: bool = 
False, grouped: bool = False):
         self._debug_enter("emit_expression")
         if ctx.OR():
             self._debug("`OR' detected")
-            if nested:
+            if grouped:
                 self._debug("GROUP-START")
                 self.emit_condition("cond %{GROUP}", final=True)
                 self._cond_indent += 1
 
-            self.emit_expression(ctx.expression(), nested=True, last=False)
-
+            self.emit_expression(ctx.expression(), nested=False, last=False)
             if self._queued:
-                self._queued.indent = self._cond_indent
                 self._queued.state.and_or = True
             self._flush_condition()
-
             self.emit_term(ctx.term(), last=last)
 
-            if nested:
+            if grouped:
                 self._flush_condition()
                 self._cond_indent -= 1
                 self.emit_condition("cond %{GROUP:END}")
@@ -434,7 +431,7 @@ class HRW4UVisitor(hrw4uVisitor):
                 self._debug("GROUP-START")
                 self.emit_condition("cond %{GROUP}", final=True)
                 self._cond_indent += 1
-                self.emit_expression(ctx.expression(), nested=False, last=True)
+                self.emit_expression(ctx.expression(), nested=False, 
last=True, grouped=True)
                 self._cond_indent -= 1
                 self._cond_state.last = last
                 self.emit_condition("cond %{GROUP:END}")
@@ -461,9 +458,20 @@ class HRW4UVisitor(hrw4uVisitor):
                 entry = self.symbol_resolver.symbol_for(name)
                 if entry:
                     symbol = entry.as_cond()
+                    default_expr = False
                 else:
-                    symbol = self.symbol_resolver.resolve_condition(name, 
self.current_section)
-                cond = self._make_condition(symbol, last=last)
+                    symbol, default_expr = 
self.symbol_resolver.resolve_condition(name, self.current_section)
+
+                if default_expr:
+                    cond_txt = f"{symbol} =\"\""
+                    negate = not self._cond_state.not_
+                else:
+                    cond_txt = symbol
+                    negate = self._cond_state.not_
+
+                self._cond_state.not_ = False
+                self._debug(f"{'implicit' if default_expr else 'explicit'} 
comparison: {cond_txt} negate={negate}")
+                cond = self._make_condition(cond_txt, last=last, negate=negate)
                 self.emit_condition(cond)
 
         except Exception as e:
diff --git a/tools/hrw4u/tests/data/conds/cookie.output.txt 
b/tools/hrw4u/tests/data/conds/cookie.output.txt
index f96ccc8faa..9a111d31a0 100644
--- a/tools/hrw4u/tests/data/conds/cookie.output.txt
+++ b/tools/hrw4u/tests/data/conds/cookie.output.txt
@@ -3,5 +3,5 @@ cond %{COOKIE:bar} /bar/
     set-cookie mybar "1"
 
 cond %{SEND_RESPONSE_HDR_HOOK} [AND]
-cond %{COOKIE:bar} [NOT]
+cond %{COOKIE:bar} =""
     set-cookie mybar "1"
diff --git a/tools/hrw4u/tests/data/conds/impl-expr.ast.txt 
b/tools/hrw4u/tests/data/conds/impl-expr.ast.txt
new file mode 100644
index 0000000000..bfc47f0e98
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/impl-expr.ast.txt
@@ -0,0 +1 @@
+(program (section REMAP { (sectionBody (conditional (ifStatement if (condition 
(expression (expression (term (factor inbound.req.X-Foo))) || (term (factor 
inbound.req.X-Bar)))) (block { (statement inbound.req.X-fie = (value "123") ;) 
})))) }) <EOF>)
diff --git a/tools/hrw4u/tests/data/conds/impl-expr.input.txt 
b/tools/hrw4u/tests/data/conds/impl-expr.input.txt
new file mode 100644
index 0000000000..ac95b57c49
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/impl-expr.input.txt
@@ -0,0 +1,5 @@
+REMAP {
+    if inbound.req.X-Foo || inbound.req.X-Bar {
+       inbound.req.X-fie = "123";
+    }
+}
diff --git a/tools/hrw4u/tests/data/conds/impl-expr.output.txt 
b/tools/hrw4u/tests/data/conds/impl-expr.output.txt
new file mode 100644
index 0000000000..6b73c0abef
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/impl-expr.output.txt
@@ -0,0 +1,4 @@
+cond %{REMAP_PSEUDO_HOOK} [AND]
+cond %{CLIENT-HEADER:X-Foo} ="" [OR,NOT]
+cond %{CLIENT-HEADER:X-Bar} ="" [NOT]
+    set-header X-fie "123"
diff --git a/tools/hrw4u/tests/data/conds/long-if.ast.txt 
b/tools/hrw4u/tests/data/conds/long-if.ast.txt
new file mode 100644
index 0000000000..4711c03ed9
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/long-if.ast.txt
@@ -0,0 +1 @@
+(program (section REMAP { (sectionBody (conditional (ifStatement if (condition 
(expression (expression (expression (term (factor (comparison (comparable 
inbound.req.xfoo) != (value ""))))) || (term (factor (comparison (comparable 
inbound.req.xBar) != (value ""))))) || (term (term (factor (comparison 
(comparable inbound.req.X-Foo) != (value "")))) && (factor (comparison 
(comparable inbound.req.X-Fie) == (value "")))))) (block { (statement 
inbound.req.X-fie = (value "123") ;) })))) }) (sec [...]
diff --git a/tools/hrw4u/tests/data/conds/long-if.input.txt 
b/tools/hrw4u/tests/data/conds/long-if.input.txt
new file mode 100644
index 0000000000..8dc1dc9246
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/long-if.input.txt
@@ -0,0 +1,11 @@
+REMAP {
+    if inbound.req.xfoo != "" || inbound.req.xBar != "" || inbound.req.X-Foo 
!= "" && inbound.req.X-Fie == "" {
+       inbound.req.X-fie = "123";
+    }
+}
+
+REMAP {
+    if inbound.req.xfoo != "" || inbound.req.xBar != "" || (inbound.req.X-Foo 
!= "" && inbound.req.X-Fie == "") {
+       inbound.req.X-fie = "123";
+    }
+}
diff --git a/tools/hrw4u/tests/data/conds/long-if.output.txt 
b/tools/hrw4u/tests/data/conds/long-if.output.txt
new file mode 100644
index 0000000000..ed2051a5ec
--- /dev/null
+++ b/tools/hrw4u/tests/data/conds/long-if.output.txt
@@ -0,0 +1,15 @@
+cond %{REMAP_PSEUDO_HOOK} [AND]
+cond %{CLIENT-HEADER:xfoo} ="" [OR,NOT]
+cond %{CLIENT-HEADER:xBar} ="" [OR,NOT]
+cond %{CLIENT-HEADER:X-Foo} ="" [AND,NOT]
+cond %{CLIENT-HEADER:X-Fie} =""
+    set-header X-fie "123"
+
+cond %{REMAP_PSEUDO_HOOK} [AND]
+cond %{CLIENT-HEADER:xfoo} ="" [OR,NOT]
+cond %{CLIENT-HEADER:xBar} ="" [OR,NOT]
+cond %{GROUP}
+    cond %{CLIENT-HEADER:X-Foo} ="" [AND,NOT]
+    cond %{CLIENT-HEADER:X-Fie} =""
+cond %{GROUP:END}
+    set-header X-fie "123"

Reply via email to