From 96a24cf07c99673b270c0b0a86fe39deb18e2a52 Mon Sep 17 00:00:00 2001 From: Leif Hedstrom Date: Mon, 9 Mar 2026 23:04:36 -0700 Subject: [PATCH] hrw4u: Fix section placement for hookless rules in u4wrh Old header_rewrite rules without an explicit hook condition default to REMAP. When u4wrh converted these, the section opened inside the if-block instead of wrapping it. Also emit comments inline inside if-blocks instead of deferring them to after the section closes, and remove the now-unused deferred comment machinery. In the forward direction, indent block-level comments at the statement level. Co-Authored-By: Keele Clendenin --- tools/hrw4u/src/hrw_visitor.py | 38 +++++++++++-------- tools/hrw4u/src/visitor.py | 2 +- tools/hrw4u/tests/data/conds/exceptions.txt | 2 + .../hrw4u/tests/data/conds/no-hook.input.txt | 6 +++ .../hrw4u/tests/data/conds/no-hook.output.txt | 3 ++ .../data/examples/all-nonsense.output.txt | 8 ++-- 6 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 tools/hrw4u/tests/data/conds/no-hook.input.txt create mode 100644 tools/hrw4u/tests/data/conds/no-hook.output.txt diff --git a/tools/hrw4u/src/hrw_visitor.py b/tools/hrw4u/src/hrw_visitor.py index 3dbdfd6208e..2878da0be28 100644 --- a/tools/hrw4u/src/hrw_visitor.py +++ b/tools/hrw4u/src/hrw_visitor.py @@ -57,7 +57,8 @@ def __init__( self._if_depth = 0 # Track nesting depth of if blocks self._in_elif_mode = False self._just_closed_nested = False - self._deferred_comments: list[str] = [] + + self._pre_section_if_start: int | None = None @lru_cache(maxsize=128) def _cached_percent_parsing(self, pct_text: str) -> tuple[str, str | None]: @@ -84,12 +85,6 @@ def _reset_condition_state(self) -> None: self._in_group = False self._group_terms.clear() - def _flush_deferred_comments(self) -> None: - """Emit comments that were deferred while inside a rule's if-block.""" - for comment in self._deferred_comments: - self.output.append(comment) - self._deferred_comments.clear() - def _start_new_section(self, section_type: SectionType) -> None: """Start a new section, handling continuation of existing sections.""" with self.debug_context(f"start_section {section_type.value}"): @@ -100,12 +95,10 @@ def _start_new_section(self, section_type: SectionType) -> None: self.emit("}") self._if_depth -= 1 self._reset_condition_state() - self._flush_deferred_comments() if self.output and self.output[-1] != "": self.output.append("") return - prev = bool(self.output) had_section = self._section_opened self._close_if_and_section() self._reset_condition_state() @@ -113,8 +106,6 @@ def _start_new_section(self, section_type: SectionType) -> None: if had_section and self.output and self.output[-1] != "": self.output.append("") - self._flush_deferred_comments() - self._section_label = section_type self.emit(f"{section_type.value} {{") self._section_opened = True @@ -162,7 +153,6 @@ def visitProgram(self, ctx: u4wrhParser.ProgramContext) -> list[str]: for line in ctx.line(): self.visit(line) self._close_if_and_section() - self._flush_deferred_comments() var_declarations = self.symbol_resolver.get_var_declarations() if var_declarations: @@ -178,9 +168,7 @@ def visitCommentLine(self, ctx: u4wrhParser.CommentLineContext) -> None: with self.debug_context("visitCommentLine"): comment_text = ctx.COMMENT().getText() self._flush_pending_condition() - if self._if_depth > 0: - self._deferred_comments.append(comment_text) - elif self._section_opened: + if self._if_depth > 0 or self._section_opened: self.emit(comment_text) else: self.output.append(comment_text) @@ -407,10 +395,27 @@ def _close_if_and_section(self) -> None: def _ensure_section_open(self, section_label: SectionType) -> None: """Ensure a section is open for statements.""" if not self._section_opened: + relocated_lines = None + relocated_if_depth = 0 + if self._if_depth > 0 and self._pre_section_if_start is not None: + relocated_lines = self.output[self._pre_section_if_start:] + relocated_if_depth = self._if_depth + self.output = self.output[:self._pre_section_if_start] + self.stmt_indent -= self._if_depth + self._if_depth = 0 + self._pre_section_if_start = None + self.emit(f"{section_label.value} {{") self._section_opened = True self.increase_indent() + if relocated_lines: + indent_prefix = " " * SystemDefaults.INDENT_SPACES + for line in relocated_lines: + self.output.append(indent_prefix + line if line.strip() else line) + self._if_depth = relocated_if_depth + self.stmt_indent += relocated_if_depth + def _start_elif_mode(self) -> None: """Handle elif line transitions.""" # After endif, we need to close the parent if-statement @@ -432,6 +437,9 @@ def _handle_else_transition(self) -> None: def _start_if_block(self, condition_expr: str) -> None: """Start a new if block.""" + if not self._section_opened and self._pre_section_if_start is None: + self._pre_section_if_start = len(self.output) + if self._in_elif_mode: self.emit(f"}} elif {condition_expr} {{") self._in_elif_mode = False diff --git a/tools/hrw4u/src/visitor.py b/tools/hrw4u/src/visitor.py index 90ea932e68b..68182fb2b9b 100644 --- a/tools/hrw4u/src/visitor.py +++ b/tools/hrw4u/src/visitor.py @@ -830,7 +830,7 @@ def visitCommentLine(self, ctx) -> None: with self.debug_context("visitCommentLine"): comment_text = ctx.COMMENT().getText() self.debug(f"preserving comment: {comment_text}") - self.output.append(comment_text) + self.emit_line(comment_text, self.stmt_indent) def visitStatement(self, ctx) -> None: with self.debug_context("visitStatement"), self.trap(ctx): diff --git a/tools/hrw4u/tests/data/conds/exceptions.txt b/tools/hrw4u/tests/data/conds/exceptions.txt index 86e44106bac..fd12cfd4a02 100644 --- a/tools/hrw4u/tests/data/conds/exceptions.txt +++ b/tools/hrw4u/tests/data/conds/exceptions.txt @@ -5,3 +5,5 @@ implicit-cmp.input: u4wrh # Double negation !(x != y) emits GROUP/GROUP:END [NOT] pairs; reverse can't reconstruct the original form double-negation.input: hrw4u +# Old format without explicit hook defaults to REMAP; hrw4u always emits explicit sections +no-hook.input: u4wrh diff --git a/tools/hrw4u/tests/data/conds/no-hook.input.txt b/tools/hrw4u/tests/data/conds/no-hook.input.txt new file mode 100644 index 00000000000..603acdb71a1 --- /dev/null +++ b/tools/hrw4u/tests/data/conds/no-hook.input.txt @@ -0,0 +1,6 @@ +REMAP { + if inbound.req.Debug { + # to return the debug header if http header debug: on + inbound.req.@DebugHeader = "TRUE"; + } +} diff --git a/tools/hrw4u/tests/data/conds/no-hook.output.txt b/tools/hrw4u/tests/data/conds/no-hook.output.txt new file mode 100644 index 00000000000..c57dcec7e50 --- /dev/null +++ b/tools/hrw4u/tests/data/conds/no-hook.output.txt @@ -0,0 +1,3 @@ +cond %{CLIENT-HEADER:Debug} ="" [NOT] +# to return the debug header if http header debug: on +set-header @DebugHeader "TRUE" diff --git a/tools/hrw4u/tests/data/examples/all-nonsense.output.txt b/tools/hrw4u/tests/data/examples/all-nonsense.output.txt index bb15eb068e3..c8672c1f28e 100644 --- a/tools/hrw4u/tests/data/examples/all-nonsense.output.txt +++ b/tools/hrw4u/tests/data/examples/all-nonsense.output.txt @@ -93,7 +93,7 @@ cond %{GROUP} cond %{GROUP:END} skip-remap TRUE no-op [L] -# like [L] + # like [L] cond %{READ_REQUEST_HDR_HOOK} [AND] # Header presence / equality and capture groups @@ -132,9 +132,9 @@ cond %{GROUP} cond %{GROUP:END} counter "write_methods_seen" set-state-int8 0 42 -# assign int8 + # assign int8 set-state-int16 0 6553 -# assign int16 + # assign int16 cond %{SEND_REQUEST_HDR_HOOK} [AND] # Use server URL information to adjust Host header @@ -174,7 +174,7 @@ elif cond %{CACHE} ("miss","skipped") cond %{GROUP:END} set-header X-Cache "%{CACHE}" -# echo the value + # echo the value # Status transforms + reason cond %{READ_RESPONSE_HDR_HOOK} [AND]