This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.2.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 48b5772b92469e86504d86f20710240882455137 Author: Leif Hedstrom <[email protected]> AuthorDate: Sat Feb 21 00:54:14 2026 -0700 hrw: fix lost [OR] modifiers, add more autests (#12904) (cherry picked from commit 29b0e93062cf7a45ad6a55958c8551a9cc95a39b) --- plugins/header_rewrite/condition.h | 6 + plugins/header_rewrite/header_rewrite.cc | 5 +- .../header_rewrite_bundle.replay.yaml | 669 +++++++++++++++++++++ .../header_rewrite/rules/complex_logics.conf | 158 +++++ 4 files changed, 836 insertions(+), 2 deletions(-) diff --git a/plugins/header_rewrite/condition.h b/plugins/header_rewrite/condition.h index 1404a747b6..1400f3433a 100644 --- a/plugins/header_rewrite/condition.h +++ b/plugins/header_rewrite/condition.h @@ -85,6 +85,12 @@ public: return _mods; } + void + set_mods(CondModifiers mods) + { + _mods = mods; + } + // Setters virtual void set_qualifier(const std::string &q) diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc index 9378f6022a..09ab73a74c 100644 --- a/plugins/header_rewrite/header_rewrite.cc +++ b/plugins/header_rewrite/header_rewrite.cc @@ -329,8 +329,9 @@ RulesConfig::parse_config(const std::string &fname, TSHttpHookID default_hook, c if (group_stack.empty()) { throw std::runtime_error("unmatched %{GROUP}"); } else { - delete cond; // We don't care about the closing group condition, it's a no-op - ngrp = group; + ngrp = group; + ngrp->set_mods(cond->mods()); + delete cond; group = group_stack.top(); group_stack.pop(); group->add_condition(ngrp); // Add the previous group to the current group's conditions diff --git a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml index ea1c966185..8e6e9a995c 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml +++ b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml @@ -155,6 +155,13 @@ autest: args: - "rules/run_plugin.conf" + - from: "http://www.example.com/from_15/" + to: "http://backend.ex:{SERVER_HTTP_PORT}/to_15/" + plugins: + - name: "header_rewrite.so" + args: + - "rules/complex_logics.conf" + log_validation: traffic_out: excludes: @@ -1114,3 +1121,665 @@ sessions: proxy-response: status: 200 + +# ========================================================================== +# Tests 31-52: Complex GROUP logic tests (rules/complex_logics.conf) +# ========================================================================== + +# Test 31: GROUP [OR] - only A header present (should match via group) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-or + headers: + fields: + - [ Host, www.example.com ] + - [ X-Group-A, "yes" ] + - [ uuid, 37 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-Or-Result, { value: "matched", as: equal } ] + +# Test 32: GROUP [OR] - only B header present (should match via OR) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-or + headers: + fields: + - [ Host, www.example.com ] + - [ X-Group-B, "yes" ] + - [ uuid, 38 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-Or-Result, { value: "matched", as: equal } ] + +# Test 33: GROUP [OR] - neither header present (should not match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-or + headers: + fields: + - [ Host, www.example.com ] + - [ uuid, 39 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-Or-Result, { as: absent } ] + +# Test 34: GROUP [AND] - both headers present (should match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-and + headers: + fields: + - [ Host, www.example.com ] + - [ X-And-A, "yes" ] + - [ X-And-B, "yes" ] + - [ uuid, 40 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-And-Result, { value: "matched", as: equal } ] + +# Test 35: GROUP [AND] - only A present (should not match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-and + headers: + fields: + - [ Host, www.example.com ] + - [ X-And-A, "yes" ] + - [ uuid, 41 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-And-Result, { as: absent } ] + +# Test 36: GROUP [NOT] - neither header present -> group=(false) -> NOT(false)=true +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-not + headers: + fields: + - [ Host, www.example.com ] + - [ uuid, 42 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-Not-Result, { value: "matched", as: equal } ] + +# Test 37: GROUP [NOT] - both headers present -> group=(true) -> NOT(true)=false +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-not + headers: + fields: + - [ Host, www.example.com ] + - [ X-Not-A, "yes" ] + - [ X-Not-B, "yes" ] + - [ uuid, 43 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-Not-Result, { as: absent } ] + +# Test 38: Nested GROUP - outer + inner-A present (should match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/nested-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Outer-A, "yes" ] + - [ X-Inner-A, "yes" ] + - [ uuid, 44 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Nested-Group-Result, { value: "matched", as: equal } ] + +# Test 39: Nested GROUP - outer + inner-B present (should match via inner OR) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/nested-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Outer-A, "yes" ] + - [ X-Inner-B, "yes" ] + - [ uuid, 45 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Nested-Group-Result, { value: "matched", as: equal } ] + +# Test 40: Nested GROUP - no outer header (should not match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/nested-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Inner-A, "yes" ] + - [ uuid, 46 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Nested-Group-Result, { as: absent } ] + +# Test 41: Two GROUPs with OR - left group matches +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/two-groups + headers: + fields: + - [ Host, www.example.com ] + - [ X-Left-A, "yes" ] + - [ X-Left-B, "yes" ] + - [ uuid, 47 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Two-Groups-Result, { value: "matched", as: equal } ] + +# Test 42: Two GROUPs with OR - right group matches +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/two-groups + headers: + fields: + - [ Host, www.example.com ] + - [ X-Right-A, "yes" ] + - [ X-Right-B, "yes" ] + - [ uuid, 48 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Two-Groups-Result, { value: "matched", as: equal } ] + +# Test 43: Two GROUPs with OR - neither group matches (partial left) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/two-groups + headers: + fields: + - [ Host, www.example.com ] + - [ X-Left-A, "yes" ] + - [ X-Right-B, "yes" ] + - [ uuid, 49 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Two-Groups-Result, { as: absent } ] + +# Test 44: GROUP inside if - alpha branch with X-Sub-A (should match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/if-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Branch, "alpha" ] + - [ X-Sub-A, "yes" ] + - [ uuid, 50 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-If-Group-Result, { value: "alpha", as: equal } ] + +# Test 45: GROUP inside if - alpha branch without sub headers (falls to else) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/if-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Branch, "alpha" ] + - [ uuid, 51 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-If-Group-Result, { value: "other", as: equal } ] + +# Test 46: GROUP inside if - beta branch +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/if-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Branch, "beta" ] + - [ uuid, 52 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-If-Group-Result, { value: "beta", as: equal } ] + +# Test 47: Nested if with GROUP OR - outer yes, case-A present +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/nested-if-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Outer, "yes" ] + - [ X-Case-A, "yes" ] + - [ uuid, 53 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Nested-If-Result, { value: "inner-match", as: equal } ] + +# Test 48: Nested if with GROUP OR - outer yes, neither case header +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/nested-if-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Outer, "yes" ] + - [ uuid, 54 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Nested-If-Result, { value: "inner-miss", as: equal } ] + +# Test 49: Nested if with GROUP OR - outer miss +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/nested-if-group + headers: + fields: + - [ Host, www.example.com ] + - [ X-Outer, "no" ] + - [ uuid, 55 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Nested-If-Result, { value: "outer-miss", as: equal } ] + +# Test 50: Complex (A OR B) AND (C OR D) - P+R present (should match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/complex-and-or + headers: + fields: + - [ Host, www.example.com ] + - [ X-P, "yes" ] + - [ X-R, "yes" ] + - [ uuid, 56 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Complex-Result, { value: "matched", as: equal } ] + +# Test 51: Complex (A OR B) AND (C OR D) - Q+S present (should match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/complex-and-or + headers: + fields: + - [ Host, www.example.com ] + - [ X-Q, "yes" ] + - [ X-S, "yes" ] + - [ uuid, 57 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Complex-Result, { value: "matched", as: equal } ] + +# Test 52: Complex (A OR B) AND (C OR D) - only P present (first group +# matches but second doesn't, should not match) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/complex-and-or + headers: + fields: + - [ Host, www.example.com ] + - [ X-P, "yes" ] + - [ uuid, 58 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Complex-Result, { as: absent } ] + +# Test 53: IP:CLIENT in GROUP with [OR] - IP matches (client is 127.0.0.1) +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/ip-group-or + headers: + fields: + - [ Host, www.example.com ] + - [ uuid, 59 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Ip-Group-Or-Result, { value: "matched", as: equal } ] + +# Test 54: IP:CLIENT in GROUP with [OR] - header X-Outer matches too +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/ip-group-or + headers: + fields: + - [ Host, www.example.com ] + - [ X-Outer, "true" ] + - [ uuid, 60 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Ip-Group-Or-Result, { value: "matched", as: equal } ] + +# Test 55: GROUP as first condition after hook - X-First-A present, should match +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-first + headers: + fields: + - [ Host, www.example.com ] + - [ X-First-A, "yes" ] + - [ uuid, 61 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-First-Result, { value: "matched", as: equal } ] + +# Test 56: GROUP as first condition after hook - no X-First-A, should not match +- transactions: + - client-request: + method: "GET" + version: "1.1" + url: /from_15/logic/group-first + headers: + fields: + - [ Host, www.example.com ] + - [ uuid, 62 ] + + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Connection, close ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Group-First-Result, { as: absent } ] diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf new file mode 100644 index 0000000000..b4f4b92518 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/complex_logics.conf @@ -0,0 +1,158 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# All rules use SEND_RESPONSE_HDR_HOOK so set-header operates on the +# response that the client sees. CLIENT-HEADER and CLIENT-URL:PATH +# conditions always refer to the original client request regardless of hook. + +# ---- Test: GROUP with [OR] on GROUP:END ---- +# hrw4u: if (inbound.req.X-Group-A) || inbound.req.X-Group-B +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/group-or$/ [AND] +cond %{GROUP} + cond %{CLIENT-HEADER:X-Group-A} ="" [NOT] +cond %{GROUP:END} [OR] +cond %{CLIENT-HEADER:X-Group-B} ="" [NOT] + set-header X-Group-Or-Result "matched" + +# ---- Test: GROUP with [AND] on GROUP:END (explicit) ---- +# hrw4u: if (inbound.req.X-And-A) && inbound.req.X-And-B +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/group-and$/ [AND] +cond %{GROUP} + cond %{CLIENT-HEADER:X-And-A} ="" [NOT] +cond %{GROUP:END} [AND] +cond %{CLIENT-HEADER:X-And-B} ="" [NOT] + set-header X-Group-And-Result "matched" + +# ---- Test: GROUP with [NOT] on GROUP:END ---- +# hrw4u: if !(inbound.req.X-Not-A && inbound.req.X-Not-B) +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/group-not$/ [AND] +cond %{GROUP} + cond %{CLIENT-HEADER:X-Not-A} ="" [NOT] + cond %{CLIENT-HEADER:X-Not-B} ="" [NOT] +cond %{GROUP:END} [NOT] + set-header X-Group-Not-Result "matched" + +# ---- Test: Nested GROUPs with mixed OR/AND ---- +# hrw4u: if inbound.req.X-Outer-A && (inbound.req.X-Inner-A || inbound.req.X-Inner-B) +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/nested-group$/ [AND] +cond %{CLIENT-HEADER:X-Outer-A} ="" [NOT] +cond %{GROUP} + cond %{CLIENT-HEADER:X-Inner-A} ="" [NOT,OR] + cond %{CLIENT-HEADER:X-Inner-B} ="" [NOT] +cond %{GROUP:END} + set-header X-Nested-Group-Result "matched" + +# ---- Test: Two GROUPs with OR between them ---- +# hrw4u: if (inbound.req.X-Left-A && inbound.req.X-Left-B) || (inbound.req.X-Right-A && inbound.req.X-Right-B) +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/two-groups$/ [AND] +cond %{GROUP} + cond %{CLIENT-HEADER:X-Left-A} ="" [NOT] + cond %{CLIENT-HEADER:X-Left-B} ="" [NOT] +cond %{GROUP:END} [OR] +cond %{GROUP} + cond %{CLIENT-HEADER:X-Right-A} ="" [NOT] + cond %{CLIENT-HEADER:X-Right-B} ="" [NOT] +cond %{GROUP:END} + set-header X-Two-Groups-Result "matched" + +# ---- Test: GROUP inside if/elif/else ---- +# hrw4u: +# if inbound.req.X-Branch == "alpha" && (inbound.req.X-Sub-A || inbound.req.X-Sub-B) +# -> "alpha" +# elif inbound.req.X-Branch == "beta" +# -> "beta" +# else +# -> "other" +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/if-group$/ [AND] + if + cond %{CLIENT-HEADER:X-Branch} ="alpha" + cond %{GROUP} + cond %{CLIENT-HEADER:X-Sub-A} ="" [NOT,OR] + cond %{CLIENT-HEADER:X-Sub-B} ="" [NOT] + cond %{GROUP:END} + set-header X-If-Group-Result "alpha" + elif + cond %{CLIENT-HEADER:X-Branch} ="beta" + set-header X-If-Group-Result "beta" + else + set-header X-If-Group-Result "other" + endif + +# ---- Test: GROUP with OR inside nested if ---- +# hrw4u: +# if inbound.req.X-Outer == "yes" +# if (inbound.req.X-Case-A || inbound.req.X-Case-B) +# -> "inner-match" +# else +# -> "inner-miss" +# else +# -> "outer-miss" +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/nested-if-group$/ [AND] + if + cond %{CLIENT-HEADER:X-Outer} ="yes" + if + cond %{GROUP} + cond %{CLIENT-HEADER:X-Case-A} ="" [NOT,OR] + cond %{CLIENT-HEADER:X-Case-B} ="" [NOT] + cond %{GROUP:END} + set-header X-Nested-If-Result "inner-match" + else + set-header X-Nested-If-Result "inner-miss" + endif + else + set-header X-Nested-If-Result "outer-miss" + endif + +# ---- Test: Complex expression: (A OR B) AND (C OR D) ---- +# hrw4u: if (inbound.req.X-P || inbound.req.X-Q) && (inbound.req.X-R || inbound.req.X-S) +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/complex-and-or$/ [AND] +cond %{GROUP} + cond %{CLIENT-HEADER:X-P} ="" [NOT,OR] + cond %{CLIENT-HEADER:X-Q} ="" [NOT] +cond %{GROUP:END} [AND] +cond %{GROUP} + cond %{CLIENT-HEADER:X-R} ="" [NOT,OR] + cond %{CLIENT-HEADER:X-S} ="" [NOT] +cond %{GROUP:END} + set-header X-Complex-Result "matched" + +# ---- Test: IP:CLIENT in GROUP with [OR] +# hrw4u: if (inbound.ip in {127.0.0.0/8}) || inbound.req.X-Outer == "true" +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{CLIENT-URL:PATH} /\/logic\/ip-group-or$/ [AND] +cond %{GROUP} + cond %{IP:CLIENT} {127.0.0.0/8} +cond %{GROUP:END} [OR] +cond %{CLIENT-HEADER:X-Outer} ="true" + set-header X-Ip-Group-Or-Result "matched" + +# ---- Test: GROUP as first condition after hook ---- +# hrw4u: if (inbound.url.path ~ /\/logic\/group-first$/) && inbound.req.X-First-A +cond %{SEND_RESPONSE_HDR_HOOK} [AND] +cond %{GROUP} + cond %{CLIENT-URL:PATH} /\/logic\/group-first$/ +cond %{GROUP:END} [AND] +cond %{CLIENT-HEADER:X-First-A} ="" [NOT] + set-header X-Group-First-Result "matched"
