From 8d72dd3f61802c1d8f10cafde4f779ee8c587e3e Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Thu, 13 Feb 2020 09:03:26 -0500 Subject: [PATCH 01/13] Implement new regex_rewrite route action. Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 45 ++++++++ .../config/route/v3/route_components.proto | 48 ++++++++ .../http/http_conn_man/headers.rst | 3 +- .../http/http_filters/router_filter.rst | 3 +- .../intro/arch_overview/http/http_routing.rst | 1 + docs/root/intro/version_history.rst | 2 + .../envoy/api/v2/route/route_components.proto | 45 ++++++++ .../config/route/v3/route_components.proto | 48 ++++++++ include/envoy/common/regex.h | 15 ++- source/common/common/regex.cc | 13 +++ source/common/router/config_impl.cc | 40 +++++-- source/common/router/config_impl.h | 2 + test/common/router/config_impl_test.cc | 107 ++++++++++++++++++ 13 files changed, 358 insertions(+), 14 deletions(-) diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index f5e6bae79a35a..e2545df0437d5 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -765,6 +765,51 @@ message RouteAction { // requests to */prefix/etc* will be stripped to */etc*. string prefix_rewrite = 5; + // Indicates that during forwarding, portions of the path that match the + // pattern should be rewritten, even allowing the substitution of capture + // groups from the pattern into the new path as specified by the rewrite + // substitution string. This is useful to allow application paths to be + // rewritten in a way that is aware of segments with variable content like + // identifiers. The router filter will place the original path as it was + // before the rewrite into the :ref:`x-envoy-original-path + // ` header. + // + // Examples: + // + // * The path pattern ^/service/([^/]+)(/.*)$ paired with a substitution + // string of \2/instance/\1 would transform */service/foo/v1/api* into + // */v1/api/instance/foo*. + // + // * The pattern *one* paired with a substitution string of *two* would + // transform */xxx/one/yyy/one/zzz* into */xxx/two/yyy/two/zzz*. + // + // * The pattern ^(.*?)one(.*)$ paired with a substitution string of + // \1two\2 would replace only the first occurrence of "one", + // transforming path */xxx/one/yyy/one/zzz* into */xxx/two/yyy/one/zzz*. + // + // * The pattern (?i)/xxx/ paired with a substitution string of /yyy/ would + // do a case-insensitive match and transform path /aaa/XxX/bbb to /aaa/yyy/bbb. + RegexRewrite regex_rewrite = 32; + + message RegexRewrite { + // The regular expression used to find portions of the path (excluding + // query parameters) that should be replaced. All matches in the path are + // replaced by the substitution string, though anchors in the regular + // expression can be used to replace just one occurrence of a pattern. + // Capture groups can be used in the pattern to extract portions of the + // path, and referenced in the substitution string. + type.matcher.RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the path. + // Capture groups in the pattern can be referenced in this string Beware, + // however, that the syntax for referring to capture groups might vary + // among the regular expression engines. Google's `RE2 `_ + // regular expression engine uses backslash followed by the capture group + // number to denote a numbered capture group. E.g., \1 refers to capture + // group 1, and \2 refers to capture group 2. + string substitution = 2; + } + oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with // this value. diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 2fde356ea6f7e..5c602e2e5032e 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -738,6 +738,54 @@ message RouteAction { // requests to */prefix/etc* will be stripped to */etc*. string prefix_rewrite = 5; + // Indicates that during forwarding, portions of the path that match the + // pattern should be rewritten, even allowing the substitution of capture + // groups from the pattern into the new path as specified by the rewrite + // substitution string. This is useful to allow application paths to be + // rewritten in a way that is aware of segments with variable content like + // identifiers. The router filter will place the original path as it was + // before the rewrite into the :ref:`x-envoy-original-path + // ` header. + // + // Examples: + // + // * The path pattern ^/service/([^/]+)(/.*)$ paired with a substitution + // string of \2/instance/\1 would transform */service/foo/v1/api* into + // */v1/api/instance/foo*. + // + // * The pattern *one* paired with a substitution string of *two* would + // transform */xxx/one/yyy/one/zzz* into */xxx/two/yyy/two/zzz*. + // + // * The pattern ^(.*?)one(.*)$ paired with a substitution string of + // \1two\2 would replace only the first occurrence of "one", + // transforming path */xxx/one/yyy/one/zzz* into */xxx/two/yyy/one/zzz*. + // + // * The pattern (?i)/xxx/ paired with a substitution string of /yyy/ would + // do a case-insensitive match and transform path /aaa/XxX/bbb to /aaa/yyy/bbb. + RegexRewrite regex_rewrite = 32; + + message RegexRewrite { + option (udpa.annotations.versioning).previous_message_type = + "envoy.api.v2.route.RouteAction.RegexRewrite"; + + // The regular expression used to find portions of the path (excluding + // query parameters) that should be replaced. All matches in the path are + // replaced by the substitution string, though anchors in the regular + // expression can be used to replace just one occurrence of a pattern. + // Capture groups can be used in the pattern to extract portions of the + // path, and referenced in the substitution string. + type.matcher.v3.RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the path. + // Capture groups in the pattern can be referenced in this string Beware, + // however, that the syntax for referring to capture groups might vary + // among the regular expression engines. Google's `RE2 `_ + // regular expression engine uses backslash followed by the capture group + // number to denote a numbered capture group. E.g., \1 refers to capture + // group 1, and \2 refers to capture group 2. + string substitution = 2; + } + oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with // this value. diff --git a/docs/root/configuration/http/http_conn_man/headers.rst b/docs/root/configuration/http/http_conn_man/headers.rst index 077cd0b30ab1c..7041772752347 100644 --- a/docs/root/configuration/http/http_conn_man/headers.rst +++ b/docs/root/configuration/http/http_conn_man/headers.rst @@ -471,7 +471,8 @@ route, virtual host, and/or global route configuration level. See the No *:-prefixed* pseudo-header may be modified via this mechanism. The *:path* and *:authority* headers may instead be modified via mechanisms such as -:ref:`prefix_rewrite ` and +:ref:`prefix_rewrite `, +:ref:`regex_rewrite `, and :ref:`host_rewrite `. Headers are appended to requests/responses in the following order: weighted cluster level headers, diff --git a/docs/root/configuration/http/http_filters/router_filter.rst b/docs/root/configuration/http/http_filters/router_filter.rst index a4e8dd28aa5d0..8eadcdeace253 100644 --- a/docs/root/configuration/http/http_filters/router_filter.rst +++ b/docs/root/configuration/http/http_filters/router_filter.rst @@ -341,7 +341,8 @@ responses. x-envoy-original-path ^^^^^^^^^^^^^^^^^^^^^ -If the route utilizes :ref:`prefix_rewrite `, +If the route utilizes :ref:`prefix_rewrite ` +or :ref:`regex_rewrite `, Envoy will put the original path header in this header. This can be useful for logging and debugging. diff --git a/docs/root/intro/arch_overview/http/http_routing.rst b/docs/root/intro/arch_overview/http/http_routing.rst index b70190ab26f72..376b5585b0899 100644 --- a/docs/root/intro/arch_overview/http/http_routing.rst +++ b/docs/root/intro/arch_overview/http/http_routing.rst @@ -30,6 +30,7 @@ request. The router filter supports the following features: * :ref:`Automatic host rewriting ` based on the DNS name of the selected upstream host. * :ref:`Prefix rewriting `. +* :ref:`Path rewriting using a regular expression and capture groups `. * :ref:`Request retries ` specified either via HTTP header or via route configuration. * Request timeout specified either via :ref:`HTTP diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index 387cc502ac4a9..2ac9d0165819d 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -30,6 +30,8 @@ Version history restart Envoy. The behavior will not switch until the connection pools are recreated. The new circuit breaker behavior is described :ref:`here `. * upstream: changed load distribution algorithm when all priorities enter :ref:`panic mode`. +* router: added support for :ref:`regex_rewrite + ` for path rewriting using regular expressions and capture groups. 1.13.0 (January 20, 2020) ========================= diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index f5e6bae79a35a..e2545df0437d5 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -765,6 +765,51 @@ message RouteAction { // requests to */prefix/etc* will be stripped to */etc*. string prefix_rewrite = 5; + // Indicates that during forwarding, portions of the path that match the + // pattern should be rewritten, even allowing the substitution of capture + // groups from the pattern into the new path as specified by the rewrite + // substitution string. This is useful to allow application paths to be + // rewritten in a way that is aware of segments with variable content like + // identifiers. The router filter will place the original path as it was + // before the rewrite into the :ref:`x-envoy-original-path + // ` header. + // + // Examples: + // + // * The path pattern ^/service/([^/]+)(/.*)$ paired with a substitution + // string of \2/instance/\1 would transform */service/foo/v1/api* into + // */v1/api/instance/foo*. + // + // * The pattern *one* paired with a substitution string of *two* would + // transform */xxx/one/yyy/one/zzz* into */xxx/two/yyy/two/zzz*. + // + // * The pattern ^(.*?)one(.*)$ paired with a substitution string of + // \1two\2 would replace only the first occurrence of "one", + // transforming path */xxx/one/yyy/one/zzz* into */xxx/two/yyy/one/zzz*. + // + // * The pattern (?i)/xxx/ paired with a substitution string of /yyy/ would + // do a case-insensitive match and transform path /aaa/XxX/bbb to /aaa/yyy/bbb. + RegexRewrite regex_rewrite = 32; + + message RegexRewrite { + // The regular expression used to find portions of the path (excluding + // query parameters) that should be replaced. All matches in the path are + // replaced by the substitution string, though anchors in the regular + // expression can be used to replace just one occurrence of a pattern. + // Capture groups can be used in the pattern to extract portions of the + // path, and referenced in the substitution string. + type.matcher.RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the path. + // Capture groups in the pattern can be referenced in this string Beware, + // however, that the syntax for referring to capture groups might vary + // among the regular expression engines. Google's `RE2 `_ + // regular expression engine uses backslash followed by the capture group + // number to denote a numbered capture group. E.g., \1 refers to capture + // group 1, and \2 refers to capture group 2. + string substitution = 2; + } + oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with // this value. diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 68a4c600aef88..701642fc0f192 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -812,6 +812,54 @@ message RouteAction { // requests to */prefix/etc* will be stripped to */etc*. string prefix_rewrite = 5; + // Indicates that during forwarding, portions of the path that match the + // pattern should be rewritten, even allowing the substitution of capture + // groups from the pattern into the new path as specified by the rewrite + // substitution string. This is useful to allow application paths to be + // rewritten in a way that is aware of segments with variable content like + // identifiers. The router filter will place the original path as it was + // before the rewrite into the :ref:`x-envoy-original-path + // ` header. + // + // Examples: + // + // * The path pattern ^/service/([^/]+)(/.*)$ paired with a substitution + // string of \2/instance/\1 would transform */service/foo/v1/api* into + // */v1/api/instance/foo*. + // + // * The pattern *one* paired with a substitution string of *two* would + // transform */xxx/one/yyy/one/zzz* into */xxx/two/yyy/two/zzz*. + // + // * The pattern ^(.*?)one(.*)$ paired with a substitution string of + // \1two\2 would replace only the first occurrence of "one", + // transforming path */xxx/one/yyy/one/zzz* into */xxx/two/yyy/one/zzz*. + // + // * The pattern (?i)/xxx/ paired with a substitution string of /yyy/ would + // do a case-insensitive match and transform path /aaa/XxX/bbb to /aaa/yyy/bbb. + RegexRewrite regex_rewrite = 32; + + message RegexRewrite { + option (udpa.annotations.versioning).previous_message_type = + "envoy.api.v2.route.RouteAction.RegexRewrite"; + + // The regular expression used to find portions of the path (excluding + // query parameters) that should be replaced. All matches in the path are + // replaced by the substitution string, though anchors in the regular + // expression can be used to replace just one occurrence of a pattern. + // Capture groups can be used in the pattern to extract portions of the + // path, and referenced in the substitution string. + type.matcher.v3.RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the path. + // Capture groups in the pattern can be referenced in this string Beware, + // however, that the syntax for referring to capture groups might vary + // among the regular expression engines. Google's `RE2 `_ + // regular expression engine uses backslash followed by the capture group + // number to denote a numbered capture group. E.g., \1 refers to capture + // group 1, and \2 refers to capture group 2. + string substitution = 2; + } + oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with // this value. diff --git a/include/envoy/common/regex.h b/include/envoy/common/regex.h index f4cdc1699ef70..df71729c17db4 100644 --- a/include/envoy/common/regex.h +++ b/include/envoy/common/regex.h @@ -9,11 +9,18 @@ namespace Regex { /** * A compiled regex expression matcher which uses an abstract regex engine. - * - * NOTE: Currently this is the same as StringMatcher, however has been split out as in the future - * we are likely to add other methods such as returning captures, etc. */ -class CompiledMatcher : public Matchers::StringMatcher {}; +class CompiledMatcher : public Matchers::StringMatcher { +public: + /** + * Replaces all non-overlapping occurrences of the pattern in "value" with + * "substitution". The "substitution" string can make references to + * capture groups in the pattern, using the syntax specific to that + * regular expression engine. + */ + virtual std::string replaceAll(absl::string_view value, + absl::string_view substitution) const PURE; +}; using CompiledMatcherPtr = std::unique_ptr; diff --git a/source/common/common/regex.cc b/source/common/common/regex.cc index b3ce6ed8f570c..0bb699692ac96 100644 --- a/source/common/common/regex.cc +++ b/source/common/common/regex.cc @@ -22,6 +22,11 @@ class CompiledStdMatcher : public CompiledMatcher { return std::regex_match(value.begin(), value.end(), regex_); } + // CompiledMatcher + std::string replaceAll(absl::string_view value, absl::string_view substitution) const override { + return std::regex_replace(std::string(value), regex_, std::string(substitution)); + } + private: const std::regex regex_; }; @@ -48,6 +53,14 @@ class CompiledGoogleReMatcher : public CompiledMatcher { return re2::RE2::FullMatch(re2::StringPiece(value.data(), value.size()), regex_); } + // CompiledMatcher + std::string replaceAll(absl::string_view value, absl::string_view substitution) const override { + std::string result = std::string(value); + re2::RE2::GlobalReplace(&result, regex_, + re2::StringPiece(substitution.data(), substitution.size())); + return result; + } + private: const re2::RE2 regex_; }; diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index d562f8e4fe411..f4a0c58549fa9 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -379,6 +379,12 @@ RouteEntryImplBase::RouteEntryImplBase(const VirtualHostImpl& vhost, throw EnvoyException(absl::StrCat("Duplicate upgrade ", upgrade_config.upgrade_type())); } } + + if (route.route().has_regex_rewrite()) { + auto rewrite_spec = route.route().regex_rewrite(); + regex_rewrite_ = Regex::Utility::parseRegex(rewrite_spec.pattern()); + regex_rewrite_substitution_ = rewrite_spec.substitution(); + } } bool RouteEntryImplBase::evaluateRuntimeMatch(const uint64_t random_value) const { @@ -476,7 +482,7 @@ void RouteEntryImplBase::finalizeRequestHeaders(Http::HeaderMap& headers, } // Handle path rewrite - if (!getPathRewrite().empty()) { + if (!getPathRewrite().empty() || regex_rewrite_ != nullptr) { rewritePathHeader(headers, insert_envoy_original_path); } } @@ -511,21 +517,39 @@ RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& return runtime; } +// finalizePathHeaders does the "standard" path rewriting, meaning that it +// handles the "prefix_rewrite" and "regex_rewrite" route actions. If +// "prefix_rewrite" is specified, it takes precedence. The "matched_path" +// argument applies only to the prefix rewriting, and describes the portion of +// the path (excluding query parms) that should be replaced by the rewrite. A +// "regex_rewrite" applies to the entire path, regardless of what portion was +// matched. void RouteEntryImplBase::finalizePathHeader(Http::HeaderMap& headers, absl::string_view matched_path, bool insert_envoy_original_path) const { const auto& rewrite = getPathRewrite(); - if (rewrite.empty()) { + if (!rewrite.empty()) { + std::string path(headers.Path()->value().getStringView()); + if (insert_envoy_original_path) { + headers.setEnvoyOriginalPath(path); + } + ASSERT(case_sensitive_ ? absl::StartsWith(path, matched_path) + : absl::StartsWithIgnoreCase(path, matched_path)); + headers.setPath(path.replace(0, matched_path.size(), rewrite)); return; } - std::string path(headers.Path()->value().getStringView()); - if (insert_envoy_original_path) { - headers.setEnvoyOriginalPath(path); + if (regex_rewrite_ != nullptr) { + std::string path(headers.Path()->value().getStringView()); + if (insert_envoy_original_path) { + headers.setEnvoyOriginalPath(path); + } + // Replace the entire path, but preserve the query parms + auto just_path(Http::PathUtil::removeQueryAndFragment(path)); + headers.setPath(path.replace( + 0, just_path.size(), regex_rewrite_->replaceAll(just_path, regex_rewrite_substitution_))); + return; } - ASSERT(case_sensitive_ ? absl::StartsWith(path, matched_path) - : absl::StartsWithIgnoreCase(path, matched_path)); - headers.setPath(path.replace(0, matched_path.size(), rewrite)); } absl::string_view RouteEntryImplBase::processRequestHost(const Http::HeaderMap& headers, diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index b9559b66efc8a..9d41e352353c0 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -472,6 +472,8 @@ class RouteEntryImplBase : public RouteEntry, protected: const bool case_sensitive_; const std::string prefix_rewrite_; + Regex::CompiledMatcherPtr regex_rewrite_; + std::string regex_rewrite_substitution_; const std::string host_rewrite_; bool include_vh_rate_limits_; diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index cbbe09b060963..42a1a1be0029d 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -447,6 +447,34 @@ TEST_F(RouteMatcherTest, TestRoutes) { route: prefix_rewrite: "/api/new_endpoint" cluster: www2 + - match: + prefix: "/newforreg1_endpoint" + route: + regex_rewrite: + pattern: + google_re2: {} + regex: "^/new(.*?)_endpoint(.*)$" + substitution: "/\\1_rewritten_endpoint\\2" + cluster: www2 + - match: + prefix: "/newforreg2_endpoint" + route: + regex_rewrite: + pattern: + google_re2: {} + regex: "e" + substitution: "X" + cluster: www2 + - match: + path: "/exact/path/for/regex1" + case_sensitive: true + route: + cluster: www2 + regex_rewrite: + pattern: + google_re2: {} + regex: "[aeioe]" + substitution: "V" - match: path: "/" route: @@ -504,6 +532,17 @@ TEST_F(RouteMatcherTest, TestRoutes) { route: cluster: three_numbers prefix_rewrite: "/rewrote" + - match: + safe_regex: + google_re2: {} + regex: ".*/\\d{4}$" + route: + cluster: four_numbers + regex_rewrite: + pattern: + google_re2: {} + regex: "(^.*)/(\\d{4})$" + substitution: "/four/\\2/endpoint\\1" - match: safe_regex: google_re2: {} @@ -781,6 +820,56 @@ TEST_F(RouteMatcherTest, TestRoutes) { EXPECT_EQ("/bar", headers.get_(Http::Headers::get().Path)); } + // Regular expression path rewrite after prefix match testing. + { + Http::TestHeaderMapImpl headers = genHeaders("www.lyft.com", "/newforreg1_endpoint/foo", "GET"); + const RouteEntry* route = config.route(headers, 0)->routeEntry(); + EXPECT_EQ("www2", route->clusterName()); + EXPECT_EQ("www2", virtualHostName(route)); + route->finalizeRequestHeaders(headers, stream_info, true); + EXPECT_EQ("/forreg1_rewritten_endpoint/foo", headers.get_(Http::Headers::get().Path)); + EXPECT_EQ("/newforreg1_endpoint/foo", headers.get_(Http::Headers::get().EnvoyOriginalPath)); + } + + // Regular expression path rewrite after prefix match testing, replace every + // occurrence, excluding query parms. + { + Http::TestHeaderMapImpl headers = + genHeaders("www.lyft.com", "/newforreg2_endpoint/tee?test=me", "GET"); + const RouteEntry* route = config.route(headers, 0)->routeEntry(); + EXPECT_EQ("www2", route->clusterName()); + EXPECT_EQ("www2", virtualHostName(route)); + route->finalizeRequestHeaders(headers, stream_info, true); + EXPECT_EQ("/nXwforrXg2_Xndpoint/tXX?test=me", headers.get_(Http::Headers::get().Path)); + EXPECT_EQ("/newforreg2_endpoint/tee?test=me", + headers.get_(Http::Headers::get().EnvoyOriginalPath)); + } + + // Regular expression path rewrite after exact path match testing. + { + Http::TestHeaderMapImpl headers = genHeaders("www.lyft.com", "/exact/path/for/regex1", "GET"); + const RouteEntry* route = config.route(headers, 0)->routeEntry(); + EXPECT_EQ("www2", route->clusterName()); + EXPECT_EQ("www2", virtualHostName(route)); + route->finalizeRequestHeaders(headers, stream_info, true); + EXPECT_EQ("/VxVct/pVth/fVr/rVgVx1", headers.get_(Http::Headers::get().Path)); + EXPECT_EQ("/exact/path/for/regex1", headers.get_(Http::Headers::get().EnvoyOriginalPath)); + } + + // Regular expression path rewrite after exact path match testing, + // with query parms. + { + Http::TestHeaderMapImpl headers = + genHeaders("www.lyft.com", "/exact/path/for/regex1?test=aeiou", "GET"); + const RouteEntry* route = config.route(headers, 0)->routeEntry(); + EXPECT_EQ("www2", route->clusterName()); + EXPECT_EQ("www2", virtualHostName(route)); + route->finalizeRequestHeaders(headers, stream_info, true); + EXPECT_EQ("/VxVct/pVth/fVr/rVgVx1?test=aeiou", headers.get_(Http::Headers::get().Path)); + EXPECT_EQ("/exact/path/for/regex1?test=aeiou", + headers.get_(Http::Headers::get().EnvoyOriginalPath)); + } + // Host rewrite testing. { Http::TestHeaderMapImpl headers = genHeaders("api.lyft.com", "/host/rewrite/me", "GET"); @@ -884,6 +973,24 @@ TEST_F(RouteMatcherTest, TestRoutes) { EXPECT_EQ("/rewrote?bar=true", headers.get_(Http::Headers::get().Path)); } + // Regular expression rewrite for regular expression matching + { + Http::TestHeaderMapImpl headers = genHeaders("bat.com", "/xx/yy/6472", "GET"); + const RouteEntry* route = config.route(headers, 0)->routeEntry(); + route->finalizeRequestHeaders(headers, stream_info, true); + EXPECT_EQ("/four/6472/endpoint/xx/yy", headers.get_(Http::Headers::get().Path)); + EXPECT_EQ("/xx/yy/6472", headers.get_(Http::Headers::get().EnvoyOriginalPath)); + } + + // Regular expression rewrite for regular expression matching, with query parms. + { + Http::TestHeaderMapImpl headers = genHeaders("bat.com", "/xx/yy/6472?test=foo", "GET"); + const RouteEntry* route = config.route(headers, 0)->routeEntry(); + route->finalizeRequestHeaders(headers, stream_info, true); + EXPECT_EQ("/four/6472/endpoint/xx/yy?test=foo", headers.get_(Http::Headers::get().Path)); + EXPECT_EQ("/xx/yy/6472?test=foo", headers.get_(Http::Headers::get().EnvoyOriginalPath)); + } + // Virtual cluster testing. { Http::TestHeaderMapImpl headers = genHeaders("api.lyft.com", "/rides", "GET"); From cc5d676248fbdafe3352f4dd5095bfd7d7db2039 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Thu, 13 Feb 2020 14:44:43 -0500 Subject: [PATCH 02/13] Bump the "next-free-field" value for RouteAction Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 2 +- api/envoy/config/route/v3/route_components.proto | 2 +- generated_api_shadow/envoy/api/v2/route/route_components.proto | 2 +- .../envoy/config/route/v3/route_components.proto | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index e2545df0437d5..0dd676dc1c097 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -530,7 +530,7 @@ message CorsPolicy { core.RuntimeFractionalPercent shadow_enabled = 10; } -// [#next-free-field: 32] +// [#next-free-field: 33] message RouteAction { enum ClusterNotFoundResponseCode { // HTTP status code - 503 Service Unavailable. diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 5c602e2e5032e..80f1bd74e5e70 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -491,7 +491,7 @@ message CorsPolicy { core.v3.RuntimeFractionalPercent shadow_enabled = 10; } -// [#next-free-field: 32] +// [#next-free-field: 33] message RouteAction { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RouteAction"; diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index e2545df0437d5..0dd676dc1c097 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -530,7 +530,7 @@ message CorsPolicy { core.RuntimeFractionalPercent shadow_enabled = 10; } -// [#next-free-field: 32] +// [#next-free-field: 33] message RouteAction { enum ClusterNotFoundResponseCode { // HTTP status code - 503 Service Unavailable. diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 701642fc0f192..5660ed99e6b78 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -553,7 +553,7 @@ message CorsPolicy { core.v3.RuntimeFractionalPercent shadow_enabled = 10; } -// [#next-free-field: 32] +// [#next-free-field: 33] message RouteAction { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RouteAction"; From b7e83ba5485afa26ba1086a051b8b236969522da Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Thu, 13 Feb 2020 15:05:17 -0500 Subject: [PATCH 03/13] Correct link for regex_rewrite Signed-off-by: James Hennessy --- docs/root/intro/arch_overview/http/http_routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/root/intro/arch_overview/http/http_routing.rst b/docs/root/intro/arch_overview/http/http_routing.rst index 376b5585b0899..95ce2b6ed796f 100644 --- a/docs/root/intro/arch_overview/http/http_routing.rst +++ b/docs/root/intro/arch_overview/http/http_routing.rst @@ -30,7 +30,7 @@ request. The router filter supports the following features: * :ref:`Automatic host rewriting ` based on the DNS name of the selected upstream host. * :ref:`Prefix rewriting `. -* :ref:`Path rewriting using a regular expression and capture groups `. +* :ref:`Path rewriting using a regular expression and capture groups `. * :ref:`Request retries ` specified either via HTTP header or via route configuration. * Request timeout specified either via :ref:`HTTP From df7590622c92f014f464e1104059db17c3f3df59 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Thu, 13 Feb 2020 16:09:58 -0500 Subject: [PATCH 04/13] Hopefully fix documentation formatting errors Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 25 ++++++++++--------- .../config/route/v3/route_components.proto | 25 ++++++++++--------- .../envoy/api/v2/route/route_components.proto | 25 ++++++++++--------- .../config/route/v3/route_components.proto | 25 ++++++++++--------- 4 files changed, 52 insertions(+), 48 deletions(-) diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index 0dd676dc1c097..17b813ef65b03 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -776,19 +776,20 @@ message RouteAction { // // Examples: // - // * The path pattern ^/service/([^/]+)(/.*)$ paired with a substitution - // string of \2/instance/\1 would transform */service/foo/v1/api* into - // */v1/api/instance/foo*. + // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution + // string of ``\2/instance/\1`` would transform ``/service/foo/v1/api`` + // into ``/v1/api/instance/foo``. // - // * The pattern *one* paired with a substitution string of *two* would - // transform */xxx/one/yyy/one/zzz* into */xxx/two/yyy/two/zzz*. + // * The pattern ``one`` paired with a substitution string of ``two`` would + // transform ``/xxx/one/yyy/one/zzz`` into ``/xxx/two/yyy/two/zzz``. // - // * The pattern ^(.*?)one(.*)$ paired with a substitution string of - // \1two\2 would replace only the first occurrence of "one", - // transforming path */xxx/one/yyy/one/zzz* into */xxx/two/yyy/one/zzz*. + // * The pattern ``^(.*?)one(.*)$`` paired with a substitution string of + // ``\1two\2`` would replace only the first occurrence of ``one``, + // transforming path ``/xxx/one/yyy/one/zzz`` into ``/xxx/two/yyy/one/zzz``. // - // * The pattern (?i)/xxx/ paired with a substitution string of /yyy/ would - // do a case-insensitive match and transform path /aaa/XxX/bbb to /aaa/yyy/bbb. + // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` + // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to + // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32; message RegexRewrite { @@ -805,8 +806,8 @@ message RouteAction { // however, that the syntax for referring to capture groups might vary // among the regular expression engines. Google's `RE2 `_ // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., \1 refers to capture - // group 1, and \2 refers to capture group 2. + // number to denote a numbered capture group. E.g., ``\1`` refers to capture + // group 1, and ``\2`` refers to capture group 2. string substitution = 2; } diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 80f1bd74e5e70..ba19e0b15dd9e 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -749,19 +749,20 @@ message RouteAction { // // Examples: // - // * The path pattern ^/service/([^/]+)(/.*)$ paired with a substitution - // string of \2/instance/\1 would transform */service/foo/v1/api* into - // */v1/api/instance/foo*. + // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution + // string of ``\2/instance/\1`` would transform ``/service/foo/v1/api`` + // into ``/v1/api/instance/foo``. // - // * The pattern *one* paired with a substitution string of *two* would - // transform */xxx/one/yyy/one/zzz* into */xxx/two/yyy/two/zzz*. + // * The pattern ``one`` paired with a substitution string of ``two`` would + // transform ``/xxx/one/yyy/one/zzz`` into ``/xxx/two/yyy/two/zzz``. // - // * The pattern ^(.*?)one(.*)$ paired with a substitution string of - // \1two\2 would replace only the first occurrence of "one", - // transforming path */xxx/one/yyy/one/zzz* into */xxx/two/yyy/one/zzz*. + // * The pattern ``^(.*?)one(.*)$`` paired with a substitution string of + // ``\1two\2`` would replace only the first occurrence of ``one``, + // transforming path ``/xxx/one/yyy/one/zzz`` into ``/xxx/two/yyy/one/zzz``. // - // * The pattern (?i)/xxx/ paired with a substitution string of /yyy/ would - // do a case-insensitive match and transform path /aaa/XxX/bbb to /aaa/yyy/bbb. + // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` + // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to + // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32; message RegexRewrite { @@ -781,8 +782,8 @@ message RouteAction { // however, that the syntax for referring to capture groups might vary // among the regular expression engines. Google's `RE2 `_ // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., \1 refers to capture - // group 1, and \2 refers to capture group 2. + // number to denote a numbered capture group. E.g., ``\1`` refers to capture + // group 1, and ``\2`` refers to capture group 2. string substitution = 2; } diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index 0dd676dc1c097..17b813ef65b03 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -776,19 +776,20 @@ message RouteAction { // // Examples: // - // * The path pattern ^/service/([^/]+)(/.*)$ paired with a substitution - // string of \2/instance/\1 would transform */service/foo/v1/api* into - // */v1/api/instance/foo*. + // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution + // string of ``\2/instance/\1`` would transform ``/service/foo/v1/api`` + // into ``/v1/api/instance/foo``. // - // * The pattern *one* paired with a substitution string of *two* would - // transform */xxx/one/yyy/one/zzz* into */xxx/two/yyy/two/zzz*. + // * The pattern ``one`` paired with a substitution string of ``two`` would + // transform ``/xxx/one/yyy/one/zzz`` into ``/xxx/two/yyy/two/zzz``. // - // * The pattern ^(.*?)one(.*)$ paired with a substitution string of - // \1two\2 would replace only the first occurrence of "one", - // transforming path */xxx/one/yyy/one/zzz* into */xxx/two/yyy/one/zzz*. + // * The pattern ``^(.*?)one(.*)$`` paired with a substitution string of + // ``\1two\2`` would replace only the first occurrence of ``one``, + // transforming path ``/xxx/one/yyy/one/zzz`` into ``/xxx/two/yyy/one/zzz``. // - // * The pattern (?i)/xxx/ paired with a substitution string of /yyy/ would - // do a case-insensitive match and transform path /aaa/XxX/bbb to /aaa/yyy/bbb. + // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` + // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to + // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32; message RegexRewrite { @@ -805,8 +806,8 @@ message RouteAction { // however, that the syntax for referring to capture groups might vary // among the regular expression engines. Google's `RE2 `_ // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., \1 refers to capture - // group 1, and \2 refers to capture group 2. + // number to denote a numbered capture group. E.g., ``\1`` refers to capture + // group 1, and ``\2`` refers to capture group 2. string substitution = 2; } diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 5660ed99e6b78..c09e336722ab7 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -823,19 +823,20 @@ message RouteAction { // // Examples: // - // * The path pattern ^/service/([^/]+)(/.*)$ paired with a substitution - // string of \2/instance/\1 would transform */service/foo/v1/api* into - // */v1/api/instance/foo*. + // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution + // string of ``\2/instance/\1`` would transform ``/service/foo/v1/api`` + // into ``/v1/api/instance/foo``. // - // * The pattern *one* paired with a substitution string of *two* would - // transform */xxx/one/yyy/one/zzz* into */xxx/two/yyy/two/zzz*. + // * The pattern ``one`` paired with a substitution string of ``two`` would + // transform ``/xxx/one/yyy/one/zzz`` into ``/xxx/two/yyy/two/zzz``. // - // * The pattern ^(.*?)one(.*)$ paired with a substitution string of - // \1two\2 would replace only the first occurrence of "one", - // transforming path */xxx/one/yyy/one/zzz* into */xxx/two/yyy/one/zzz*. + // * The pattern ``^(.*?)one(.*)$`` paired with a substitution string of + // ``\1two\2`` would replace only the first occurrence of ``one``, + // transforming path ``/xxx/one/yyy/one/zzz`` into ``/xxx/two/yyy/one/zzz``. // - // * The pattern (?i)/xxx/ paired with a substitution string of /yyy/ would - // do a case-insensitive match and transform path /aaa/XxX/bbb to /aaa/yyy/bbb. + // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` + // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to + // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32; message RegexRewrite { @@ -855,8 +856,8 @@ message RouteAction { // however, that the syntax for referring to capture groups might vary // among the regular expression engines. Google's `RE2 `_ // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., \1 refers to capture - // group 1, and \2 refers to capture group 2. + // number to denote a numbered capture group. E.g., ``\1`` refers to capture + // group 1, and ``\2`` refers to capture group 2. string substitution = 2; } From 7e09740241b72207236c22c8f63da50e706de1f5 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Thu, 13 Feb 2020 17:26:41 -0500 Subject: [PATCH 05/13] Address pedantic spelling complaints Signed-off-by: James Hennessy --- source/common/router/config_impl.cc | 8 ++++---- test/common/router/config_impl_test.cc | 6 +++--- tools/spelling_dictionary.txt | 4 ++++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index f4a0c58549fa9..efa6ba5d1c3ec 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -521,9 +521,9 @@ RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& // handles the "prefix_rewrite" and "regex_rewrite" route actions. If // "prefix_rewrite" is specified, it takes precedence. The "matched_path" // argument applies only to the prefix rewriting, and describes the portion of -// the path (excluding query parms) that should be replaced by the rewrite. A -// "regex_rewrite" applies to the entire path, regardless of what portion was -// matched. +// the path (excluding query parameters) that should be replaced by the +// rewrite. A "regex_rewrite" applies to the entire path, regardless of what +// portion was matched. void RouteEntryImplBase::finalizePathHeader(Http::HeaderMap& headers, absl::string_view matched_path, bool insert_envoy_original_path) const { @@ -544,7 +544,7 @@ void RouteEntryImplBase::finalizePathHeader(Http::HeaderMap& headers, if (insert_envoy_original_path) { headers.setEnvoyOriginalPath(path); } - // Replace the entire path, but preserve the query parms + // Replace the entire path, but preserve the query parameters auto just_path(Http::PathUtil::removeQueryAndFragment(path)); headers.setPath(path.replace( 0, just_path.size(), regex_rewrite_->replaceAll(just_path, regex_rewrite_substitution_))); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 42a1a1be0029d..8f2c46e149241 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -832,7 +832,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { } // Regular expression path rewrite after prefix match testing, replace every - // occurrence, excluding query parms. + // occurrence, excluding query parameters. { Http::TestHeaderMapImpl headers = genHeaders("www.lyft.com", "/newforreg2_endpoint/tee?test=me", "GET"); @@ -857,7 +857,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { } // Regular expression path rewrite after exact path match testing, - // with query parms. + // with query parameters. { Http::TestHeaderMapImpl headers = genHeaders("www.lyft.com", "/exact/path/for/regex1?test=aeiou", "GET"); @@ -982,7 +982,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { EXPECT_EQ("/xx/yy/6472", headers.get_(Http::Headers::get().EnvoyOriginalPath)); } - // Regular expression rewrite for regular expression matching, with query parms. + // Regular expression rewrite for regular expression matching, with query parameters. { Http::TestHeaderMapImpl headers = genHeaders("bat.com", "/xx/yy/6472?test=foo", "GET"); const RouteEntry* route = config.route(headers, 0)->routeEntry(); diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index 217468c6db3c3..8c9c6889e0bcf 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -337,6 +337,7 @@ XNOR XSS YAML ZXID +aaa abc absl accesslog @@ -390,6 +391,7 @@ balancers barbaz baz bazel +bbb behaviour benchmarked benchmarking @@ -1200,6 +1202,7 @@ xmodem xxhash xxs xyz +yyy zag zig zipkin @@ -1207,3 +1210,4 @@ zlib OBQ SemVer SCM +zzz From 6f7367ab928710b8b57ddab925964e3639b461f9 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Thu, 13 Feb 2020 18:55:44 -0500 Subject: [PATCH 06/13] Move the RegexRewrite message in the proto files to conform with checking rules Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 40 ++++++++-------- .../config/route/v3/route_components.proto | 46 ++++++++++--------- .../envoy/api/v2/route/route_components.proto | 40 ++++++++-------- .../config/route/v3/route_components.proto | 46 ++++++++++--------- 4 files changed, 90 insertions(+), 82 deletions(-) diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index 17b813ef65b03..c1edb80720eed 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -696,6 +696,27 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } + // Describe how to match the path and rewrite it using a regular expression + // and a substitution string. + message RegexRewrite { + // The regular expression used to find portions of the path (excluding + // query parameters) that should be replaced. All matches in the path are + // replaced by the substitution string, though anchors in the regular + // expression can be used to replace just one occurrence of a pattern. + // Capture groups can be used in the pattern to extract portions of the + // path, and referenced in the substitution string. + type.matcher.RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the path. + // Capture groups in the pattern can be referenced in this string Beware, + // however, that the syntax for referring to capture groups might vary + // among the regular expression engines. Google's `RE2 `_ + // regular expression engine uses backslash followed by the capture group + // number to denote a numbered capture group. E.g., ``\1`` refers to capture + // group 1, and ``\2`` refers to capture group 2. + string substitution = 2; + } + reserved 12, 18, 19, 16, 22, 21; oneof cluster_specifier { @@ -792,25 +813,6 @@ message RouteAction { // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32; - message RegexRewrite { - // The regular expression used to find portions of the path (excluding - // query parameters) that should be replaced. All matches in the path are - // replaced by the substitution string, though anchors in the regular - // expression can be used to replace just one occurrence of a pattern. - // Capture groups can be used in the pattern to extract portions of the - // path, and referenced in the substitution string. - type.matcher.RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string Beware, - // however, that the syntax for referring to capture groups might vary - // among the regular expression engines. Google's `RE2 `_ - // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., ``\1`` refers to capture - // group 1, and ``\2`` refers to capture group 2. - string substitution = 2; - } - oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with // this value. diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index ba19e0b15dd9e..56b1540d3162f 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -667,6 +667,30 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } + // Describe how to match the path and rewrite it using a regular expression + // and a substitution string. + message RegexRewrite { + option (udpa.annotations.versioning).previous_message_type = + "envoy.api.v2.route.RouteAction.RegexRewrite"; + + // The regular expression used to find portions of the path (excluding + // query parameters) that should be replaced. All matches in the path are + // replaced by the substitution string, though anchors in the regular + // expression can be used to replace just one occurrence of a pattern. + // Capture groups can be used in the pattern to extract portions of the + // path, and referenced in the substitution string. + type.matcher.v3.RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the path. + // Capture groups in the pattern can be referenced in this string Beware, + // however, that the syntax for referring to capture groups might vary + // among the regular expression engines. Google's `RE2 `_ + // regular expression engine uses backslash followed by the capture group + // number to denote a numbered capture group. E.g., ``\1`` refers to capture + // group 1, and ``\2`` refers to capture group 2. + string substitution = 2; + } + reserved 12, 18, 19, 16, 22, 21, 10; reserved "request_mirror_policy"; @@ -765,28 +789,6 @@ message RouteAction { // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32; - message RegexRewrite { - option (udpa.annotations.versioning).previous_message_type = - "envoy.api.v2.route.RouteAction.RegexRewrite"; - - // The regular expression used to find portions of the path (excluding - // query parameters) that should be replaced. All matches in the path are - // replaced by the substitution string, though anchors in the regular - // expression can be used to replace just one occurrence of a pattern. - // Capture groups can be used in the pattern to extract portions of the - // path, and referenced in the substitution string. - type.matcher.v3.RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string Beware, - // however, that the syntax for referring to capture groups might vary - // among the regular expression engines. Google's `RE2 `_ - // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., ``\1`` refers to capture - // group 1, and ``\2`` refers to capture group 2. - string substitution = 2; - } - oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with // this value. diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index 17b813ef65b03..c1edb80720eed 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -696,6 +696,27 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } + // Describe how to match the path and rewrite it using a regular expression + // and a substitution string. + message RegexRewrite { + // The regular expression used to find portions of the path (excluding + // query parameters) that should be replaced. All matches in the path are + // replaced by the substitution string, though anchors in the regular + // expression can be used to replace just one occurrence of a pattern. + // Capture groups can be used in the pattern to extract portions of the + // path, and referenced in the substitution string. + type.matcher.RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the path. + // Capture groups in the pattern can be referenced in this string Beware, + // however, that the syntax for referring to capture groups might vary + // among the regular expression engines. Google's `RE2 `_ + // regular expression engine uses backslash followed by the capture group + // number to denote a numbered capture group. E.g., ``\1`` refers to capture + // group 1, and ``\2`` refers to capture group 2. + string substitution = 2; + } + reserved 12, 18, 19, 16, 22, 21; oneof cluster_specifier { @@ -792,25 +813,6 @@ message RouteAction { // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32; - message RegexRewrite { - // The regular expression used to find portions of the path (excluding - // query parameters) that should be replaced. All matches in the path are - // replaced by the substitution string, though anchors in the regular - // expression can be used to replace just one occurrence of a pattern. - // Capture groups can be used in the pattern to extract portions of the - // path, and referenced in the substitution string. - type.matcher.RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string Beware, - // however, that the syntax for referring to capture groups might vary - // among the regular expression engines. Google's `RE2 `_ - // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., ``\1`` refers to capture - // group 1, and ``\2`` refers to capture group 2. - string substitution = 2; - } - oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with // this value. diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index c09e336722ab7..cb39728b32d76 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -743,6 +743,30 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } + // Describe how to match the path and rewrite it using a regular expression + // and a substitution string. + message RegexRewrite { + option (udpa.annotations.versioning).previous_message_type = + "envoy.api.v2.route.RouteAction.RegexRewrite"; + + // The regular expression used to find portions of the path (excluding + // query parameters) that should be replaced. All matches in the path are + // replaced by the substitution string, though anchors in the regular + // expression can be used to replace just one occurrence of a pattern. + // Capture groups can be used in the pattern to extract portions of the + // path, and referenced in the substitution string. + type.matcher.v3.RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the path. + // Capture groups in the pattern can be referenced in this string Beware, + // however, that the syntax for referring to capture groups might vary + // among the regular expression engines. Google's `RE2 `_ + // regular expression engine uses backslash followed by the capture group + // number to denote a numbered capture group. E.g., ``\1`` refers to capture + // group 1, and ``\2`` refers to capture group 2. + string substitution = 2; + } + reserved 12, 18, 19, 16, 22, 21; oneof cluster_specifier { @@ -839,28 +863,6 @@ message RouteAction { // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32; - message RegexRewrite { - option (udpa.annotations.versioning).previous_message_type = - "envoy.api.v2.route.RouteAction.RegexRewrite"; - - // The regular expression used to find portions of the path (excluding - // query parameters) that should be replaced. All matches in the path are - // replaced by the substitution string, though anchors in the regular - // expression can be used to replace just one occurrence of a pattern. - // Capture groups can be used in the pattern to extract portions of the - // path, and referenced in the substitution string. - type.matcher.v3.RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string Beware, - // however, that the syntax for referring to capture groups might vary - // among the regular expression engines. Google's `RE2 `_ - // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., ``\1`` refers to capture - // group 1, and ``\2`` refers to capture group 2. - string substitution = 2; - } - oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with // this value. From 02f843fa14335727746611980707a2bbc9fcdd67 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Tue, 18 Feb 2020 14:37:22 -0500 Subject: [PATCH 07/13] Update documentation as requested. This updates the descriptions of prefix_rewrite and regex_rewrite to say that only one should be specified, and that prefix_rewrite takes precedence if both are. It also moved the bullet in version_history.rst to the "router" section, so they're in alphabetical order. Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 8 ++++++++ api/envoy/config/route/v3/route_components.proto | 8 ++++++++ docs/root/intro/version_history.rst | 4 ++-- .../envoy/api/v2/route/route_components.proto | 8 ++++++++ .../envoy/config/route/v3/route_components.proto | 8 ++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index c1edb80720eed..4fdd9bbc512df 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -763,6 +763,10 @@ message RouteAction { // place the original path before rewrite into the :ref:`x-envoy-original-path // ` header. // + // Only one of *prefix_rewrite* or + // :ref:`regex_rewrite ` + // should be specified. If both are specified, *prefix_rewrite* take precedence. + // // .. attention:: // // Pay careful attention to the use of trailing slashes in the @@ -795,6 +799,10 @@ message RouteAction { // before the rewrite into the :ref:`x-envoy-original-path // ` header. // + // Only one of :ref:`prefix_rewrite ` + // or *regex_rewrite* should be specified. If both are specified, + // *prefix_rewrite* take precedence. + // // Examples: // // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 56b1540d3162f..718c1eee3e4aa 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -739,6 +739,10 @@ message RouteAction { // place the original path before rewrite into the :ref:`x-envoy-original-path // ` header. // + // Only one of *prefix_rewrite* or + // :ref:`regex_rewrite ` + // should be specified. If both are specified, *prefix_rewrite* take precedence. + // // .. attention:: // // Pay careful attention to the use of trailing slashes in the @@ -771,6 +775,10 @@ message RouteAction { // before the rewrite into the :ref:`x-envoy-original-path // ` header. // + // Only one of :ref:`prefix_rewrite ` + // or *regex_rewrite* should be specified. If both are specified, + // *prefix_rewrite* take precedence. + // // Examples: // // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index 2ac9d0165819d..c28c537dd9ac9 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -14,6 +14,8 @@ Version history * router: added :ref:`auto_san_validation ` to support overrriding SAN validation to transport socket for new upstream connections based on the downstream HTTP host/authority header. * router: added the ability to match a route based on whether a downstream TLS connection certificate has been :ref:`validated `. +* router: added support for :ref:`regex_rewrite + ` for path rewriting using regular expressions and capture groups. * sds: added :ref:`GenericSecret ` to support secret of generic type. * stat sinks: stat sink extensions use the "envoy.stat_sinks" name space. A mapping of extension names is available in the :ref:`deprecated ` documentation. @@ -30,8 +32,6 @@ Version history restart Envoy. The behavior will not switch until the connection pools are recreated. The new circuit breaker behavior is described :ref:`here `. * upstream: changed load distribution algorithm when all priorities enter :ref:`panic mode`. -* router: added support for :ref:`regex_rewrite - ` for path rewriting using regular expressions and capture groups. 1.13.0 (January 20, 2020) ========================= diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index c1edb80720eed..4fdd9bbc512df 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -763,6 +763,10 @@ message RouteAction { // place the original path before rewrite into the :ref:`x-envoy-original-path // ` header. // + // Only one of *prefix_rewrite* or + // :ref:`regex_rewrite ` + // should be specified. If both are specified, *prefix_rewrite* take precedence. + // // .. attention:: // // Pay careful attention to the use of trailing slashes in the @@ -795,6 +799,10 @@ message RouteAction { // before the rewrite into the :ref:`x-envoy-original-path // ` header. // + // Only one of :ref:`prefix_rewrite ` + // or *regex_rewrite* should be specified. If both are specified, + // *prefix_rewrite* take precedence. + // // Examples: // // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index cb39728b32d76..37ec3f98ad840 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -813,6 +813,10 @@ message RouteAction { // place the original path before rewrite into the :ref:`x-envoy-original-path // ` header. // + // Only one of *prefix_rewrite* or + // :ref:`regex_rewrite ` + // should be specified. If both are specified, *prefix_rewrite* take precedence. + // // .. attention:: // // Pay careful attention to the use of trailing slashes in the @@ -845,6 +849,10 @@ message RouteAction { // before the rewrite into the :ref:`x-envoy-original-path // ` header. // + // Only one of :ref:`prefix_rewrite ` + // or *regex_rewrite* should be specified. If both are specified, + // *prefix_rewrite* take precedence. + // // Examples: // // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution From e5bab50f679e9e78939b27e166fe4fe7c813f74a Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Tue, 18 Feb 2020 19:50:30 -0500 Subject: [PATCH 08/13] Raise an exception if both prefix_rewrite and regex_rewrite are used. Also add an annotation to the v3 proto files indicating that prefix_rewrite and regex_rewrite will be migrated in v4 to a "oneof" choice called path_rewrite. Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 5 ++--- api/envoy/config/route/v3/route_components.proto | 11 ++++++----- .../envoy/api/v2/route/route_components.proto | 5 ++--- .../envoy/config/route/v3/route_components.proto | 11 ++++++----- source/common/router/config_impl.cc | 3 +++ 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index 4fdd9bbc512df..8b0ec1780b268 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -765,7 +765,7 @@ message RouteAction { // // Only one of *prefix_rewrite* or // :ref:`regex_rewrite ` - // should be specified. If both are specified, *prefix_rewrite* take precedence. + // may be specified. // // .. attention:: // @@ -800,8 +800,7 @@ message RouteAction { // ` header. // // Only one of :ref:`prefix_rewrite ` - // or *regex_rewrite* should be specified. If both are specified, - // *prefix_rewrite* take precedence. + // or *regex_rewrite* may be specified. // // Examples: // diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 718c1eee3e4aa..269d84263e3f7 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -14,6 +14,7 @@ import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/wrappers.proto"; +import "udpa/annotations/migrate.proto"; import "udpa/annotations/versioning.proto"; import "envoy/annotations/deprecation.proto"; @@ -741,7 +742,7 @@ message RouteAction { // // Only one of *prefix_rewrite* or // :ref:`regex_rewrite ` - // should be specified. If both are specified, *prefix_rewrite* take precedence. + // may be specified. // // .. attention:: // @@ -764,7 +765,7 @@ message RouteAction { // // Having above entries in the config, requests to */prefix* will be stripped to */*, while // requests to */prefix/etc* will be stripped to */etc*. - string prefix_rewrite = 5; + string prefix_rewrite = 5 [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite"]; // Indicates that during forwarding, portions of the path that match the // pattern should be rewritten, even allowing the substitution of capture @@ -776,8 +777,7 @@ message RouteAction { // ` header. // // Only one of :ref:`prefix_rewrite ` - // or *regex_rewrite* should be specified. If both are specified, - // *prefix_rewrite* take precedence. + // or *regex_rewrite* may be specified. // // Examples: // @@ -795,7 +795,8 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RegexRewrite regex_rewrite = 32; + RegexRewrite regex_rewrite = 32 + [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite"]; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index 4fdd9bbc512df..8b0ec1780b268 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -765,7 +765,7 @@ message RouteAction { // // Only one of *prefix_rewrite* or // :ref:`regex_rewrite ` - // should be specified. If both are specified, *prefix_rewrite* take precedence. + // may be specified. // // .. attention:: // @@ -800,8 +800,7 @@ message RouteAction { // ` header. // // Only one of :ref:`prefix_rewrite ` - // or *regex_rewrite* should be specified. If both are specified, - // *prefix_rewrite* take precedence. + // or *regex_rewrite* may be specified. // // Examples: // diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 37ec3f98ad840..509297019118c 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -14,6 +14,7 @@ import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/wrappers.proto"; +import "udpa/annotations/migrate.proto"; import "udpa/annotations/versioning.proto"; import "envoy/annotations/deprecation.proto"; @@ -815,7 +816,7 @@ message RouteAction { // // Only one of *prefix_rewrite* or // :ref:`regex_rewrite ` - // should be specified. If both are specified, *prefix_rewrite* take precedence. + // may be specified. // // .. attention:: // @@ -838,7 +839,7 @@ message RouteAction { // // Having above entries in the config, requests to */prefix* will be stripped to */*, while // requests to */prefix/etc* will be stripped to */etc*. - string prefix_rewrite = 5; + string prefix_rewrite = 5 [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite"]; // Indicates that during forwarding, portions of the path that match the // pattern should be rewritten, even allowing the substitution of capture @@ -850,8 +851,7 @@ message RouteAction { // ` header. // // Only one of :ref:`prefix_rewrite ` - // or *regex_rewrite* should be specified. If both are specified, - // *prefix_rewrite* take precedence. + // or *regex_rewrite* may be specified. // // Examples: // @@ -869,7 +869,8 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RegexRewrite regex_rewrite = 32; + RegexRewrite regex_rewrite = 32 + [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite"]; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index efa6ba5d1c3ec..4104a91a2e3b8 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -381,6 +381,9 @@ RouteEntryImplBase::RouteEntryImplBase(const VirtualHostImpl& vhost, } if (route.route().has_regex_rewrite()) { + if (!prefix_rewrite_.empty()) { + throw EnvoyException("Cannot specify both prefix_rewrite and regex_rewrite"); + } auto rewrite_spec = route.route().regex_rewrite(); regex_rewrite_ = Regex::Utility::parseRegex(rewrite_spec.pattern()); regex_rewrite_substitution_ = rewrite_spec.substitution(); From 8c7984e5f9659a75b5e87a411a5e825905359b98 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Tue, 18 Feb 2020 20:20:06 -0500 Subject: [PATCH 09/13] Update a comment that still mentioned a precedence Signed-off-by: James Hennessy --- source/common/router/config_impl.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 4104a91a2e3b8..476deccbadab6 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -521,11 +521,11 @@ RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& } // finalizePathHeaders does the "standard" path rewriting, meaning that it -// handles the "prefix_rewrite" and "regex_rewrite" route actions. If -// "prefix_rewrite" is specified, it takes precedence. The "matched_path" -// argument applies only to the prefix rewriting, and describes the portion of -// the path (excluding query parameters) that should be replaced by the -// rewrite. A "regex_rewrite" applies to the entire path, regardless of what +// handles the "prefix_rewrite" and "regex_rewrite" route actions, only one of +// which can be specified. The "matched_path" argument applies only to the +// prefix rewriting, and describes the portion of the path (excluding query +// parameters) that should be replaced by the rewrite. A "regex_rewrite" +// applies to the entire path (excluding query parameters), regardless of what // portion was matched. void RouteEntryImplBase::finalizePathHeader(Http::HeaderMap& headers, absl::string_view matched_path, From 68cf320323e53bd9082519b56c55460dd78c0ff2 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Wed, 19 Feb 2020 15:47:52 -0500 Subject: [PATCH 10/13] Address review comments Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 8 ++++---- .../config/route/v3/route_components.proto | 13 +++++++------ .../envoy/api/v2/route/route_components.proto | 8 ++++---- .../config/route/v3/route_components.proto | 13 +++++++------ source/common/common/regex.cc | 6 +++++- source/common/router/config_impl.cc | 18 ++++++++++-------- test/common/router/config_impl_test.cc | 4 ++-- 7 files changed, 39 insertions(+), 31 deletions(-) diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index 8b0ec1780b268..d50a55c9ef326 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -708,9 +708,9 @@ message RouteAction { type.matcher.RegexMatcher pattern = 1; // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string Beware, - // however, that the syntax for referring to capture groups might vary - // among the regular expression engines. Google's `RE2 `_ + // Capture groups in the pattern can be referenced in this string. Beware, + // however, that the syntax for referring to capture groups is defined by + // the regular expression engines. Google's `RE2 `_ // regular expression engine uses backslash followed by the capture group // number to denote a numbered capture group. E.g., ``\1`` refers to capture // group 1, and ``\2`` refers to capture group 2. @@ -802,7 +802,7 @@ message RouteAction { // Only one of :ref:`prefix_rewrite ` // or *regex_rewrite* may be specified. // - // Examples: + // Examples using Google's `RE2 `_ engine: // // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution // string of ``\2/instance/\1`` would transform ``/service/foo/v1/api`` diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 269d84263e3f7..67a916da4b409 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -683,9 +683,9 @@ message RouteAction { type.matcher.v3.RegexMatcher pattern = 1; // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string Beware, - // however, that the syntax for referring to capture groups might vary - // among the regular expression engines. Google's `RE2 `_ + // Capture groups in the pattern can be referenced in this string. Beware, + // however, that the syntax for referring to capture groups is defined by + // the regular expression engines. Google's `RE2 `_ // regular expression engine uses backslash followed by the capture group // number to denote a numbered capture group. E.g., ``\1`` refers to capture // group 1, and ``\2`` refers to capture group 2. @@ -765,7 +765,8 @@ message RouteAction { // // Having above entries in the config, requests to */prefix* will be stripped to */*, while // requests to */prefix/etc* will be stripped to */etc*. - string prefix_rewrite = 5 [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite"]; + string prefix_rewrite = 5 + [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite_specifier"]; // Indicates that during forwarding, portions of the path that match the // pattern should be rewritten, even allowing the substitution of capture @@ -779,7 +780,7 @@ message RouteAction { // Only one of :ref:`prefix_rewrite ` // or *regex_rewrite* may be specified. // - // Examples: + // Examples using Google's `RE2 `_ engine: // // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution // string of ``\2/instance/\1`` would transform ``/service/foo/v1/api`` @@ -796,7 +797,7 @@ message RouteAction { // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32 - [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite"]; + [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite_specifier"]; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index 8b0ec1780b268..d50a55c9ef326 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -708,9 +708,9 @@ message RouteAction { type.matcher.RegexMatcher pattern = 1; // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string Beware, - // however, that the syntax for referring to capture groups might vary - // among the regular expression engines. Google's `RE2 `_ + // Capture groups in the pattern can be referenced in this string. Beware, + // however, that the syntax for referring to capture groups is defined by + // the regular expression engines. Google's `RE2 `_ // regular expression engine uses backslash followed by the capture group // number to denote a numbered capture group. E.g., ``\1`` refers to capture // group 1, and ``\2`` refers to capture group 2. @@ -802,7 +802,7 @@ message RouteAction { // Only one of :ref:`prefix_rewrite ` // or *regex_rewrite* may be specified. // - // Examples: + // Examples using Google's `RE2 `_ engine: // // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution // string of ``\2/instance/\1`` would transform ``/service/foo/v1/api`` diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 509297019118c..9e63110838a50 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -759,9 +759,9 @@ message RouteAction { type.matcher.v3.RegexMatcher pattern = 1; // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string Beware, - // however, that the syntax for referring to capture groups might vary - // among the regular expression engines. Google's `RE2 `_ + // Capture groups in the pattern can be referenced in this string. Beware, + // however, that the syntax for referring to capture groups is defined by + // the regular expression engines. Google's `RE2 `_ // regular expression engine uses backslash followed by the capture group // number to denote a numbered capture group. E.g., ``\1`` refers to capture // group 1, and ``\2`` refers to capture group 2. @@ -839,7 +839,8 @@ message RouteAction { // // Having above entries in the config, requests to */prefix* will be stripped to */*, while // requests to */prefix/etc* will be stripped to */etc*. - string prefix_rewrite = 5 [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite"]; + string prefix_rewrite = 5 + [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite_specifier"]; // Indicates that during forwarding, portions of the path that match the // pattern should be rewritten, even allowing the substitution of capture @@ -853,7 +854,7 @@ message RouteAction { // Only one of :ref:`prefix_rewrite ` // or *regex_rewrite* may be specified. // - // Examples: + // Examples using Google's `RE2 `_ engine: // // * The path pattern ``^/service/([^/]+)(/.*)$`` paired with a substitution // string of ``\2/instance/\1`` would transform ``/service/foo/v1/api`` @@ -870,7 +871,7 @@ message RouteAction { // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. RegexRewrite regex_rewrite = 32 - [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite"]; + [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite_specifier"]; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/source/common/common/regex.cc b/source/common/common/regex.cc index 0bb699692ac96..6d8467f8ea195 100644 --- a/source/common/common/regex.cc +++ b/source/common/common/regex.cc @@ -24,7 +24,11 @@ class CompiledStdMatcher : public CompiledMatcher { // CompiledMatcher std::string replaceAll(absl::string_view value, absl::string_view substitution) const override { - return std::regex_replace(std::string(value), regex_, std::string(substitution)); + try { + return std::regex_replace(std::string(value), regex_, std::string(substitution)); + } catch (const std::regex_error& e) { + return std::string(value); + } } private: diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 476deccbadab6..2a5d8ccb01177 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -531,11 +531,17 @@ void RouteEntryImplBase::finalizePathHeader(Http::HeaderMap& headers, absl::string_view matched_path, bool insert_envoy_original_path) const { const auto& rewrite = getPathRewrite(); + if (rewrite.empty() && regex_rewrite_ == nullptr) { + // There are no rewrites configured. Just return. + return; + } + + std::string path(headers.Path()->value().getStringView()); + if (insert_envoy_original_path) { + headers.setEnvoyOriginalPath(path); + } + if (!rewrite.empty()) { - std::string path(headers.Path()->value().getStringView()); - if (insert_envoy_original_path) { - headers.setEnvoyOriginalPath(path); - } ASSERT(case_sensitive_ ? absl::StartsWith(path, matched_path) : absl::StartsWithIgnoreCase(path, matched_path)); headers.setPath(path.replace(0, matched_path.size(), rewrite)); @@ -543,10 +549,6 @@ void RouteEntryImplBase::finalizePathHeader(Http::HeaderMap& headers, } if (regex_rewrite_ != nullptr) { - std::string path(headers.Path()->value().getStringView()); - if (insert_envoy_original_path) { - headers.setEnvoyOriginalPath(path); - } // Replace the entire path, but preserve the query parameters auto just_path(Http::PathUtil::removeQueryAndFragment(path)); headers.setPath(path.replace( diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 3c45757f6a87e..3e45d8403953c 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -454,7 +454,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { pattern: google_re2: {} regex: "^/new(.*?)_endpoint(.*)$" - substitution: "/\\1_rewritten_endpoint\\2" + substitution: /\1_rewritten_endpoint\2 cluster: www2 - match: prefix: "/newforreg2_endpoint" @@ -542,7 +542,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { pattern: google_re2: {} regex: "(^.*)/(\\d{4})$" - substitution: "/four/\\2/endpoint\\1" + substitution: /four/\2/endpoint\1 - match: safe_regex: google_re2: {} From 697810320f797fb6b4a8386983983d310ff4de5f Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Wed, 19 Feb 2020 19:50:43 -0500 Subject: [PATCH 11/13] Remove oneof_promotion annotations from v3 protos, as tooling is not yet ready. Signed-off-by: James Hennessy --- api/envoy/config/route/v3/route_components.proto | 7 ++----- .../envoy/config/route/v3/route_components.proto | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 67a916da4b409..d909f023245f1 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -14,7 +14,6 @@ import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/wrappers.proto"; -import "udpa/annotations/migrate.proto"; import "udpa/annotations/versioning.proto"; import "envoy/annotations/deprecation.proto"; @@ -765,8 +764,7 @@ message RouteAction { // // Having above entries in the config, requests to */prefix* will be stripped to */*, while // requests to */prefix/etc* will be stripped to */etc*. - string prefix_rewrite = 5 - [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite_specifier"]; + string prefix_rewrite = 5; // Indicates that during forwarding, portions of the path that match the // pattern should be rewritten, even allowing the substitution of capture @@ -796,8 +794,7 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RegexRewrite regex_rewrite = 32 - [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite_specifier"]; + RegexRewrite regex_rewrite = 32; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 9e63110838a50..fcece30d6d6b7 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -14,7 +14,6 @@ import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/wrappers.proto"; -import "udpa/annotations/migrate.proto"; import "udpa/annotations/versioning.proto"; import "envoy/annotations/deprecation.proto"; @@ -839,8 +838,7 @@ message RouteAction { // // Having above entries in the config, requests to */prefix* will be stripped to */*, while // requests to */prefix/etc* will be stripped to */etc*. - string prefix_rewrite = 5 - [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite_specifier"]; + string prefix_rewrite = 5; // Indicates that during forwarding, portions of the path that match the // pattern should be rewritten, even allowing the substitution of capture @@ -870,8 +868,7 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RegexRewrite regex_rewrite = 32 - [(udpa.annotations.field_migrate).oneof_promotion = "path_rewrite_specifier"]; + RegexRewrite regex_rewrite = 32; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with From ba5ad9f0d533b2cc4f39dd996a1052acc0b7b997 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Tue, 25 Feb 2020 17:38:32 -0500 Subject: [PATCH 12/13] Make the RegexRewrite api message a general type called RegexMatchAndSubstitute. Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 24 +---------- .../config/route/v3/route_components.proto | 27 +----------- .../type/matcher/regex_match_and_subst.proto | 37 ++++++++++++++++ .../matcher/v3/regex_match_and_subst.proto | 42 +++++++++++++++++++ docs/root/api-v2/types/types.rst | 1 + docs/root/api-v3/types/types.rst | 1 + .../envoy/api/v2/route/route_components.proto | 24 +---------- .../config/route/v3/route_components.proto | 27 +----------- .../type/matcher/regex_match_and_subst.proto | 37 ++++++++++++++++ .../matcher/v3/regex_match_and_subst.proto | 42 +++++++++++++++++++ 10 files changed, 168 insertions(+), 94 deletions(-) create mode 100644 api/envoy/type/matcher/regex_match_and_subst.proto create mode 100644 api/envoy/type/matcher/v3/regex_match_and_subst.proto create mode 100644 generated_api_shadow/envoy/type/matcher/regex_match_and_subst.proto create mode 100644 generated_api_shadow/envoy/type/matcher/v3/regex_match_and_subst.proto diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index d50a55c9ef326..46b791274f63f 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -4,6 +4,7 @@ package envoy.api.v2.route; import "envoy/api/v2/core/base.proto"; import "envoy/type/matcher/regex.proto"; +import "envoy/type/matcher/regex_match_and_subst.proto"; import "envoy/type/matcher/string.proto"; import "envoy/type/percent.proto"; import "envoy/type/range.proto"; @@ -696,27 +697,6 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } - // Describe how to match the path and rewrite it using a regular expression - // and a substitution string. - message RegexRewrite { - // The regular expression used to find portions of the path (excluding - // query parameters) that should be replaced. All matches in the path are - // replaced by the substitution string, though anchors in the regular - // expression can be used to replace just one occurrence of a pattern. - // Capture groups can be used in the pattern to extract portions of the - // path, and referenced in the substitution string. - type.matcher.RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string. Beware, - // however, that the syntax for referring to capture groups is defined by - // the regular expression engines. Google's `RE2 `_ - // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., ``\1`` refers to capture - // group 1, and ``\2`` refers to capture group 2. - string substitution = 2; - } - reserved 12, 18, 19, 16, 22, 21; oneof cluster_specifier { @@ -818,7 +798,7 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RegexRewrite regex_rewrite = 32; + type.matcher.RegexMatchAndSubstitute regex_rewrite = 32; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index d909f023245f1..02ad434d41a6b 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -4,6 +4,7 @@ package envoy.config.route.v3; import "envoy/config/core/v3/base.proto"; import "envoy/type/matcher/v3/regex.proto"; +import "envoy/type/matcher/v3/regex_match_and_subst.proto"; import "envoy/type/matcher/v3/string.proto"; import "envoy/type/tracing/v3/custom_tag.proto"; import "envoy/type/v3/percent.proto"; @@ -667,30 +668,6 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } - // Describe how to match the path and rewrite it using a regular expression - // and a substitution string. - message RegexRewrite { - option (udpa.annotations.versioning).previous_message_type = - "envoy.api.v2.route.RouteAction.RegexRewrite"; - - // The regular expression used to find portions of the path (excluding - // query parameters) that should be replaced. All matches in the path are - // replaced by the substitution string, though anchors in the regular - // expression can be used to replace just one occurrence of a pattern. - // Capture groups can be used in the pattern to extract portions of the - // path, and referenced in the substitution string. - type.matcher.v3.RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string. Beware, - // however, that the syntax for referring to capture groups is defined by - // the regular expression engines. Google's `RE2 `_ - // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., ``\1`` refers to capture - // group 1, and ``\2`` refers to capture group 2. - string substitution = 2; - } - reserved 12, 18, 19, 16, 22, 21, 10; reserved "request_mirror_policy"; @@ -794,7 +771,7 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RegexRewrite regex_rewrite = 32; + type.matcher.v3.RegexMatchAndSubstitute regex_rewrite = 32; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/api/envoy/type/matcher/regex_match_and_subst.proto b/api/envoy/type/matcher/regex_match_and_subst.proto new file mode 100644 index 0000000000000..d576a6ff1823f --- /dev/null +++ b/api/envoy/type/matcher/regex_match_and_subst.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package envoy.type.matcher; + +import "envoy/type/matcher/regex.proto"; + +option java_package = "io.envoyproxy.envoy.type.matcher"; +option java_outer_classname = "RegexMatchAndSubstProto"; +option java_multiple_files = true; + +// [#protodoc-title: Regex match and substitute] + +// Describes how to match a string and then produce a new string using a regular +// expression and a substitution string. +message RegexMatchAndSubstitute { + // The regular expression used to find portions of a string (hereafter called + // the "subject string") that should be replaced. When a new string is + // produced during the substitution operation, the new string is initially + // the same as the subject string, but then all matches in the subject string + // are replaced by the substitution string. If replacing all matches isn't + // desired, regular expression anchors can be used to ensure a single match, + // so as to replace just one occurrence of a pattern. Capture groups can be + // used in the pattern to extract portions of the subject string, and then + // referenced in the substitution string. + RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the + // subject string during a substitution operation to produce a new string. + // Capture groups in the pattern can be referenced in the substitution + // string. Note, however, that the syntax for referring to capture groups is + // defined by the chosen regular expression engine. Google's `RE2 + // `_ regular expression engine uses a + // backslash followed by the capture group number to denote a numbered + // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers + // to capture group 2. + string substitution = 2; +} diff --git a/api/envoy/type/matcher/v3/regex_match_and_subst.proto b/api/envoy/type/matcher/v3/regex_match_and_subst.proto new file mode 100644 index 0000000000000..16d8e70a911b2 --- /dev/null +++ b/api/envoy/type/matcher/v3/regex_match_and_subst.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package envoy.type.matcher.v3; + +import "envoy/type/matcher/v3/regex.proto"; + +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.type.matcher.v3"; +option java_outer_classname = "RegexMatchAndSubstProto"; +option java_multiple_files = true; + +// [#protodoc-title: Regex match and substitute] + +// Describes how to match a string and then produce a new string using a regular +// expression and a substitution string. +message RegexMatchAndSubstitute { + option (udpa.annotations.versioning).previous_message_type = + "envoy.type.matcher.RegexMatchAndSubstitute"; + + // The regular expression used to find portions of a string (hereafter called + // the "subject string") that should be replaced. When a new string is + // produced during the substitution operation, the new string is initially + // the same as the subject string, but then all matches in the subject string + // are replaced by the substitution string. If replacing all matches isn't + // desired, regular expression anchors can be used to ensure a single match, + // so as to replace just one occurrence of a pattern. Capture groups can be + // used in the pattern to extract portions of the subject string, and then + // referenced in the substitution string. + RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the + // subject string during a substitution operation to produce a new string. + // Capture groups in the pattern can be referenced in the substitution + // string. Note, however, that the syntax for referring to capture groups is + // defined by the chosen regular expression engine. Google's `RE2 + // `_ regular expression engine uses a + // backslash followed by the capture group number to denote a numbered + // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers + // to capture group 2. + string substitution = 2; +} diff --git a/docs/root/api-v2/types/types.rst b/docs/root/api-v2/types/types.rst index b85889bf45a91..a8040ee4c5799 100644 --- a/docs/root/api-v2/types/types.rst +++ b/docs/root/api-v2/types/types.rst @@ -17,6 +17,7 @@ Types ../type/matcher/number.proto ../type/matcher/path.proto ../type/matcher/regex.proto + ../type/matcher/regex_match_and_subst.proto ../type/matcher/string.proto ../type/matcher/struct.proto ../type/matcher/value.proto diff --git a/docs/root/api-v3/types/types.rst b/docs/root/api-v3/types/types.rst index f9c1cad3ea820..411a2a3909339 100644 --- a/docs/root/api-v3/types/types.rst +++ b/docs/root/api-v3/types/types.rst @@ -17,6 +17,7 @@ Types ../type/matcher/v3/number.proto ../type/matcher/v3/path.proto ../type/matcher/v3/regex.proto + ../type/matcher/v3/regex_match_and_subst.proto ../type/matcher/v3/string.proto ../type/matcher/v3/struct.proto ../type/matcher/v3/value.proto diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index d50a55c9ef326..46b791274f63f 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -4,6 +4,7 @@ package envoy.api.v2.route; import "envoy/api/v2/core/base.proto"; import "envoy/type/matcher/regex.proto"; +import "envoy/type/matcher/regex_match_and_subst.proto"; import "envoy/type/matcher/string.proto"; import "envoy/type/percent.proto"; import "envoy/type/range.proto"; @@ -696,27 +697,6 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } - // Describe how to match the path and rewrite it using a regular expression - // and a substitution string. - message RegexRewrite { - // The regular expression used to find portions of the path (excluding - // query parameters) that should be replaced. All matches in the path are - // replaced by the substitution string, though anchors in the regular - // expression can be used to replace just one occurrence of a pattern. - // Capture groups can be used in the pattern to extract portions of the - // path, and referenced in the substitution string. - type.matcher.RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string. Beware, - // however, that the syntax for referring to capture groups is defined by - // the regular expression engines. Google's `RE2 `_ - // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., ``\1`` refers to capture - // group 1, and ``\2`` refers to capture group 2. - string substitution = 2; - } - reserved 12, 18, 19, 16, 22, 21; oneof cluster_specifier { @@ -818,7 +798,7 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RegexRewrite regex_rewrite = 32; + type.matcher.RegexMatchAndSubstitute regex_rewrite = 32; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index fcece30d6d6b7..9d6a782689f44 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -4,6 +4,7 @@ package envoy.config.route.v3; import "envoy/config/core/v3/base.proto"; import "envoy/type/matcher/v3/regex.proto"; +import "envoy/type/matcher/v3/regex_match_and_subst.proto"; import "envoy/type/matcher/v3/string.proto"; import "envoy/type/tracing/v3/custom_tag.proto"; import "envoy/type/v3/percent.proto"; @@ -743,30 +744,6 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } - // Describe how to match the path and rewrite it using a regular expression - // and a substitution string. - message RegexRewrite { - option (udpa.annotations.versioning).previous_message_type = - "envoy.api.v2.route.RouteAction.RegexRewrite"; - - // The regular expression used to find portions of the path (excluding - // query parameters) that should be replaced. All matches in the path are - // replaced by the substitution string, though anchors in the regular - // expression can be used to replace just one occurrence of a pattern. - // Capture groups can be used in the pattern to extract portions of the - // path, and referenced in the substitution string. - type.matcher.v3.RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the path. - // Capture groups in the pattern can be referenced in this string. Beware, - // however, that the syntax for referring to capture groups is defined by - // the regular expression engines. Google's `RE2 `_ - // regular expression engine uses backslash followed by the capture group - // number to denote a numbered capture group. E.g., ``\1`` refers to capture - // group 1, and ``\2`` refers to capture group 2. - string substitution = 2; - } - reserved 12, 18, 19, 16, 22, 21; oneof cluster_specifier { @@ -868,7 +845,7 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RegexRewrite regex_rewrite = 32; + type.matcher.v3.RegexMatchAndSubstitute regex_rewrite = 32; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with diff --git a/generated_api_shadow/envoy/type/matcher/regex_match_and_subst.proto b/generated_api_shadow/envoy/type/matcher/regex_match_and_subst.proto new file mode 100644 index 0000000000000..d576a6ff1823f --- /dev/null +++ b/generated_api_shadow/envoy/type/matcher/regex_match_and_subst.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package envoy.type.matcher; + +import "envoy/type/matcher/regex.proto"; + +option java_package = "io.envoyproxy.envoy.type.matcher"; +option java_outer_classname = "RegexMatchAndSubstProto"; +option java_multiple_files = true; + +// [#protodoc-title: Regex match and substitute] + +// Describes how to match a string and then produce a new string using a regular +// expression and a substitution string. +message RegexMatchAndSubstitute { + // The regular expression used to find portions of a string (hereafter called + // the "subject string") that should be replaced. When a new string is + // produced during the substitution operation, the new string is initially + // the same as the subject string, but then all matches in the subject string + // are replaced by the substitution string. If replacing all matches isn't + // desired, regular expression anchors can be used to ensure a single match, + // so as to replace just one occurrence of a pattern. Capture groups can be + // used in the pattern to extract portions of the subject string, and then + // referenced in the substitution string. + RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the + // subject string during a substitution operation to produce a new string. + // Capture groups in the pattern can be referenced in the substitution + // string. Note, however, that the syntax for referring to capture groups is + // defined by the chosen regular expression engine. Google's `RE2 + // `_ regular expression engine uses a + // backslash followed by the capture group number to denote a numbered + // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers + // to capture group 2. + string substitution = 2; +} diff --git a/generated_api_shadow/envoy/type/matcher/v3/regex_match_and_subst.proto b/generated_api_shadow/envoy/type/matcher/v3/regex_match_and_subst.proto new file mode 100644 index 0000000000000..16d8e70a911b2 --- /dev/null +++ b/generated_api_shadow/envoy/type/matcher/v3/regex_match_and_subst.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package envoy.type.matcher.v3; + +import "envoy/type/matcher/v3/regex.proto"; + +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.type.matcher.v3"; +option java_outer_classname = "RegexMatchAndSubstProto"; +option java_multiple_files = true; + +// [#protodoc-title: Regex match and substitute] + +// Describes how to match a string and then produce a new string using a regular +// expression and a substitution string. +message RegexMatchAndSubstitute { + option (udpa.annotations.versioning).previous_message_type = + "envoy.type.matcher.RegexMatchAndSubstitute"; + + // The regular expression used to find portions of a string (hereafter called + // the "subject string") that should be replaced. When a new string is + // produced during the substitution operation, the new string is initially + // the same as the subject string, but then all matches in the subject string + // are replaced by the substitution string. If replacing all matches isn't + // desired, regular expression anchors can be used to ensure a single match, + // so as to replace just one occurrence of a pattern. Capture groups can be + // used in the pattern to extract portions of the subject string, and then + // referenced in the substitution string. + RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the + // subject string during a substitution operation to produce a new string. + // Capture groups in the pattern can be referenced in the substitution + // string. Note, however, that the syntax for referring to capture groups is + // defined by the chosen regular expression engine. Google's `RE2 + // `_ regular expression engine uses a + // backslash followed by the capture group number to denote a numbered + // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers + // to capture group 2. + string substitution = 2; +} From 4eed676ecab0a93247914011b1727825bed8c627 Mon Sep 17 00:00:00 2001 From: James Hennessy Date: Wed, 26 Feb 2020 11:00:02 -0500 Subject: [PATCH 13/13] Use the existing regex.proto file for the RegexMatchAndSubstitute message. Also add a test that verifies an exception is raised if both prefix_rewrite and regex_rewrite are specified in a route. Signed-off-by: James Hennessy --- api/envoy/api/v2/route/route_components.proto | 1 - .../config/route/v3/route_components.proto | 1 - api/envoy/type/matcher/regex.proto | 26 ++++++++++++ .../type/matcher/regex_match_and_subst.proto | 37 ---------------- api/envoy/type/matcher/v3/regex.proto | 29 +++++++++++++ .../matcher/v3/regex_match_and_subst.proto | 42 ------------------- docs/root/api-v2/types/types.rst | 1 - docs/root/api-v3/types/types.rst | 1 - .../envoy/api/v2/route/route_components.proto | 1 - .../config/route/v3/route_components.proto | 1 - .../envoy/type/matcher/regex.proto | 26 ++++++++++++ .../type/matcher/regex_match_and_subst.proto | 37 ---------------- .../envoy/type/matcher/v3/regex.proto | 29 +++++++++++++ .../matcher/v3/regex_match_and_subst.proto | 42 ------------------- test/common/router/config_impl_test.cc | 22 ++++++++++ 15 files changed, 132 insertions(+), 164 deletions(-) delete mode 100644 api/envoy/type/matcher/regex_match_and_subst.proto delete mode 100644 api/envoy/type/matcher/v3/regex_match_and_subst.proto delete mode 100644 generated_api_shadow/envoy/type/matcher/regex_match_and_subst.proto delete mode 100644 generated_api_shadow/envoy/type/matcher/v3/regex_match_and_subst.proto diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index 46b791274f63f..4312993396965 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -4,7 +4,6 @@ package envoy.api.v2.route; import "envoy/api/v2/core/base.proto"; import "envoy/type/matcher/regex.proto"; -import "envoy/type/matcher/regex_match_and_subst.proto"; import "envoy/type/matcher/string.proto"; import "envoy/type/percent.proto"; import "envoy/type/range.proto"; diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 02ad434d41a6b..5430a813f0fb3 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -4,7 +4,6 @@ package envoy.config.route.v3; import "envoy/config/core/v3/base.proto"; import "envoy/type/matcher/v3/regex.proto"; -import "envoy/type/matcher/v3/regex_match_and_subst.proto"; import "envoy/type/matcher/v3/string.proto"; import "envoy/type/tracing/v3/custom_tag.proto"; import "envoy/type/v3/percent.proto"; diff --git a/api/envoy/type/matcher/regex.proto b/api/envoy/type/matcher/regex.proto index 2dd5bbe047cbb..2be13845fc00b 100644 --- a/api/envoy/type/matcher/regex.proto +++ b/api/envoy/type/matcher/regex.proto @@ -35,3 +35,29 @@ message RegexMatcher { // The regex match string. The string must be supported by the configured engine. string regex = 2 [(validate.rules).string = {min_bytes: 1}]; } + +// Describes how to match a string and then produce a new string using a regular +// expression and a substitution string. +message RegexMatchAndSubstitute { + // The regular expression used to find portions of a string (hereafter called + // the "subject string") that should be replaced. When a new string is + // produced during the substitution operation, the new string is initially + // the same as the subject string, but then all matches in the subject string + // are replaced by the substitution string. If replacing all matches isn't + // desired, regular expression anchors can be used to ensure a single match, + // so as to replace just one occurrence of a pattern. Capture groups can be + // used in the pattern to extract portions of the subject string, and then + // referenced in the substitution string. + RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the + // subject string during a substitution operation to produce a new string. + // Capture groups in the pattern can be referenced in the substitution + // string. Note, however, that the syntax for referring to capture groups is + // defined by the chosen regular expression engine. Google's `RE2 + // `_ regular expression engine uses a + // backslash followed by the capture group number to denote a numbered + // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers + // to capture group 2. + string substitution = 2; +} diff --git a/api/envoy/type/matcher/regex_match_and_subst.proto b/api/envoy/type/matcher/regex_match_and_subst.proto deleted file mode 100644 index d576a6ff1823f..0000000000000 --- a/api/envoy/type/matcher/regex_match_and_subst.proto +++ /dev/null @@ -1,37 +0,0 @@ -syntax = "proto3"; - -package envoy.type.matcher; - -import "envoy/type/matcher/regex.proto"; - -option java_package = "io.envoyproxy.envoy.type.matcher"; -option java_outer_classname = "RegexMatchAndSubstProto"; -option java_multiple_files = true; - -// [#protodoc-title: Regex match and substitute] - -// Describes how to match a string and then produce a new string using a regular -// expression and a substitution string. -message RegexMatchAndSubstitute { - // The regular expression used to find portions of a string (hereafter called - // the "subject string") that should be replaced. When a new string is - // produced during the substitution operation, the new string is initially - // the same as the subject string, but then all matches in the subject string - // are replaced by the substitution string. If replacing all matches isn't - // desired, regular expression anchors can be used to ensure a single match, - // so as to replace just one occurrence of a pattern. Capture groups can be - // used in the pattern to extract portions of the subject string, and then - // referenced in the substitution string. - RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the - // subject string during a substitution operation to produce a new string. - // Capture groups in the pattern can be referenced in the substitution - // string. Note, however, that the syntax for referring to capture groups is - // defined by the chosen regular expression engine. Google's `RE2 - // `_ regular expression engine uses a - // backslash followed by the capture group number to denote a numbered - // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers - // to capture group 2. - string substitution = 2; -} diff --git a/api/envoy/type/matcher/v3/regex.proto b/api/envoy/type/matcher/v3/regex.proto index bf62d7e32a559..acfb905ea01c5 100644 --- a/api/envoy/type/matcher/v3/regex.proto +++ b/api/envoy/type/matcher/v3/regex.proto @@ -42,3 +42,32 @@ message RegexMatcher { // The regex match string. The string must be supported by the configured engine. string regex = 2 [(validate.rules).string = {min_bytes: 1}]; } + +// Describes how to match a string and then produce a new string using a regular +// expression and a substitution string. +message RegexMatchAndSubstitute { + option (udpa.annotations.versioning).previous_message_type = + "envoy.type.matcher.RegexMatchAndSubstitute"; + + // The regular expression used to find portions of a string (hereafter called + // the "subject string") that should be replaced. When a new string is + // produced during the substitution operation, the new string is initially + // the same as the subject string, but then all matches in the subject string + // are replaced by the substitution string. If replacing all matches isn't + // desired, regular expression anchors can be used to ensure a single match, + // so as to replace just one occurrence of a pattern. Capture groups can be + // used in the pattern to extract portions of the subject string, and then + // referenced in the substitution string. + RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the + // subject string during a substitution operation to produce a new string. + // Capture groups in the pattern can be referenced in the substitution + // string. Note, however, that the syntax for referring to capture groups is + // defined by the chosen regular expression engine. Google's `RE2 + // `_ regular expression engine uses a + // backslash followed by the capture group number to denote a numbered + // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers + // to capture group 2. + string substitution = 2; +} diff --git a/api/envoy/type/matcher/v3/regex_match_and_subst.proto b/api/envoy/type/matcher/v3/regex_match_and_subst.proto deleted file mode 100644 index 16d8e70a911b2..0000000000000 --- a/api/envoy/type/matcher/v3/regex_match_and_subst.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto3"; - -package envoy.type.matcher.v3; - -import "envoy/type/matcher/v3/regex.proto"; - -import "udpa/annotations/versioning.proto"; - -option java_package = "io.envoyproxy.envoy.type.matcher.v3"; -option java_outer_classname = "RegexMatchAndSubstProto"; -option java_multiple_files = true; - -// [#protodoc-title: Regex match and substitute] - -// Describes how to match a string and then produce a new string using a regular -// expression and a substitution string. -message RegexMatchAndSubstitute { - option (udpa.annotations.versioning).previous_message_type = - "envoy.type.matcher.RegexMatchAndSubstitute"; - - // The regular expression used to find portions of a string (hereafter called - // the "subject string") that should be replaced. When a new string is - // produced during the substitution operation, the new string is initially - // the same as the subject string, but then all matches in the subject string - // are replaced by the substitution string. If replacing all matches isn't - // desired, regular expression anchors can be used to ensure a single match, - // so as to replace just one occurrence of a pattern. Capture groups can be - // used in the pattern to extract portions of the subject string, and then - // referenced in the substitution string. - RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the - // subject string during a substitution operation to produce a new string. - // Capture groups in the pattern can be referenced in the substitution - // string. Note, however, that the syntax for referring to capture groups is - // defined by the chosen regular expression engine. Google's `RE2 - // `_ regular expression engine uses a - // backslash followed by the capture group number to denote a numbered - // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers - // to capture group 2. - string substitution = 2; -} diff --git a/docs/root/api-v2/types/types.rst b/docs/root/api-v2/types/types.rst index a8040ee4c5799..b85889bf45a91 100644 --- a/docs/root/api-v2/types/types.rst +++ b/docs/root/api-v2/types/types.rst @@ -17,7 +17,6 @@ Types ../type/matcher/number.proto ../type/matcher/path.proto ../type/matcher/regex.proto - ../type/matcher/regex_match_and_subst.proto ../type/matcher/string.proto ../type/matcher/struct.proto ../type/matcher/value.proto diff --git a/docs/root/api-v3/types/types.rst b/docs/root/api-v3/types/types.rst index 411a2a3909339..f9c1cad3ea820 100644 --- a/docs/root/api-v3/types/types.rst +++ b/docs/root/api-v3/types/types.rst @@ -17,7 +17,6 @@ Types ../type/matcher/v3/number.proto ../type/matcher/v3/path.proto ../type/matcher/v3/regex.proto - ../type/matcher/v3/regex_match_and_subst.proto ../type/matcher/v3/string.proto ../type/matcher/v3/struct.proto ../type/matcher/v3/value.proto diff --git a/generated_api_shadow/envoy/api/v2/route/route_components.proto b/generated_api_shadow/envoy/api/v2/route/route_components.proto index 46b791274f63f..4312993396965 100644 --- a/generated_api_shadow/envoy/api/v2/route/route_components.proto +++ b/generated_api_shadow/envoy/api/v2/route/route_components.proto @@ -4,7 +4,6 @@ package envoy.api.v2.route; import "envoy/api/v2/core/base.proto"; import "envoy/type/matcher/regex.proto"; -import "envoy/type/matcher/regex_match_and_subst.proto"; import "envoy/type/matcher/string.proto"; import "envoy/type/percent.proto"; import "envoy/type/range.proto"; diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 9d6a782689f44..91b5d67fc7cc3 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -4,7 +4,6 @@ package envoy.config.route.v3; import "envoy/config/core/v3/base.proto"; import "envoy/type/matcher/v3/regex.proto"; -import "envoy/type/matcher/v3/regex_match_and_subst.proto"; import "envoy/type/matcher/v3/string.proto"; import "envoy/type/tracing/v3/custom_tag.proto"; import "envoy/type/v3/percent.proto"; diff --git a/generated_api_shadow/envoy/type/matcher/regex.proto b/generated_api_shadow/envoy/type/matcher/regex.proto index 2dd5bbe047cbb..2be13845fc00b 100644 --- a/generated_api_shadow/envoy/type/matcher/regex.proto +++ b/generated_api_shadow/envoy/type/matcher/regex.proto @@ -35,3 +35,29 @@ message RegexMatcher { // The regex match string. The string must be supported by the configured engine. string regex = 2 [(validate.rules).string = {min_bytes: 1}]; } + +// Describes how to match a string and then produce a new string using a regular +// expression and a substitution string. +message RegexMatchAndSubstitute { + // The regular expression used to find portions of a string (hereafter called + // the "subject string") that should be replaced. When a new string is + // produced during the substitution operation, the new string is initially + // the same as the subject string, but then all matches in the subject string + // are replaced by the substitution string. If replacing all matches isn't + // desired, regular expression anchors can be used to ensure a single match, + // so as to replace just one occurrence of a pattern. Capture groups can be + // used in the pattern to extract portions of the subject string, and then + // referenced in the substitution string. + RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the + // subject string during a substitution operation to produce a new string. + // Capture groups in the pattern can be referenced in the substitution + // string. Note, however, that the syntax for referring to capture groups is + // defined by the chosen regular expression engine. Google's `RE2 + // `_ regular expression engine uses a + // backslash followed by the capture group number to denote a numbered + // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers + // to capture group 2. + string substitution = 2; +} diff --git a/generated_api_shadow/envoy/type/matcher/regex_match_and_subst.proto b/generated_api_shadow/envoy/type/matcher/regex_match_and_subst.proto deleted file mode 100644 index d576a6ff1823f..0000000000000 --- a/generated_api_shadow/envoy/type/matcher/regex_match_and_subst.proto +++ /dev/null @@ -1,37 +0,0 @@ -syntax = "proto3"; - -package envoy.type.matcher; - -import "envoy/type/matcher/regex.proto"; - -option java_package = "io.envoyproxy.envoy.type.matcher"; -option java_outer_classname = "RegexMatchAndSubstProto"; -option java_multiple_files = true; - -// [#protodoc-title: Regex match and substitute] - -// Describes how to match a string and then produce a new string using a regular -// expression and a substitution string. -message RegexMatchAndSubstitute { - // The regular expression used to find portions of a string (hereafter called - // the "subject string") that should be replaced. When a new string is - // produced during the substitution operation, the new string is initially - // the same as the subject string, but then all matches in the subject string - // are replaced by the substitution string. If replacing all matches isn't - // desired, regular expression anchors can be used to ensure a single match, - // so as to replace just one occurrence of a pattern. Capture groups can be - // used in the pattern to extract portions of the subject string, and then - // referenced in the substitution string. - RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the - // subject string during a substitution operation to produce a new string. - // Capture groups in the pattern can be referenced in the substitution - // string. Note, however, that the syntax for referring to capture groups is - // defined by the chosen regular expression engine. Google's `RE2 - // `_ regular expression engine uses a - // backslash followed by the capture group number to denote a numbered - // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers - // to capture group 2. - string substitution = 2; -} diff --git a/generated_api_shadow/envoy/type/matcher/v3/regex.proto b/generated_api_shadow/envoy/type/matcher/v3/regex.proto index bf62d7e32a559..acfb905ea01c5 100644 --- a/generated_api_shadow/envoy/type/matcher/v3/regex.proto +++ b/generated_api_shadow/envoy/type/matcher/v3/regex.proto @@ -42,3 +42,32 @@ message RegexMatcher { // The regex match string. The string must be supported by the configured engine. string regex = 2 [(validate.rules).string = {min_bytes: 1}]; } + +// Describes how to match a string and then produce a new string using a regular +// expression and a substitution string. +message RegexMatchAndSubstitute { + option (udpa.annotations.versioning).previous_message_type = + "envoy.type.matcher.RegexMatchAndSubstitute"; + + // The regular expression used to find portions of a string (hereafter called + // the "subject string") that should be replaced. When a new string is + // produced during the substitution operation, the new string is initially + // the same as the subject string, but then all matches in the subject string + // are replaced by the substitution string. If replacing all matches isn't + // desired, regular expression anchors can be used to ensure a single match, + // so as to replace just one occurrence of a pattern. Capture groups can be + // used in the pattern to extract portions of the subject string, and then + // referenced in the substitution string. + RegexMatcher pattern = 1; + + // The string that should be substituted into matching portions of the + // subject string during a substitution operation to produce a new string. + // Capture groups in the pattern can be referenced in the substitution + // string. Note, however, that the syntax for referring to capture groups is + // defined by the chosen regular expression engine. Google's `RE2 + // `_ regular expression engine uses a + // backslash followed by the capture group number to denote a numbered + // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers + // to capture group 2. + string substitution = 2; +} diff --git a/generated_api_shadow/envoy/type/matcher/v3/regex_match_and_subst.proto b/generated_api_shadow/envoy/type/matcher/v3/regex_match_and_subst.proto deleted file mode 100644 index 16d8e70a911b2..0000000000000 --- a/generated_api_shadow/envoy/type/matcher/v3/regex_match_and_subst.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto3"; - -package envoy.type.matcher.v3; - -import "envoy/type/matcher/v3/regex.proto"; - -import "udpa/annotations/versioning.proto"; - -option java_package = "io.envoyproxy.envoy.type.matcher.v3"; -option java_outer_classname = "RegexMatchAndSubstProto"; -option java_multiple_files = true; - -// [#protodoc-title: Regex match and substitute] - -// Describes how to match a string and then produce a new string using a regular -// expression and a substitution string. -message RegexMatchAndSubstitute { - option (udpa.annotations.versioning).previous_message_type = - "envoy.type.matcher.RegexMatchAndSubstitute"; - - // The regular expression used to find portions of a string (hereafter called - // the "subject string") that should be replaced. When a new string is - // produced during the substitution operation, the new string is initially - // the same as the subject string, but then all matches in the subject string - // are replaced by the substitution string. If replacing all matches isn't - // desired, regular expression anchors can be used to ensure a single match, - // so as to replace just one occurrence of a pattern. Capture groups can be - // used in the pattern to extract portions of the subject string, and then - // referenced in the substitution string. - RegexMatcher pattern = 1; - - // The string that should be substituted into matching portions of the - // subject string during a substitution operation to produce a new string. - // Capture groups in the pattern can be referenced in the substitution - // string. Note, however, that the syntax for referring to capture groups is - // defined by the chosen regular expression engine. Google's `RE2 - // `_ regular expression engine uses a - // backslash followed by the capture group number to denote a numbered - // capture group. E.g., ``\1`` refers to capture group 1, and ``\2`` refers - // to capture group 2. - string substitution = 2; -} diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 76ddeba995b44..e3f0491d4f1d4 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -3553,6 +3553,28 @@ TEST_F(RouteMatcherTest, TestDuplicatePrefixWildcardDomainConfig) { "Only unique values for domains are permitted. Duplicate entry of domain bar.*"); } +TEST_F(RouteMatcherTest, TestPrefixAndRegexRewrites) { + const std::string yaml = R"EOF( +virtual_hosts: +- name: www2 + domains: ["bar.*"] + routes: + - match: { prefix: "/foo" } + route: + prefix_rewrite: / + regex_rewrite: + pattern: + google_re2: {} + regex: foo + substitution: bar + cluster: www2 + )EOF"; + + EXPECT_THROW_WITH_MESSAGE( + TestConfigImpl(parseRouteConfigurationFromV2Yaml(yaml), factory_context_, true), + EnvoyException, "Cannot specify both prefix_rewrite and regex_rewrite"); +} + TEST_F(RouteMatcherTest, TestDomainMatchOrderConfig) { const std::string yaml = R"EOF( virtual_hosts: