diff --git a/plugins/cachekey/pattern.cc b/plugins/cachekey/pattern.cc index 515cd8f4f0b..6e2baf3492e 100644 --- a/plugins/cachekey/pattern.cc +++ b/plugins/cachekey/pattern.cc @@ -152,7 +152,7 @@ Pattern::process(const String &subject, StringVector &result) } else { /* Replacement was not provided so return all capturing groups except the group zero. */ StringVector captures; - if (capture(subject, captures)) { + if (capture(subject, captures) && !captures.empty()) { if (captures.size() == 1) { result.push_back(captures[0]); } else { @@ -210,7 +210,7 @@ Pattern::capture(const String &subject, StringVector &result) return false; } - RegexMatches matches; + RegexMatches matches(_re.get_capture_count() + 1); int matchCount = _re.exec(subject, matches, RE_NOTEMPTY); if (matchCount < 0) { if (matchCount != RE_ERROR_NOMATCH) { @@ -219,7 +219,7 @@ Pattern::capture(const String &subject, StringVector &result) return false; } - for (int i = 0; i < matchCount; i++) { + for (int i = 0; i < matches.size(); i++) { std::string_view capture = matches[i]; String dst(capture.data(), capture.length()); @@ -246,7 +246,7 @@ Pattern::replace(const String &subject, String &result) return false; } - RegexMatches matches; + RegexMatches matches(_re.get_capture_count() + 1); int matchCount = _re.exec(subject, matches, RE_NOTEMPTY); if (matchCount < 0) { if (matchCount != RE_ERROR_NOMATCH) { @@ -257,7 +257,7 @@ Pattern::replace(const String &subject, String &result) /* Verify the replacement has the right number of matching groups */ for (int i = 0; i < _tokenCount; i++) { - if (_tokens[i] >= matchCount) { + if (_tokens[i] >= matches.size()) { CacheKeyError("invalid reference in replacement string: $%d", _tokens[i]); return false; } diff --git a/tests/gold_tests/pluginTest/cachekey/cachekey_capture.test.py b/tests/gold_tests/pluginTest/cachekey/cachekey_capture.test.py new file mode 100644 index 00000000000..72451b04403 --- /dev/null +++ b/tests/gold_tests/pluginTest/cachekey/cachekey_capture.test.py @@ -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. + +Test.Summary = 'Verify cachekey patterns preserve all capture groups.' +Test.SkipUnless(Condition.PluginExists('cachekey.so'), Condition.PluginExists('xdebug.so')) +Test.ATSReplayTest(replay_file='capture.replay.yaml') diff --git a/tests/gold_tests/pluginTest/cachekey/capture.replay.yaml b/tests/gold_tests/pluginTest/cachekey/capture.replay.yaml new file mode 100644 index 00000000000..0808d6bff71 --- /dev/null +++ b/tests/gold_tests/pluginTest/cachekey/capture.replay.yaml @@ -0,0 +1,203 @@ +# 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. + +meta: + version: '1.0' + +autest: + description: 'Cachekey captures beyond the default regex match capacity' + server: + name: server + client: + name: client + ats: + name: ts + records_config: + proxy.config.diags.debug.enabled: 1 + proxy.config.diags.debug.tags: cachekey + plugin_config: + - 'xdebug.so --enable=x-cache-key' + remap_config: + - from: 'http://nine.example.com/' + to: 'http://127.0.0.1:{SERVER_HTTP_PORT}/' + plugins: + - name: cachekey.so + args: + - '--static-prefix=capture' + - '--capture-path=(a)(b)(c)(d)(e)(f)(g)(h)(i)' + - from: 'http://ten.example.com/' + to: 'http://127.0.0.1:{SERVER_HTTP_PORT}/' + plugins: + - name: cachekey.so + args: + - '--static-prefix=capture' + - '--capture-path=(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)' + - from: 'http://twelve.example.com/' + to: 'http://127.0.0.1:{SERVER_HTTP_PORT}/' + plugins: + - name: cachekey.so + args: + - '--static-prefix=capture' + - '--capture-path=(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)' + - from: 'http://replace.example.com/' + to: 'http://127.0.0.1:{SERVER_HTTP_PORT}/' + plugins: + - name: cachekey.so + args: + - '--static-prefix=capture' + - '--capture-path=/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)/$9$1/' + - from: 'http://whole.example.com/' + to: 'http://127.0.0.1:{SERVER_HTTP_PORT}/' + plugins: + - name: cachekey.so + args: + - '--static-prefix=capture' + - '--capture-path=abcdef' + - from: 'http://no-match.example.com/' + to: 'http://127.0.0.1:{SERVER_HTTP_PORT}/' + plugins: + - name: cachekey.so + args: + - '--static-prefix=capture' + - '--capture-path=(z)' + +sessions: +- transactions: + - client-request: + method: GET + url: /abcdefghi + version: '1.1' + headers: + fields: + - [Host, nine.example.com] + - [uuid, nine] + - [X-Debug, x-cache-key] + server-response: + status: 200 + reason: OK + headers: + fields: + - [Content-Length, 0] + proxy-response: + status: 200 + headers: + fields: + - [X-Cache-Key, {value: '/capture/a/b/c/d/e/f/g/h/i', as: equal}] + + - client-request: + method: GET + url: /abcdefghij + version: '1.1' + headers: + fields: + - [Host, ten.example.com] + - [uuid, ten] + - [X-Debug, x-cache-key] + server-response: + status: 200 + reason: OK + headers: + fields: + - [Content-Length, 0] + proxy-response: + status: 200 + headers: + fields: + - [X-Cache-Key, {value: '/capture/a/b/c/d/e/f/g/h/i/j', as: equal}] + + - client-request: + method: GET + url: /abcdefghijkl + version: '1.1' + headers: + fields: + - [Host, twelve.example.com] + - [uuid, twelve] + - [X-Debug, x-cache-key] + server-response: + status: 200 + reason: OK + headers: + fields: + - [Content-Length, 0] + proxy-response: + status: 200 + headers: + fields: + - [X-Cache-Key, {value: '/capture/a/b/c/d/e/f/g/h/i/j/k/l', as: equal}] + + - client-request: + method: GET + url: /abcdefghijkl + version: '1.1' + headers: + fields: + - [Host, replace.example.com] + - [uuid, replace] + - [X-Debug, x-cache-key] + server-response: + status: 200 + reason: OK + headers: + fields: + - [Content-Length, 0] + proxy-response: + status: 200 + headers: + fields: + - [X-Cache-Key, {value: '/capture/ia', as: equal}] + + - client-request: + method: GET + url: /abcdef + version: '1.1' + headers: + fields: + - [Host, whole.example.com] + - [uuid, whole] + - [X-Debug, x-cache-key] + server-response: + status: 200 + reason: OK + headers: + fields: + - [Content-Length, 0] + proxy-response: + status: 200 + headers: + fields: + - [X-Cache-Key, {value: '/capture/abcdef', as: equal}] + + - client-request: + method: GET + url: /abcdef + version: '1.1' + headers: + fields: + - [Host, no-match.example.com] + - [uuid, no-match] + - [X-Debug, x-cache-key] + server-response: + status: 200 + reason: OK + headers: + fields: + - [Content-Length, 0] + proxy-response: + status: 200 + headers: + fields: + - [X-Cache-Key, {value: '/capture', as: equal}]