From 7823210c720c0e9ce0450c601fd0099947f55d4c Mon Sep 17 00:00:00 2001 From: Leif Hedstrom Date: Sat, 4 Apr 2026 09:41:10 -0600 Subject: [PATCH] hrw4u: Sync Python transpiler and add roundtrip tests Extract hrw4u-specific changes from #12825. Fixes issues in the Python transpiler found during C++ parser development, normalizes autest .conf rules for bidirectional conversion, and adds roundtrip tests verifying hrw4u<->conf equivalence. --- .../header_rewrite/rules/complex_logics.conf | 136 +++++++++--------- .../header_rewrite/rules/complex_logics.hrw4u | 125 ++++++++++++++++ .../rules/glob_set_redirect.conf | 5 +- .../rules/glob_set_redirect.hrw4u | 27 ++++ .../header_rewrite/rules/implicit_hook.conf | 20 +-- .../header_rewrite/rules/implicit_hook.hrw4u | 35 +++++ .../header_rewrite/rules/nested_ifs.conf | 48 +++---- .../header_rewrite/rules/nested_ifs.hrw4u | 40 ++++++ .../rules/procs/test/stamp.hrw4u | 3 + .../header_rewrite/rules/query_sub_key.conf | 3 +- .../header_rewrite/rules/query_sub_key.hrw4u | 22 +++ .../header_rewrite/rules/regex_tests.conf | 11 +- .../header_rewrite/rules/regex_tests.hrw4u | 35 +++++ .../pluginTest/header_rewrite/rules/rule.conf | 8 +- .../header_rewrite/rules/rule.hrw4u | 23 +++ .../rules/rule_add_cache_result_header.conf | 5 +- .../rules/rule_add_cache_result_header.hrw4u | 19 +++ .../header_rewrite/rules/rule_client.conf | 40 +++--- .../header_rewrite/rules/rule_client.hrw4u | 53 +++++++ .../rules/rule_cond_method.conf | 9 +- .../rules/rule_cond_method.hrw4u | 27 ++++ .../rules/rule_effective_address.conf | 9 +- .../rules/rule_effective_address.hrw4u | 23 +++ .../header_rewrite/rules/rule_empty_body.conf | 5 +- .../rules/rule_empty_body.hrw4u | 23 +++ .../header_rewrite/rules/rule_l_value.conf | 12 +- .../header_rewrite/rules/rule_l_value.hrw4u | 25 ++++ .../rules/rule_procedures.hrw4u | 22 +++ .../rules/rule_server_conditions.conf | 15 +- .../rules/rule_server_conditions.hrw4u | 37 +++++ .../rules/rule_session_vars.conf | 6 +- .../rules/rule_session_vars.hrw4u | 33 +++++ .../rules/rule_set_body_from_plugin.conf | 13 +- .../rules/rule_set_body_from_plugin.hrw4u | 29 ++++ .../rules/rule_set_body_from_remap.conf | 14 +- .../rules/rule_set_body_from_remap.hrw4u | 27 ++++ .../rules/rule_set_body_status.conf | 5 +- .../rules/rule_set_body_status.hrw4u | 23 +++ .../rule_set_header_after_ssn_txn_count.conf | 4 +- .../rule_set_header_after_ssn_txn_count.hrw4u | 21 +++ .../header_rewrite/rules/run_plugin.conf | 5 +- .../header_rewrite/rules/run_plugin.hrw4u | 25 ++++ .../header_rewrite/rules/set_redirect.conf | 4 +- .../header_rewrite/rules/set_redirect.hrw4u | 19 +++ tools/hrw4u/README.md | 63 ++++++++ tools/hrw4u/pyproject.toml | 1 + tools/hrw4u/scripts/u4wrh | 18 ++- tools/hrw4u/src/hrw_symbols.py | 47 +++--- tools/hrw4u/src/hrw_visitor.py | 39 ++++- tools/hrw4u/src/tables.py | 6 +- tools/hrw4u/src/types.py | 2 + tools/hrw4u/tests/data/ops/set-cc-alg.ast.txt | 1 + .../hrw4u/tests/data/ops/set-cc-alg.input.txt | 3 + .../tests/data/ops/set-cc-alg.output.txt | 2 + .../data/ops/set-effective-address.ast.txt | 1 + .../data/ops/set-effective-address.input.txt | 3 + .../data/ops/set-effective-address.output.txt | 2 + tools/hrw4u/tests/data/ops/skip-remap.ast.txt | 2 +- .../hrw4u/tests/data/ops/skip-remap.input.txt | 2 +- .../tests/data/ops/skip-remap.output.txt | 2 +- tools/hrw4u/tests/test_autest_rules.py | 68 +++++++++ .../hrw4u/tests/test_autest_rules_reverse.py | 68 +++++++++ 62 files changed, 1201 insertions(+), 222 deletions(-) create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/glob_set_redirect.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/implicit_hook.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/nested_ifs.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/procs/test/stamp.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/query_sub_key.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/regex_tests.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_add_cache_result_header.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_client.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_cond_method.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_empty_body.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_l_value.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_procedures.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_server_conditions.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_session_vars.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_status.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/run_plugin.hrw4u create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/set_redirect.hrw4u create mode 100644 tools/hrw4u/tests/data/ops/set-cc-alg.ast.txt create mode 100644 tools/hrw4u/tests/data/ops/set-cc-alg.input.txt create mode 100644 tools/hrw4u/tests/data/ops/set-cc-alg.output.txt create mode 100644 tools/hrw4u/tests/data/ops/set-effective-address.ast.txt create mode 100644 tools/hrw4u/tests/data/ops/set-effective-address.input.txt create mode 100644 tools/hrw4u/tests/data/ops/set-effective-address.output.txt create mode 100644 tools/hrw4u/tests/test_autest_rules.py create mode 100644 tools/hrw4u/tests/test_autest_rules_reverse.py diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf index b4f4b925180..9040258faa0 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf @@ -14,11 +14,9 @@ # 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] @@ -28,9 +26,9 @@ cond %{GROUP} 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 -# ---- 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} @@ -38,108 +36,108 @@ cond %{GROUP} 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) -# ---- 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-A} ="" [AND,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) -# ---- 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 %{CLIENT-HEADER:X-Outer-A} ="" [AND,NOT] cond %{GROUP} - cond %{CLIENT-HEADER:X-Inner-A} ="" [NOT,OR] + cond %{CLIENT-HEADER:X-Inner-A} ="" [OR,NOT] 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) -# ---- 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-A} ="" [AND,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-A} ="" [AND,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" -# ---- 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 %{CLIENT-URL:PATH} /\/logic\/if-group$/ + if + cond %{CLIENT-HEADER:X-Branch} ="alpha" [AND] cond %{GROUP} - cond %{CLIENT-HEADER:X-Case-A} ="" [NOT,OR] - cond %{CLIENT-HEADER:X-Case-B} ="" [NOT] + cond %{CLIENT-HEADER:X-Sub-A} ="" [OR,NOT] + cond %{CLIENT-HEADER:X-Sub-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 + 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$/ + if + cond %{CLIENT-HEADER:X-Outer} ="yes" + if + cond %{GROUP} + cond %{CLIENT-HEADER:X-Case-A} ="" [OR,NOT] + 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) -# ---- 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-P} ="" [OR,NOT] cond %{CLIENT-HEADER:X-Q} ="" [NOT] cond %{GROUP:END} [AND] cond %{GROUP} - cond %{CLIENT-HEADER:X-R} ="" [NOT,OR] + cond %{CLIENT-HEADER:X-R} ="" [OR,NOT] 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" -# ---- 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} @@ -147,9 +145,9 @@ cond %{GROUP} 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 -# ---- 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$/ diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.hrw4u new file mode 100644 index 00000000000..176c7acca85 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.hrw4u @@ -0,0 +1,125 @@ +# +# 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 +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/group-or$/ && (inbound.req.X-Group-A) || inbound.req.X-Group-B { + inbound.resp.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 + } +} + +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/group-and$/ && (inbound.req.X-And-A) && inbound.req.X-And-B { + inbound.resp.X-Group-And-Result = "matched"; + # ---- Test: GROUP with [NOT] on GROUP:END ---- + # hrw4u: if !(inbound.req.X-Not-A && inbound.req.X-Not-B) + } +} + +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/group-not$/ && !(inbound.req.X-Not-A && inbound.req.X-Not-B) { + inbound.resp.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) + } +} + +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/nested-group$/ && inbound.req.X-Outer-A && (inbound.req.X-Inner-A || inbound.req.X-Inner-B) { + inbound.resp.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) + } +} + +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/two-groups$/ && (inbound.req.X-Left-A && inbound.req.X-Left-B) || (inbound.req.X-Right-A && inbound.req.X-Right-B) { + inbound.resp.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" + } +} + +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/if-group$/ { + if inbound.req.X-Branch == "alpha" && (inbound.req.X-Sub-A || inbound.req.X-Sub-B) { + inbound.resp.X-If-Group-Result = "alpha"; + } elif inbound.req.X-Branch == "beta" { + inbound.resp.X-If-Group-Result = "beta"; + } else { + inbound.resp.X-If-Group-Result = "other"; + } + # ---- 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" + } +} + +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/nested-if-group$/ { + if inbound.req.X-Outer == "yes" { + if (inbound.req.X-Case-A || inbound.req.X-Case-B) { + inbound.resp.X-Nested-If-Result = "inner-match"; + } else { + inbound.resp.X-Nested-If-Result = "inner-miss"; + } + } else { + inbound.resp.X-Nested-If-Result = "outer-miss"; + } + # ---- 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) + } +} + +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/complex-and-or$/ && (inbound.req.X-P || inbound.req.X-Q) && (inbound.req.X-R || inbound.req.X-S) { + inbound.resp.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" + } +} + +SEND_RESPONSE { + if inbound.url.path ~ /\/logic\/ip-group-or$/ && (inbound.ip in {127.0.0.0/8}) || inbound.req.X-Outer == "true" { + inbound.resp.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 + } +} + +SEND_RESPONSE { + if (inbound.url.path ~ /\/logic\/group-first$/) && inbound.req.X-First-A { + inbound.resp.X-Group-First-Result = "matched"; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/glob_set_redirect.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/glob_set_redirect.conf index 7dfa502693f..69dccd12aa0 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/glob_set_redirect.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/glob_set_redirect.conf @@ -14,11 +14,10 @@ # 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_RESPONSE_HDR_HOOK} [AND] cond %{ID:REQUEST} =0 - set-redirect 301 http://redirect.com/here + set-redirect 301 "http://redirect.com/here" cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{ID:REQUEST} =1 - set-redirect 301 http://redirect.com/here + set-redirect 301 "http://redirect.com/here" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/glob_set_redirect.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/glob_set_redirect.hrw4u new file mode 100644 index 00000000000..ae70990fe29 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/glob_set_redirect.hrw4u @@ -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. +READ_RESPONSE { + if id.REQUEST == 0 { + set-redirect(301, "http://redirect.com/here"); + } +} + +SEND_RESPONSE { + if id.REQUEST == 1 { + set-redirect(301, "http://redirect.com/here"); + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/implicit_hook.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/implicit_hook.conf index c957bfa7eb7..9e3b7438249 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/implicit_hook.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/implicit_hook.conf @@ -14,21 +14,21 @@ # 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 %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{CLIENT-HEADER:X-Client-Foo} ="foo" [NOCASE,PRE] - set-header X-Response-Foo "Prefix" + set-header X-Response-Foo "Prefix" elif - cond %{CLIENT-HEADER:X-Client-Foo} ="bar" - set-header X-Response-Foo "Never" + cond %{CLIENT-HEADER:X-Client-Foo} ="bar" + set-header X-Response-Foo "Never" elif - cond %{CLIENT-HEADER:X-Client-Foo} ="" [NOT] - set-header X-Response-Foo "Yes" + cond %{CLIENT-HEADER:X-Client-Foo} ="" [NOT] + set-header X-Response-Foo "Yes" else - set-header X-Response-Foo "No" + set-header X-Response-Foo "No" +cond %{REMAP_PSEUDO_HOOK} [AND] cond %{CLIENT-HEADER:X-Fie} ="fie" [NOCASE] - add-header X-Client-Foo "Yes" + add-header X-Client-Foo "Yes" elif - cond %{CLIENT-HEADER:X-Fie} ="nope" - add-header X-client-Foo "Yes" + cond %{CLIENT-HEADER:X-Fie} ="nope" + add-header X-client-Foo "Yes" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/implicit_hook.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/implicit_hook.hrw4u new file mode 100644 index 00000000000..2522960c71f --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/implicit_hook.hrw4u @@ -0,0 +1,35 @@ +# +# 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. +SEND_RESPONSE { + if inbound.req.X-Client-Foo == "foo" with NOCASE,PRE { + inbound.resp.X-Response-Foo = "Prefix"; + } elif inbound.req.X-Client-Foo == "bar" { + inbound.resp.X-Response-Foo = "Never"; + } elif inbound.req.X-Client-Foo { + inbound.resp.X-Response-Foo = "Yes"; + } else { + inbound.resp.X-Response-Foo = "No"; + } +} + +REMAP { + if inbound.req.X-Fie == "fie" with NOCASE { + inbound.req.X-Client-Foo += "Yes"; + } elif inbound.req.X-Fie == "nope" { + inbound.req.X-client-Foo += "Yes"; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/nested_ifs.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/nested_ifs.conf index 5453b0b6fa9..4e82ef77c8c 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/nested_ifs.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/nested_ifs.conf @@ -17,28 +17,28 @@ # cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{STATUS} =200 - set-header X-When-200-Before "Yes" - if - cond %{CLIENT-HEADER:X-Foo} ="foo" - set-header X-Foo "Yes" - if - cond %{CLIENT-HEADER:X-Bar} ="bar" [NOCASE] - set-header X-Foo-And-Bar "Yes" - elif + set-header X-When-200-Before "Yes" + if + cond %{CLIENT-HEADER:X-Foo} ="foo" + set-header X-Foo "Yes" + if + cond %{CLIENT-HEADER:X-Bar} ="bar" [NOCASE] + set-header X-Foo-And-Bar "Yes" + elif + cond %{CLIENT-HEADER:X-Fie} ="fie" [NOCASE] + set-header X-Foo-And-Fie "Yes" + endif + elif + cond %{CLIENT-HEADER:X-Foo} ="maybe" + set-header X-Foo "Maybe" + elif + cond %{CLIENT-HEADER:X-Foo} ="definitely" + set-header X-Foo "Definitely" + else + set-header X-Foo "Nothing" + endif + if cond %{CLIENT-HEADER:X-Fie} ="fie" [NOCASE] - set-header X-Foo-And-Fie "Yes" - endif - elif - cond %{CLIENT-HEADER:X-Foo} ="maybe" - set-header X-Foo "Maybe" - elif - cond %{CLIENT-HEADER:X-Foo} ="definitely" - set-header X-Foo "Definitely" - else - set-header X-Foo "Nothing" - endif - if - cond %{CLIENT-HEADER:X-Fie} ="fie" [NOCASE] - set-header X-Fie-Anywhere "Yes" - endif - set-header X-When-200-After "Yes" + set-header X-Fie-Anywhere "Yes" + endif + set-header X-When-200-After "Yes" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/nested_ifs.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/nested_ifs.hrw4u new file mode 100644 index 00000000000..666b2e9cba9 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/nested_ifs.hrw4u @@ -0,0 +1,40 @@ +# +# 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. +# +SEND_RESPONSE { + if inbound.status == 200 { + inbound.resp.X-When-200-Before = "Yes"; + if inbound.req.X-Foo == "foo" { + inbound.resp.X-Foo = "Yes"; + if inbound.req.X-Bar == "bar" with NOCASE { + inbound.resp.X-Foo-And-Bar = "Yes"; + } elif inbound.req.X-Fie == "fie" with NOCASE { + inbound.resp.X-Foo-And-Fie = "Yes"; + } + } elif inbound.req.X-Foo == "maybe" { + inbound.resp.X-Foo = "Maybe"; + } elif inbound.req.X-Foo == "definitely" { + inbound.resp.X-Foo = "Definitely"; + } else { + inbound.resp.X-Foo = "Nothing"; + } + if inbound.req.X-Fie == "fie" with NOCASE { + inbound.resp.X-Fie-Anywhere = "Yes"; + } + inbound.resp.X-When-200-After = "Yes"; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/procs/test/stamp.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/procs/test/stamp.hrw4u new file mode 100644 index 00000000000..d1f8e80f44b --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/procs/test/stamp.hrw4u @@ -0,0 +1,3 @@ +procedure test::stamp($tag) { + inbound.req.X-Stamp = "$tag"; +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/query_sub_key.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/query_sub_key.conf index a885c65283d..9e7a5929426 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/query_sub_key.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/query_sub_key.conf @@ -14,8 +14,7 @@ # 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 query parameter sub-key extraction cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{CLIENT-URL:QUERY:sub} ="" [NOT] - set-header X-Query-Sub "%{CLIENT-URL:QUERY:sub}" + set-header X-Query-Sub "%{CLIENT-URL:QUERY:sub}" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/query_sub_key.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/query_sub_key.hrw4u new file mode 100644 index 00000000000..a615a36650c --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/query_sub_key.hrw4u @@ -0,0 +1,22 @@ +# +# 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 query parameter sub-key extraction +SEND_RESPONSE { + if inbound.url.query.sub { + inbound.resp.X-Query-Sub = "{inbound.url.query.sub}"; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/regex_tests.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/regex_tests.conf index 8695244eaf5..7450a333d65 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/regex_tests.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/regex_tests.conf @@ -14,18 +14,17 @@ # 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 %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{CLIENT-HEADER:X-Test1} /^[Ff]o+[bB]ar/ - set-header X-Match "yes" + set-header X-Match "yes" cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{CLIENT-URL:QUERY} /uid=([0-9]+)/ - set-header X-Match-3 "%{LAST-CAPTURE:1}" + set-header X-Match-3 "%{LAST-CAPTURE:1}" elif - cond %{CLIENT-HEADER:X-We-Set} /^fie[0-9]+fum$/ [NOCASE] - set-header X-Match-2 "yes" + cond %{CLIENT-HEADER:X-We-Set} /^fie[0-9]+fum$/ [NOCASE] + set-header X-Match-2 "yes" cond %{REMAP_PSEUDO_HOOK} [AND] cond %{CLIENT-URL:PATH} /^from/ - set-header X-We-Set "FIe123fuM" + set-header X-We-Set "FIe123fuM" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/regex_tests.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/regex_tests.hrw4u new file mode 100644 index 00000000000..79767422e8e --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/regex_tests.hrw4u @@ -0,0 +1,35 @@ +# +# 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. +SEND_RESPONSE { + if inbound.req.X-Test1 ~ /^[Ff]o+[bB]ar/ { + inbound.resp.X-Match = "yes"; + } +} + +SEND_RESPONSE { + if inbound.url.query ~ /uid=([0-9]+)/ { + inbound.resp.X-Match-3 = "{capture.1}"; + } elif inbound.req.X-We-Set ~ /^fie[0-9]+fum$/ with NOCASE { + inbound.resp.X-Match-2 = "yes"; + } +} + +REMAP { + if inbound.url.path ~ /^from/ { + inbound.req.X-We-Set = "FIe123fuM"; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf index 3a472618f9d..70bae595172 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf @@ -14,11 +14,9 @@ # 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_RESPONSE_HDR_HOOK} [AND] cond %{STATUS} =200 - set-status 303 + set-status 303 elif - cond %{STATUS} =503 - set-status 502 - + cond %{STATUS} =503 + set-status 502 diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule.hrw4u new file mode 100644 index 00000000000..1b97beb0361 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule.hrw4u @@ -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. +READ_RESPONSE { + if outbound.status == 200 { + outbound.status = 303; + } elif outbound.status == 503 { + outbound.status = 502; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_add_cache_result_header.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_add_cache_result_header.conf index 25b4f7e6107..f3ee7d74cb7 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_add_cache_result_header.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_add_cache_result_header.conf @@ -14,6 +14,5 @@ # 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 %{SEND_RESPONSE_HDR_HOOK} - set-header Cache-Result %{CACHE} \ No newline at end of file +cond %{SEND_RESPONSE_HDR_HOOK} [AND] + set-header Cache-Result "%{CACHE}" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_add_cache_result_header.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_add_cache_result_header.hrw4u new file mode 100644 index 00000000000..62ca3a1f6d2 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_add_cache_result_header.hrw4u @@ -0,0 +1,19 @@ +# +# 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. +SEND_RESPONSE { + inbound.resp.Cache-Result = "{cache()}"; +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_client.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_client.conf index 6562aaa1f24..4534cbd12a4 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_client.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_client.conf @@ -14,34 +14,34 @@ # 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 %{CLIENT-URL:PATH} /^from_1\// -cond %{CLIENT-URL:SCHEME} =http -cond %{CLIENT-URL:HOST} =www.example.com +cond %{REMAP_PSEUDO_HOOK} [AND] +cond %{CLIENT-URL:PATH} /^from_1\// [AND] +cond %{CLIENT-URL:SCHEME} =http [AND] +cond %{CLIENT-URL:HOST} =www.example.com [AND] cond %{CLIENT-URL:QUERY} /foo=bar/ - set-status 304 + set-status 304 -cond %{SEND_RESPONSE_HDR_HOOK} -cond %{CLIENT-URL:PATH} (png,gif,jpeg) [EXT,NOCASE] - set-header X-Extension "Yes" +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} (png,gif,jpeg) [NOCASE,EXT] + set-header X-Extension "Yes" -cond %{SEND_RESPONSE_HDR_HOOK} -cond %{CLIENT-URL:PATH} (hrw,foo) [MID,NOCASE] - no-op +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} (hrw,foo) [NOCASE,MID] + no-op else - set-header X-Pre-Else "Yes" + set-header X-Pre-Else "Yes" -cond %{SEND_RESPONSE_HDR_HOOK} +cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{CLIENT-HEADER:X-Testing} (foo,bar,"foo,bar") - set-header X-Testing "Yes" + set-header X-Testing "Yes" elif - cond %{CLIENT-HEADER:X-Testing} ="elif" - set-header X-Testing "elif" + cond %{CLIENT-HEADER:X-Testing} ="elif" + set-header X-Testing "elif" else - set-header X-Testing "No" + set-header X-Testing "No" -cond %{SEND_RESPONSE_HDR_HOOK} +cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{CLIENT-HEADER:X-Quoted-Set} ("foo","bar") - set-header X-Quoted-Set "Yes" + set-header X-Quoted-Set "Yes" else - set-header X-Quoted-Set "No" + set-header X-Quoted-Set "No" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_client.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_client.hrw4u new file mode 100644 index 00000000000..377fdc6b8a9 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_client.hrw4u @@ -0,0 +1,53 @@ +# +# 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. +REMAP { + if inbound.url.path ~ /^from_1\// && inbound.url.scheme == http && inbound.url.host == www.example.com && inbound.url.query ~ /foo=bar/ { + inbound.status = 304; + } +} + +SEND_RESPONSE { + if inbound.url.path in [png, gif, jpeg] with EXT,NOCASE { + inbound.resp.X-Extension = "Yes"; + } +} + +SEND_RESPONSE { + if inbound.url.path in [hrw, foo] with NOCASE,MID { + no-op(); + } else { + inbound.resp.X-Pre-Else = "Yes"; + } +} + +SEND_RESPONSE { + if inbound.req.X-Testing in [foo, bar, "foo,bar"] { + inbound.resp.X-Testing = "Yes"; + } elif inbound.req.X-Testing == "elif" { + inbound.resp.X-Testing = "elif"; + } else { + inbound.resp.X-Testing = "No"; + } +} + +SEND_RESPONSE { + if inbound.req.X-Quoted-Set in ["foo", "bar"] { + inbound.resp.X-Quoted-Set = "Yes"; + } else { + inbound.resp.X-Quoted-Set = "No"; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_cond_method.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_cond_method.conf index a244509bd0b..95e09b41fe5 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_cond_method.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_cond_method.conf @@ -14,11 +14,10 @@ # 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 %{REMAP_PSEUDO_HOOK} +cond %{REMAP_PSEUDO_HOOK} [AND] cond %{METHOD} (GET,PUSH) -set-config proxy.config.http.insert_response_via_str 1 [L] + set-config "proxy.config.http.insert_response_via_str" 1 -cond %{SEND_REQUEST_HDR_HOOK} +cond %{SEND_REQUEST_HDR_HOOK} [AND] cond %{METHOD} =DELETE -set-config proxy.config.http.insert_response_via_str 1 [L] + set-config "proxy.config.http.insert_response_via_str" 1 diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_cond_method.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_cond_method.hrw4u new file mode 100644 index 00000000000..60e8a66a7b7 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_cond_method.hrw4u @@ -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. +REMAP { + if inbound.method in [GET, PUSH] { + set-config("proxy.config.http.insert_response_via_str", 1); + } +} + +SEND_REQUEST { + if outbound.method == DELETE { + set-config("proxy.config.http.insert_response_via_str", 1); + } +} 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 index f7c84b18a5c..fef0cde70b4 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.conf @@ -14,9 +14,8 @@ # 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 %{REMAP_PSEUDO_HOOK} [AND] + set-effective-address "%{CLIENT-HEADER:Real-IP}" -cond %{REMAP_PSEUDO_HOOK} - set-effective-address %{HEADER:Real-IP} - -cond %{SEND_RESPONSE_HDR_HOOK} - set-header Effective-IP %{INBOUND:REMOTE-ADDR} +cond %{SEND_RESPONSE_HDR_HOOK} [AND] + set-header Effective-IP "%{INBOUND:REMOTE-ADDR}" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.hrw4u new file mode 100644 index 00000000000..a04c8c459ef --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_effective_address.hrw4u @@ -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. +REMAP { + set-effective-address("{inbound.req.Real-IP}"); +} + +SEND_RESPONSE { + inbound.resp.Effective-IP = "{inbound.conn.REMOTE-ADDR}"; +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_empty_body.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_empty_body.conf index 73a19b5fda1..9b626fd93ed 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_empty_body.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_empty_body.conf @@ -14,9 +14,8 @@ # 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 %{REMAP_PSEUDO_HOOK} +cond %{REMAP_PSEUDO_HOOK} [AND] set-status 200 -cond %{SEND_RESPONSE_HDR_HOOK} +cond %{SEND_RESPONSE_HDR_HOOK} [AND] set-body "" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_empty_body.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_empty_body.hrw4u new file mode 100644 index 00000000000..bd59c145797 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_empty_body.hrw4u @@ -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. +REMAP { + inbound.status = 200; +} + +SEND_RESPONSE { + inbound.resp.body = ""; +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_l_value.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_l_value.conf index 82e530a7771..f1336f78875 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_l_value.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_l_value.conf @@ -14,10 +14,10 @@ # 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 %{SEND_RESPONSE_HDR_HOOK} [AND] + set-header X-First "First" + set-header X-Last "Last" + no-op [L] -cond %{SEND_RESPONSE_HDR_HOOK} - set-header X-First "First" - set-header X-Last "Last" [L] - -cond %{SEND_RESPONSE_HDR_HOOK} - set-header X-Not-Here "Stop" +cond %{SEND_RESPONSE_HDR_HOOK} [AND] + set-header X-Not-Here "Stop" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_l_value.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_l_value.hrw4u new file mode 100644 index 00000000000..311e211bdb7 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_l_value.hrw4u @@ -0,0 +1,25 @@ +# +# 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. +SEND_RESPONSE { + inbound.resp.X-First = "First"; + inbound.resp.X-Last = "Last"; + break; +} + +SEND_RESPONSE { + inbound.resp.X-Not-Here = "Stop"; +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_procedures.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_procedures.hrw4u new file mode 100644 index 00000000000..59809ca3cb7 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_procedures.hrw4u @@ -0,0 +1,22 @@ +# +# 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. + +use test::stamp + +SEND_RESPONSE { + test::stamp("autest-procedure-ok"); +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_server_conditions.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_server_conditions.conf index e46e2d39da1..ae58b96f247 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_server_conditions.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_server_conditions.conf @@ -14,19 +14,18 @@ # 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 SERVER-HEADER and SERVER-URL conditions -cond %{SEND_REQUEST_HDR_HOOK} - set-header X-Server-Marker "ATS-Processed" +cond %{SEND_REQUEST_HDR_HOOK} [AND] + set-header X-Server-Marker "ATS-Processed" -cond %{SEND_RESPONSE_HDR_HOOK} - set-header X-Server-Path "%{SERVER-URL:PATH}" - set-header X-Server-Host-Header "%{SERVER-HEADER:Host}" +cond %{SEND_RESPONSE_HDR_HOOK} [AND] + set-header X-Server-Path "%{SERVER-URL:PATH}" + set-header X-Server-Host-Header "%{SERVER-HEADER:Host}" cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{SERVER-HEADER:X-Server-Marker} ="ATS-Processed" - set-header X-Marker-Found "Yes" + set-header X-Marker-Found "Yes" cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{SERVER-URL:PATH} /^to_16\// - set-header X-Path-Match "Yes" + set-header X-Path-Match "Yes" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_server_conditions.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_server_conditions.hrw4u new file mode 100644 index 00000000000..56a44c1125a --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_server_conditions.hrw4u @@ -0,0 +1,37 @@ +# +# 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 SERVER-HEADER and SERVER-URL conditions +SEND_REQUEST { + outbound.req.X-Server-Marker = "ATS-Processed"; +} + +SEND_RESPONSE { + inbound.resp.X-Server-Path = "{outbound.url.path}"; + inbound.resp.X-Server-Host-Header = "{outbound.req.Host}"; +} + +SEND_RESPONSE { + if outbound.req.X-Server-Marker == "ATS-Processed" { + inbound.resp.X-Marker-Found = "Yes"; + } +} + +SEND_RESPONSE { + if outbound.url.path ~ /^to_16\// { + inbound.resp.X-Path-Match = "Yes"; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_session_vars.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_session_vars.conf index d4b13761951..92209dd8d44 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_session_vars.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_session_vars.conf @@ -20,7 +20,7 @@ # request the flag is still true, so X-Session-Seen is added. cond %{SEND_RESPONSE_HDR_HOOK} [AND] cond %{SESSION-FLAG:0} =TRUE - set-header X-Session-Seen "yes" + set-header X-Session-Seen "yes" -cond %{SEND_RESPONSE_HDR_HOOK} - set-session-flag 0 true +cond %{SEND_RESPONSE_HDR_HOOK} [AND] + set-session-flag 0 true diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_session_vars.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_session_vars.hrw4u new file mode 100644 index 00000000000..d3912392296 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_session_vars.hrw4u @@ -0,0 +1,33 @@ +# +# 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 SESSION-FLAG: on the first request the flag is unset so the header is +# absent; set-session-flag then marks it true. On the second keep-alive +# request the flag is still true, so X-Session-Seen is added. +SESSION_VARS { + ssn_bool_0: bool; +} + +SEND_RESPONSE { + if ssn_bool_0 == TRUE { + inbound.resp.X-Session-Seen = "yes"; + } +} + +SEND_RESPONSE { + ssn_bool_0 = true; +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.conf index c4c83abb17d..b7fb773d438 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.conf @@ -14,13 +14,12 @@ # 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 uses cond %{CLIENT-URL:PATH} to differentiate tests # It is not needed to make set-body-from work -cond %{READ_RESPONSE_HDR_HOOK} -cond %{CLIENT-URL:PATH} = "plugin_success" -set-body-from http://www.example.com/404.html +cond %{READ_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} ="plugin_success" + set-body-from "http://www.example.com/404.html" -cond %{READ_RESPONSE_HDR_HOOK} -cond %{CLIENT-URL:PATH} = "plugin_fail" -set-body-from http://www.example.com/plugin_no_server +cond %{READ_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} ="plugin_fail" + set-body-from "http://www.example.com/plugin_no_server" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.hrw4u new file mode 100644 index 00000000000..4d57b082536 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_plugin.hrw4u @@ -0,0 +1,29 @@ +# +# 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 uses cond %{CLIENT-URL:PATH} to differentiate tests +# It is not needed to make set-body-from work +READ_RESPONSE { + if inbound.url.path == "plugin_success" { + set-body-from("http://www.example.com/404.html"); + } +} + +READ_RESPONSE { + if inbound.url.path == "plugin_fail" { + set-body-from("http://www.example.com/plugin_no_server"); + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.conf index 351e17f7b8d..11dbee6800e 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.conf @@ -14,11 +14,11 @@ # 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_RESPONSE_HDR_HOOK} -cond %{CLIENT-URL:PATH} = "remap_success" [OR] -cond %{CLIENT-URL:PATH} = "200" -set-body-from http://www.example.com/404.html +cond %{READ_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} ="remap_success" [OR] +cond %{CLIENT-URL:PATH} ="200" + set-body-from "http://www.example.com/404.html" -cond %{READ_RESPONSE_HDR_HOOK} -cond %{CLIENT-URL:PATH} = "remap_fail" -set-body-from http://www.example.com/fail +cond %{READ_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} ="remap_fail" + set-body-from "http://www.example.com/fail" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.hrw4u new file mode 100644 index 00000000000..31c3a6f6c95 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_from_remap.hrw4u @@ -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. +READ_RESPONSE { + if inbound.url.path == "remap_success" || inbound.url.path == "200" { + set-body-from("http://www.example.com/404.html"); + } +} + +READ_RESPONSE { + if inbound.url.path == "remap_fail" { + set-body-from("http://www.example.com/fail"); + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_status.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_status.conf index 58256210b7a..da1edb30405 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_status.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_status.conf @@ -14,9 +14,8 @@ # 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 %{REMAP_PSEUDO_HOOK} +cond %{REMAP_PSEUDO_HOOK} [AND] set-status 200 -cond %{SEND_RESPONSE_HDR_HOOK} +cond %{SEND_RESPONSE_HDR_HOOK} [AND] set-body "%{STATUS}" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_status.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_status.hrw4u new file mode 100644 index 00000000000..5afa3fae975 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_body_status.hrw4u @@ -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. +REMAP { + inbound.status = 200; +} + +SEND_RESPONSE { + inbound.resp.body = "{inbound.status}"; +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.conf index ab3fdd36536..5538f585620 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.conf @@ -15,5 +15,5 @@ # See the License for the specific language governing permissions and # limitations under the License. cond %{SEND_RESPONSE_HDR_HOOK} [AND] - cond %{SSN-TXN-COUNT} >2 - set-header Connection close \ No newline at end of file +cond %{SSN-TXN-COUNT} >2 + set-header Connection "close" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.hrw4u new file mode 100644 index 00000000000..5e890bcd57b --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.hrw4u @@ -0,0 +1,21 @@ +# +# 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. +SEND_RESPONSE { + if ssn-txn-count() > 2 { + inbound.resp.Connection = "close"; + } +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/run_plugin.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/run_plugin.conf index af55270d00c..c012bf2c57a 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/run_plugin.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/run_plugin.conf @@ -14,12 +14,11 @@ # 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 run-plugin operator with a relative plugin path. # This verifies that the plugin factory is properly initialized when # header_rewrite is used as a remap plugin without a geo database. # We use multiplexer.so because with no arguments it creates an empty # instance and just returns TSREMAP_NO_REMAP, allowing the request to # proceed normally to the backend. -cond %{REMAP_PSEUDO_HOOK} - run-plugin multiplexer.so +cond %{REMAP_PSEUDO_HOOK} [AND] + run-plugin "multiplexer.so" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/run_plugin.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/run_plugin.hrw4u new file mode 100644 index 00000000000..38eb7896d33 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/run_plugin.hrw4u @@ -0,0 +1,25 @@ +# +# 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 run-plugin operator with a relative plugin path. +# This verifies that the plugin factory is properly initialized when +# header_rewrite is used as a remap plugin without a geo database. +# We use multiplexer.so because with no arguments it creates an empty +# instance and just returns TSREMAP_NO_REMAP, allowing the request to +# proceed normally to the backend. +REMAP { + run-plugin("multiplexer.so"); +} diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/set_redirect.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/set_redirect.conf index 6a3230a6ada..61389a5835e 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/set_redirect.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/set_redirect.conf @@ -14,5 +14,5 @@ # 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. - -set-redirect 301 %{TO-URL:URL} +cond %{REMAP_PSEUDO_HOOK} [AND] + set-redirect 301 "%{TO-URL:URL}" diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/set_redirect.hrw4u b/tests/gold_tests/pluginTest/header_rewrite/rules/set_redirect.hrw4u new file mode 100644 index 00000000000..2ec22d0c0e3 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/set_redirect.hrw4u @@ -0,0 +1,19 @@ +# +# 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. +REMAP { + set-redirect(301, "{to.url.url}"); +} diff --git a/tools/hrw4u/README.md b/tools/hrw4u/README.md index cd2dfdbe2dd..2909ae71f99 100644 --- a/tools/hrw4u/README.md +++ b/tools/hrw4u/README.md @@ -21,10 +21,73 @@ Decompiles existing `header_rewrite` rules back into HRW4U source code. ## Requirements +### Python Tools (hrw4u, u4wrh) - **Python 3.11+** (uses modern type annotations and performance features) - **ANTLR4** for grammar parsing - **uv** (for Python environment management) +### C++ Native Parser (src/hrw4u) + +ATS includes a C++ ANTLR4-based parser that lets the `header_rewrite` plugin +load `.hrw4u` files directly. Building it requires: + +- **ANTLR4 C++ runtime** (headers and shared library) +- **ANTLR4 tool** (`antlr4` command, same major.minor version as the runtime) + +#### macOS (Homebrew) +```bash +brew install antlr4-cpp-runtime antlr4 +``` + +#### Fedora/RHEL +```bash +dnf install antlr4-cpp-runtime-devel antlr4 +``` + +#### Ubuntu/Debian (from source recommended) + +The distro packages often have version mismatches between the tool and runtime, +and the shared library may be built with a different C++ ABI. Building from +source is recommended: + +```bash +# Download ANTLR4 C++ runtime source (match JAR version) +# https://github.com/antlr/antlr4/releases + +cd antlr4-cpp-runtime- +cmake -B build \ + -DCMAKE_INSTALL_PREFIX=/opt/antlr4 \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DANTLR4_INSTALL=ON +cmake --build build -j$(nproc) +cmake --install build +``` + +If you are building ATS with a non-default compiler toolchain, build the +ANTLR4 runtime with the same compilers to avoid ABI mismatches: + +```bash +cmake -B build \ + -DCMAKE_C_COMPILER=/opt/llvm/bin/clang \ + -DCMAKE_CXX_COMPILER=/opt/llvm/bin/clang++ \ + -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ + -DCMAKE_INSTALL_PREFIX=/opt/antlr4 \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DANTLR4_INSTALL=ON +cmake --build build -j$(nproc) +cmake --install build +``` + +Then pass the install prefix when configuring ATS: +```bash +cmake -B build -DCMAKE_PREFIX_PATH=/opt/antlr4 ... +``` + +If the `antlr4` tool is not on `PATH`, set it explicitly: +```bash +cmake -B build -DANTLR4_EXECUTABLE=/opt/antlr4/bin/antlr4 ... +``` + ## Development Setup ### 1. Clone and Setup Environment diff --git a/tools/hrw4u/pyproject.toml b/tools/hrw4u/pyproject.toml index d30dd7491f5..bab7c816e84 100644 --- a/tools/hrw4u/pyproject.toml +++ b/tools/hrw4u/pyproject.toml @@ -80,6 +80,7 @@ markers = [ "ast: marks tests for AST validation", "procedures: marks tests for procedure expansion", "sandbox: marks tests for sandbox policy enforcement", + "autest: marks tests that validate autest rule files", ] [dependency-groups] diff --git a/tools/hrw4u/scripts/u4wrh b/tools/hrw4u/scripts/u4wrh index 2730661e5ba..8eddc6029f9 100755 --- a/tools/hrw4u/scripts/u4wrh +++ b/tools/hrw4u/scripts/u4wrh @@ -19,12 +19,26 @@ from __future__ import annotations +import argparse + from hrw4u.common import run_main from u4wrh.hrw_visitor import HRWInverseVisitor from u4wrh.u4wrhLexer import u4wrhLexer from u4wrh.u4wrhParser import u4wrhParser +def _add_args(parser: argparse.ArgumentParser, output_group: argparse._MutuallyExclusiveGroup) -> None: + parser.add_argument( + "--no-merge-sections", action="store_true", help="Keep same-named hook sections separate instead of merging them") + + +def _visitor_kwargs(args: argparse.Namespace) -> dict[str, object]: + kwargs: dict[str, object] = {} + if args.no_merge_sections: + kwargs['merge_sections'] = False + return kwargs + + def main() -> None: """Main entry point for the u4wrh script.""" run_main( @@ -34,7 +48,9 @@ def main() -> None: visitor_class=HRWInverseVisitor, error_prefix="u4wrh", output_flag_name="hrw4u", - output_flag_help="Produce reconstructed hrw4u output (default)") + output_flag_help="Produce reconstructed hrw4u output (default)", + add_args=_add_args, + visitor_kwargs=_visitor_kwargs) if __name__ == "__main__": diff --git a/tools/hrw4u/src/hrw_symbols.py b/tools/hrw4u/src/hrw_symbols.py index aa7ef58d4d1..37de85cc44b 100644 --- a/tools/hrw4u/src/hrw_symbols.py +++ b/tools/hrw4u/src/hrw_symbols.py @@ -16,7 +16,6 @@ # limitations under the License. from __future__ import annotations -from typing import Callable from functools import cached_property import re @@ -71,11 +70,6 @@ def _rev_functions(self) -> dict[str, str]: return reverse_map return {params.target: fn_name for fn_name, params in self._function_map.items()} - @cached_property - def _rev_sections(self) -> dict[str, str]: - """Cached reverse section mapping.""" - return {s.hook_name: s.value for s in SectionType} - def _section_label_for_tag(self, tag: str) -> str | None: try: return SectionType.from_hook(tag).value @@ -143,14 +137,6 @@ def _resolve_ambiguous_exact(self, tag: str, section: SectionType | None) -> str elif tag == "IP": return None - for key, params in self._condition_map.items(): - mapped_tag = params.target - tag_part = mapped_tag.replace("%{", "").replace("}", "").split(":")[0] - restricted = params.sections if params else None - if tag_part == tag: - if not restricted or not section or section not in restricted: - pass - return None def _handle_state_tag(self, tag: str, payload: str | None, scope: types.VarScope = types.VarScope.TXN) -> tuple[str, bool]: @@ -344,14 +330,43 @@ def convert_set_to_brackets(self, set_text: str) -> str: if set_text.startswith('(') and set_text.endswith(')'): content = set_text[1:-1] - content = ', '.join(item.strip() for item in content.split(',')) + items = self._split_set_items(content) + content = ', '.join(item.strip() for item in items) return '[' + content + ']' elif set_text.startswith('[') and set_text.endswith(']'): content = set_text[1:-1] - content = ', '.join(item.strip() for item in content.split(',')) + items = self._split_set_items(content) + content = ', '.join(item.strip() for item in items) return '[' + content + ']' return set_text + def _split_set_items(self, content: str) -> list[str]: + """Split set items on commas, respecting quoted strings.""" + items = [] + current = [] + in_quotes = False + quote_char = None + + for char in content: + if char in '"\'': + if not in_quotes: + in_quotes = True + quote_char = char + elif char == quote_char: + in_quotes = False + quote_char = None + current.append(char) + elif char == ',' and not in_quotes: + items.append(''.join(current)) + current = [] + else: + current.append(char) + + if current: + items.append(''.join(current)) + + return items + def format_iprange(self, iprange_text: str) -> str: """Format IP range with proper spacing.""" try: diff --git a/tools/hrw4u/src/hrw_visitor.py b/tools/hrw4u/src/hrw_visitor.py index b6dc61a90d0..42a4deecd7f 100644 --- a/tools/hrw4u/src/hrw_visitor.py +++ b/tools/hrw4u/src/hrw_visitor.py @@ -40,13 +40,15 @@ def __init__( section_label: SectionType = SectionType.REMAP, debug: bool = SystemDefaults.DEFAULT_DEBUG, error_collector=None, - preserve_comments: bool = True) -> None: + preserve_comments: bool = True, + merge_sections: bool = True) -> None: super().__init__(filename=filename, debug=debug, error_collector=error_collector) # HRW inverse-specific state self._section_label = section_label self.preserve_comments = preserve_comments + self.merge_sections = merge_sections self._pending_terms: list[tuple[str, CondState]] = [] self._in_group: bool = False self._group_terms: list[tuple[str, CondState]] = [] @@ -59,6 +61,7 @@ def __init__( self._just_closed_nested = False self._pre_section_if_start: int | None = None + self._expecting_if_cond = False # True after 'if' operator, before its condition @lru_cache(maxsize=128) def _cached_percent_parsing(self, pct_text: str) -> tuple[str, str | None]: @@ -84,12 +87,23 @@ def _reset_condition_state(self) -> None: self._in_elif_mode = False self._in_group = False self._group_terms.clear() + self._expecting_if_cond = False + + def _close_if_chain_for_new_rule(self) -> None: + """Close if-else chain when a new rule starts without elif/else.""" + expecting_nested_if = self._expecting_if_cond + self._expecting_if_cond = False + + if (self._if_depth > 0 and not self._in_elif_mode and not self._pending_terms and not expecting_nested_if and + not self._in_group): + self.debug("new rule detected - closing if chain") + self._start_new_section(self._section_label) 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}"): - if self._section_opened and self._section_label == section_type: - self.debug(f"continuing existing section") + if self.merge_sections and self._section_opened and self._section_label == section_type: + self.debug("continuing existing section") while self._if_depth > 0: self.decrease_indent() self.emit("}") @@ -163,7 +177,12 @@ def visitProgram(self, ctx: u4wrhParser.ProgramContext) -> list[str]: preamble += ["SESSION_VARS {"] + [self.format_with_indent(d, 1) for d in ssn_decls] + ["}", ""] if preamble: - self.output = preamble + self.output + insert_pos = 0 + for i, line in enumerate(self.output): + if not line.startswith("#") and line.strip(): + break + insert_pos = i + 1 + self.output = self.output[:insert_pos] + preamble + self.output[insert_pos:] return self.output @@ -184,6 +203,7 @@ def visitIfLine(self, ctx: u4wrhParser.IfLineContext) -> None: with self.debug_context("visitIfLine"): self._flush_pending_condition() self._just_closed_nested = False + self._expecting_if_cond = True return None def visitEndifLine(self, ctx: u4wrhParser.EndifLineContext) -> None: @@ -236,6 +256,9 @@ def visitCondLine(self, ctx: u4wrhParser.CondLineContext) -> None: except ValueError: pass + # Not a hook - check if we need to close existing if-else chain + self._close_if_chain_for_new_rule() + match tag: case "GROUP": if payload is None: @@ -271,6 +294,9 @@ def visitCondLine(self, ctx: u4wrhParser.CondLineContext) -> None: return None case _: + # No percent block - check if we need to close existing if-else chain + self._close_if_chain_for_new_rule() + if body.comparison(): comparison_expr = self._build_comparison_expression(body.comparison()) if comparison_expr != "ERROR": # Skip if error occurred @@ -374,6 +400,11 @@ def visitOpLine(self, ctx: u4wrhParser.OpLineContext) -> None: stmt = self.symbol_resolver.op_to_hrw4u(cmd, args, self._section_label, op_state) self.emit(stmt + ";") + # If [L] modifier was on a non-no-op operator, emit break; after the statement + # (no-op [L] is already converted to "break" by op_to_hrw4u) + if op_state.last and cmd != "no-op": + self.emit("break;") + return None # Condition block lifecycle methods - specific to inverse visitor diff --git a/tools/hrw4u/src/tables.py b/tools/hrw4u/src/tables.py index 86d2de67a79..9b476763710 100644 --- a/tools/hrw4u/src/tables.py +++ b/tools/hrw4u/src/tables.py @@ -44,7 +44,7 @@ "inbound.cookie.": MapParams(target=HeaderOperations.COOKIE_OPERATIONS, validate=Validator.http_token(), sections=HTTP_SECTIONS), "inbound.req.": MapParams(target=HeaderOperations.OPERATIONS, add=True, validate=Validator.http_header_name(), sections=HTTP_SECTIONS), "inbound.resp.body": MapParams(target="set-body", validate=Validator.quoted_or_simple(), sections=HTTP_SECTIONS), - "inbound.resp.": MapParams(target=HeaderOperations.OPERATIONS, add=True, validate=Validator.http_header_name(), sections={SectionType.READ_RESPONSE, SectionType.SEND_RESPONSE}), + "inbound.resp.": MapParams(target=HeaderOperations.OPERATIONS, add=True, validate=Validator.http_header_name(), sections={SectionType.READ_RESPONSE, SectionType.SEND_RESPONSE, SectionType.TXN_CLOSE}), "inbound.status.reason": MapParams(target="set-status-reason", validate=Validator.quoted_or_simple(), sections=HTTP_SECTIONS), "inbound.status": MapParams(target="set-status", validate=Validator.range(0, 999), sections=HTTP_SECTIONS), "inbound.url.": MapParams(target=HeaderOperations.DESTINATION_OPERATIONS, upper=True, validate=Validator.suffix_group(SuffixGroup.URL_FIELDS), sections=HTTP_SECTIONS), @@ -65,7 +65,9 @@ "keep_query": MapParams(target="rm-destination QUERY", validate=Validator.arg_count(1).quoted_or_simple(), sections=HTTP_SECTIONS), "run-plugin": MapParams(target="run-plugin", validate=Validator.min_args(1).quoted_or_simple(), sections=HTTP_SECTIONS), "set-body-from": MapParams(target="set-body-from", validate=Validator.arg_count(1).quoted_or_simple(), sections=HTTP_SECTIONS), + "set-cc-alg": MapParams(target="set-cc-alg", validate=Validator.arg_count(1).quoted_or_simple(), sections=HTTP_SECTIONS), "set-config": MapParams(target="set-config", validate=Validator.arg_count(2).quoted_or_simple(), sections=HTTP_SECTIONS), + "set-effective-address": MapParams(target="set-effective-address", validate=Validator.arg_count(1).quoted_or_simple(), sections=HTTP_SECTIONS), "set-redirect": MapParams(target="set-redirect", validate=Validator.arg_count(2).arg_at(0, Validator.range(300, 399)).arg_at(1, Validator.quoted_or_simple()), sections=HTTP_SECTIONS), "skip-remap": MapParams(target="skip-remap", validate=Validator.arg_count(1).suffix_group(SuffixGroup.BOOL_FIELDS)._add(Validator.normalize_arg_at(0)), sections={SectionType.PRE_REMAP, SectionType.REMAP, SectionType.READ_REQUEST}), "set-plugin-cntl": MapParams(target="set-plugin-cntl", validate=Validator.arg_count(2)._add(Validator.normalize_arg_at(0)).arg_at(0, Validator.suffix_group(SuffixGroup.PLUGIN_CNTL_FIELDS))._add(Validator.normalize_arg_at(1))._add(Validator.conditional_arg_validation(SuffixGroup.PLUGIN_CNTL_MAPPING.value)), sections=HTTP_SECTIONS), @@ -110,7 +112,7 @@ "inbound.conn.": MapParams(target="INBOUND", upper=True, prefix=True, validate=Validator.suffix_group(SuffixGroup.CONN_FIELDS)), "inbound.cookie.": MapParams(target="COOKIE", prefix=True, validate=Validator.http_token(), sections=HTTP_SECTIONS, rev={"reverse_fallback": "inbound.cookie."}), "inbound.req.": MapParams(target="CLIENT-HEADER", prefix=True, validate=Validator.http_header_name(), sections=HTTP_SECTIONS, rev={"reverse_fallback": "inbound.req."}), - "inbound.resp.": MapParams(target="HEADER", prefix=True, validate=Validator.http_header_name(), sections={SectionType.READ_RESPONSE, SectionType.SEND_RESPONSE}, rev={"reverse_context": "header_condition"}), + "inbound.resp.": MapParams(target="HEADER", prefix=True, validate=Validator.http_header_name(), sections={SectionType.READ_RESPONSE, SectionType.SEND_RESPONSE, SectionType.TXN_CLOSE}, rev={"reverse_context": "header_condition"}), "inbound.url.query.": MapParams(target="CLIENT-URL:QUERY", prefix=True, validate=Validator.http_token(), sections=HTTP_SECTIONS), "inbound.url.": MapParams(target="CLIENT-URL", upper=True, prefix=True, validate=Validator.suffix_group(SuffixGroup.URL_FIELDS), sections=HTTP_SECTIONS), "nexthop.": MapParams(target="NEXT-HOP", upper=True, prefix=True, validate=Validator.suffix_group(SuffixGroup.NEXTHOP_FIELDS), sections={SectionType.SEND_REQUEST, SectionType.READ_RESPONSE, SectionType.SEND_RESPONSE}, rev={"reverse_fallback": "nexthop."}), diff --git a/tools/hrw4u/src/types.py b/tools/hrw4u/src/types.py index 97c28438bea..c708bea51b9 100644 --- a/tools/hrw4u/src/types.py +++ b/tools/hrw4u/src/types.py @@ -39,7 +39,9 @@ class MagicStrings(str, Enum): SET_BODY_FROM = "set-body-from" NO_OP = "no-op" SET_DEBUG = "set-debug" + SET_CC_ALG = "set-cc-alg" SET_CONFIG = "set-config" + SET_EFFECTIVE_ADDRESS = "set-effective-address" SET_REDIRECT = "set-redirect" SKIP_REMAP = "skip-remap" RUN_PLUGIN = "run-plugin" diff --git a/tools/hrw4u/tests/data/ops/set-cc-alg.ast.txt b/tools/hrw4u/tests/data/ops/set-cc-alg.ast.txt new file mode 100644 index 00000000000..7d723bb3bb7 --- /dev/null +++ b/tools/hrw4u/tests/data/ops/set-cc-alg.ast.txt @@ -0,0 +1 @@ +(program (programItem (section REMAP { (sectionBody (statement (functionCall set-cc-alg ( (argumentList (value "cubic")) )) ;)) })) ) diff --git a/tools/hrw4u/tests/data/ops/set-cc-alg.input.txt b/tools/hrw4u/tests/data/ops/set-cc-alg.input.txt new file mode 100644 index 00000000000..642ff2f64bf --- /dev/null +++ b/tools/hrw4u/tests/data/ops/set-cc-alg.input.txt @@ -0,0 +1,3 @@ +REMAP { + set-cc-alg("cubic"); +} diff --git a/tools/hrw4u/tests/data/ops/set-cc-alg.output.txt b/tools/hrw4u/tests/data/ops/set-cc-alg.output.txt new file mode 100644 index 00000000000..446f639664a --- /dev/null +++ b/tools/hrw4u/tests/data/ops/set-cc-alg.output.txt @@ -0,0 +1,2 @@ +cond %{REMAP_PSEUDO_HOOK} [AND] + set-cc-alg "cubic" diff --git a/tools/hrw4u/tests/data/ops/set-effective-address.ast.txt b/tools/hrw4u/tests/data/ops/set-effective-address.ast.txt new file mode 100644 index 00000000000..91379a7f997 --- /dev/null +++ b/tools/hrw4u/tests/data/ops/set-effective-address.ast.txt @@ -0,0 +1 @@ +(program (programItem (section REMAP { (sectionBody (statement (functionCall set-effective-address ( (argumentList (value "{inbound.req.Real-IP}")) )) ;)) })) ) diff --git a/tools/hrw4u/tests/data/ops/set-effective-address.input.txt b/tools/hrw4u/tests/data/ops/set-effective-address.input.txt new file mode 100644 index 00000000000..ef958d8a5ee --- /dev/null +++ b/tools/hrw4u/tests/data/ops/set-effective-address.input.txt @@ -0,0 +1,3 @@ +REMAP { + set-effective-address("{inbound.req.Real-IP}"); +} diff --git a/tools/hrw4u/tests/data/ops/set-effective-address.output.txt b/tools/hrw4u/tests/data/ops/set-effective-address.output.txt new file mode 100644 index 00000000000..625d503a2b9 --- /dev/null +++ b/tools/hrw4u/tests/data/ops/set-effective-address.output.txt @@ -0,0 +1,2 @@ +cond %{REMAP_PSEUDO_HOOK} [AND] + set-effective-address "%{CLIENT-HEADER:Real-IP}" diff --git a/tools/hrw4u/tests/data/ops/skip-remap.ast.txt b/tools/hrw4u/tests/data/ops/skip-remap.ast.txt index f817d0995f0..9223a553e19 100644 --- a/tools/hrw4u/tests/data/ops/skip-remap.ast.txt +++ b/tools/hrw4u/tests/data/ops/skip-remap.ast.txt @@ -1 +1 @@ -(program (programItem (section REMAP { (sectionBody (conditional (ifStatement if (condition (expression (term (factor (comparison (comparable inbound.req.path) ~ (regex /foo/)))))) (block { (blockItem (statement (functionCall skip-remap ( (argumentList (value true)) )) ;)) })))) })) ) +(program (programItem (section PRE_REMAP { (sectionBody (conditional (ifStatement if (condition (expression (term (factor (comparison (comparable inbound.req.path) ~ (regex /foo/)))))) (block { (blockItem (statement (functionCall skip-remap ( (argumentList (value true)) )) ;)) })))) })) ) diff --git a/tools/hrw4u/tests/data/ops/skip-remap.input.txt b/tools/hrw4u/tests/data/ops/skip-remap.input.txt index ac8da25f338..a679ea7c608 100644 --- a/tools/hrw4u/tests/data/ops/skip-remap.input.txt +++ b/tools/hrw4u/tests/data/ops/skip-remap.input.txt @@ -1,4 +1,4 @@ -REMAP { +PRE_REMAP { if inbound.req.path ~ /foo/ { skip-remap(true); } diff --git a/tools/hrw4u/tests/data/ops/skip-remap.output.txt b/tools/hrw4u/tests/data/ops/skip-remap.output.txt index 5f4a5194b20..fdf7a4022f7 100644 --- a/tools/hrw4u/tests/data/ops/skip-remap.output.txt +++ b/tools/hrw4u/tests/data/ops/skip-remap.output.txt @@ -1,3 +1,3 @@ -cond %{REMAP_PSEUDO_HOOK} [AND] +cond %{READ_REQUEST_PRE_REMAP_HOOK} [AND] cond %{CLIENT-HEADER:path} /foo/ skip-remap TRUE diff --git a/tools/hrw4u/tests/test_autest_rules.py b/tools/hrw4u/tests/test_autest_rules.py new file mode 100644 index 00000000000..6f2c6d69d55 --- /dev/null +++ b/tools/hrw4u/tests/test_autest_rules.py @@ -0,0 +1,68 @@ +# +# 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 hrw4u -> conf conversion matches the autest .conf files.""" +from __future__ import annotations + +from pathlib import Path + +import pytest +from antlr4 import CommonTokenStream, InputStream + +from hrw4u.hrw4uLexer import hrw4uLexer +from hrw4u.hrw4uParser import hrw4uParser +from hrw4u.visitor import HRW4UVisitor +from hrw4u.errors import ErrorCollector + +AUTEST_RULES_DIR = Path( + __file__).resolve().parent.parent.parent.parent / "tests" / "gold_tests" / "pluginTest" / "header_rewrite" / "rules" + + +def _collect_autest_pairs() -> list[pytest.param]: + """Collect .conf/.hrw4u pairs from the autest rules directory.""" + if not AUTEST_RULES_DIR.is_dir(): + return [] + + pairs = [] + for conf in sorted(AUTEST_RULES_DIR.glob("*.conf")): + hrw4u = conf.with_suffix(".hrw4u") + if hrw4u.exists(): + pairs.append(pytest.param(conf, hrw4u, id=conf.stem)) + + return pairs + + +@pytest.mark.autest +@pytest.mark.parametrize("conf_file,hrw4u_file", _collect_autest_pairs()) +def test_hrw4u_to_conf(conf_file: Path, hrw4u_file: Path) -> None: + """Test that hrw4u -> conf output matches the .conf file.""" + text = hrw4u_file.read_text() + lexer = hrw4uLexer(InputStream(text)) + stream = CommonTokenStream(lexer) + parser = hrw4uParser(stream) + tree = parser.program() + + ec = ErrorCollector(max_errors=10) + visitor = HRW4UVisitor(filename=str(hrw4u_file), error_collector=ec) + result = visitor.visit(tree) + + assert not ec.has_errors(), f"hrw4u errors in {hrw4u_file.name}:\n{ec.get_error_summary()}" + assert result is not None, f"hrw4u produced no output for {hrw4u_file.name}" + + expected = conf_file.read_text().strip() + actual = '\n'.join(result).strip() + + assert actual == expected, (f"hrw4u output for {hrw4u_file.name} does not match {conf_file.name}") diff --git a/tools/hrw4u/tests/test_autest_rules_reverse.py b/tools/hrw4u/tests/test_autest_rules_reverse.py new file mode 100644 index 00000000000..34bbfbdf2fc --- /dev/null +++ b/tools/hrw4u/tests/test_autest_rules_reverse.py @@ -0,0 +1,68 @@ +# +# 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 conf -> hrw4u conversion matches the autest .hrw4u files.""" +from __future__ import annotations + +from pathlib import Path + +import pytest +from antlr4 import CommonTokenStream, InputStream + +from u4wrh.u4wrhLexer import u4wrhLexer +from u4wrh.u4wrhParser import u4wrhParser +from u4wrh.hrw_visitor import HRWInverseVisitor +from hrw4u.errors import ErrorCollector + +AUTEST_RULES_DIR = Path( + __file__).resolve().parent.parent.parent.parent / "tests" / "gold_tests" / "pluginTest" / "header_rewrite" / "rules" + + +def _collect_autest_pairs() -> list[pytest.param]: + """Collect .conf/.hrw4u pairs from the autest rules directory.""" + if not AUTEST_RULES_DIR.is_dir(): + return [] + + pairs = [] + for conf in sorted(AUTEST_RULES_DIR.glob("*.conf")): + hrw4u = conf.with_suffix(".hrw4u") + if hrw4u.exists(): + pairs.append(pytest.param(conf, hrw4u, id=conf.stem)) + + return pairs + + +@pytest.mark.autest +@pytest.mark.parametrize("conf_file,hrw4u_file", _collect_autest_pairs()) +def test_conf_to_hrw4u(conf_file: Path, hrw4u_file: Path) -> None: + """Test that conf -> hrw4u output matches the .hrw4u file.""" + text = conf_file.read_text() + lexer = u4wrhLexer(InputStream(text)) + stream = CommonTokenStream(lexer) + parser = u4wrhParser(stream) + tree = parser.program() + + ec = ErrorCollector(max_errors=10) + visitor = HRWInverseVisitor(filename=str(conf_file), merge_sections=False, error_collector=ec) + result = visitor.visit(tree) + + assert not ec.has_errors(), f"u4wrh errors in {conf_file.name}:\n{ec.get_error_summary()}" + assert result is not None, f"u4wrh produced no output for {conf_file.name}" + + expected = hrw4u_file.read_text().strip() + actual = '\n'.join(result).strip() + + assert actual == expected, (f"u4wrh output for {conf_file.name} does not match {hrw4u_file.name}")