diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index 4d92675968fcb..65fd4ab5b72f5 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -312,6 +312,34 @@ class MetadataMatchCriteria { metadataMatchCriteria() const PURE; }; +/** + * Type of path matching that a route entry uses. + */ +enum class PathMatchType { + None, + Prefix, + Exact, + Regex, +}; + +/** + * Criterion that a route entry uses for matching a particular path. + */ +class PathMatchCriterion { +public: + virtual ~PathMatchCriterion() {} + + /** + * @return PathMatchType type of path match. + */ + virtual PathMatchType matchType() const PURE; + + /** + * @return const std::string& the string with which to compare paths. + */ + virtual const std::string& matcher() const PURE; +}; + /** * An individual resolved route entry. */ @@ -421,6 +449,11 @@ class RouteEntry : public ResponseEntry { * this route. */ virtual const envoy::api::v2::core::Metadata& metadata() const PURE; + + /** + * @return const PathMatchCriterion& the match criterion for this route. + */ + virtual const PathMatchCriterion& pathMatchCriterion() const PURE; }; /** diff --git a/source/common/http/async_client_impl.cc b/source/common/http/async_client_impl.cc index fcf21fad469d8..95d0ba32bf04f 100644 --- a/source/common/http/async_client_impl.cc +++ b/source/common/http/async_client_impl.cc @@ -23,6 +23,8 @@ const AsyncStreamImpl::NullRateLimitPolicy AsyncStreamImpl::NullVirtualHost::rat const AsyncStreamImpl::NullConfig AsyncStreamImpl::NullVirtualHost::route_configuration_; const std::multimap AsyncStreamImpl::RouteEntryImpl::opaque_config_; const envoy::api::v2::core::Metadata AsyncStreamImpl::RouteEntryImpl::metadata_; +const AsyncStreamImpl::NullPathMatchCriterion + AsyncStreamImpl::RouteEntryImpl::path_match_criterion_; const std::list AsyncStreamImpl::NullConfig::internal_only_headers_; AsyncClientImpl::AsyncClientImpl(const Upstream::ClusterInfo& cluster, Stats::Store& stats_store, diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index 1a2ba32a490da..585a55b77f9fb 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -151,6 +151,11 @@ class AsyncStreamImpl : public AsyncClient::Stream, static const NullConfig route_configuration_; }; + struct NullPathMatchCriterion : public Router::PathMatchCriterion { + Router::PathMatchType matchType() const override { return Router::PathMatchType::None; } + const std::string& matcher() const override { return EMPTY_STRING; } + }; + struct RouteEntryImpl : public Router::RouteEntry { RouteEntryImpl(const std::string& cluster_name, const Optional& timeout) @@ -191,6 +196,9 @@ class AsyncStreamImpl : public AsyncClient::Stream, bool useWebSocket() const override { return false; } bool includeVirtualHostRateLimits() const override { return true; } const envoy::api::v2::core::Metadata& metadata() const override { return metadata_; } + const Router::PathMatchCriterion& pathMatchCriterion() const override { + return path_match_criterion_; + } static const NullRateLimitPolicy rate_limit_policy_; static const NullRetryPolicy retry_policy_; @@ -198,6 +206,7 @@ class AsyncStreamImpl : public AsyncClient::Stream, static const NullVirtualHost virtual_host_; static const std::multimap opaque_config_; static const envoy::api::v2::core::Metadata metadata_; + static const NullPathMatchCriterion path_match_criterion_; const std::string& cluster_name_; Optional timeout_; diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index bf7ac7b66a37b..ad382ebd48300 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -592,7 +592,8 @@ RegexRouteEntryImpl::RegexRouteEntryImpl(const VirtualHostImpl& vhost, const envoy::api::v2::route::Route& route, Runtime::Loader& loader) : RouteEntryImplBase(vhost, route, loader), - regex_(RegexUtil::parseRegex(route.match().regex().c_str())) {} + regex_(RegexUtil::parseRegex(route.match().regex().c_str())), + regex_str_(route.match().regex()) {} void RegexRouteEntryImpl::finalizeRequestHeaders( Http::HeaderMap& headers, const RequestInfo::RequestInfo& request_info) const { diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index 273650d7717f8..0bbe1b503b464 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -291,6 +291,7 @@ class RouteEntryImplBase : public RouteEntry, public Matchable, public DirectResponseEntry, public Route, + public PathMatchCriterion, public std::enable_shared_from_this { public: /** @@ -335,6 +336,7 @@ class RouteEntryImplBase : public RouteEntry, } bool includeVirtualHostRateLimits() const override { return include_vh_rate_limits_; } const envoy::api::v2::core::Metadata& metadata() const override { return metadata_; } + const PathMatchCriterion& pathMatchCriterion() const override { return *this; } // Router::DirectResponseEntry std::string newPath(const Http::HeaderMap& headers) const override; @@ -409,6 +411,9 @@ class RouteEntryImplBase : public RouteEntry, return parent_->includeVirtualHostRateLimits(); } const envoy::api::v2::core::Metadata& metadata() const override { return parent_->metadata(); } + const PathMatchCriterion& pathMatchCriterion() const override { + return parent_->pathMatchCriterion(); + } // Router::Route const DirectResponseEntry* directResponseEntry() const override { return nullptr; } @@ -513,6 +518,10 @@ class PrefixRouteEntryImpl : public RouteEntryImplBase { void finalizeRequestHeaders(Http::HeaderMap& headers, const RequestInfo::RequestInfo& request_info) const override; + // Router::PathMatchCriterion + const std::string& matcher() const override { return prefix_; } + PathMatchType matchType() const override { return PathMatchType::Prefix; } + // Router::Matchable RouteConstSharedPtr matches(const Http::HeaderMap& headers, uint64_t random_value) const override; @@ -532,6 +541,10 @@ class PathRouteEntryImpl : public RouteEntryImplBase { void finalizeRequestHeaders(Http::HeaderMap& headers, const RequestInfo::RequestInfo& request_info) const override; + // Router::PathMatchCriterion + const std::string& matcher() const override { return path_; } + PathMatchType matchType() const override { return PathMatchType::Exact; } + // Router::Matchable RouteConstSharedPtr matches(const Http::HeaderMap& headers, uint64_t random_value) const override; @@ -551,11 +564,16 @@ class RegexRouteEntryImpl : public RouteEntryImplBase { void finalizeRequestHeaders(Http::HeaderMap& headers, const RequestInfo::RequestInfo& request_info) const override; + // Router::PathMatchCriterion + const std::string& matcher() const override { return regex_str_; } + PathMatchType matchType() const override { return PathMatchType::Regex; } + // Router::Matchable RouteConstSharedPtr matches(const Http::HeaderMap& headers, uint64_t random_value) const override; private: const std::regex regex_; + const std::string regex_str_; }; /** diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index 746f78e9a1c69..1ce5777b8f13f 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -863,7 +863,7 @@ TEST_F(AsyncClientImplTest, WatermarkCallbacks) { EXPECT_CALL(stream_callbacks_, onReset()); } -TEST_F(AsyncClientImplTest, NullRouteConfigTest) { +TEST_F(AsyncClientImplTest, RdsGettersTest) { TestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); AsyncClient::Stream* stream = @@ -872,8 +872,12 @@ TEST_F(AsyncClientImplTest, NullRouteConfigTest) { Http::StreamDecoderFilterCallbacks* filter_callbacks = static_cast(stream); auto route = filter_callbacks->route(); + ASSERT_NE(nullptr, route); auto route_entry = route->routeEntry(); ASSERT_NE(nullptr, route_entry); + auto& path_match_criterion = route_entry->pathMatchCriterion(); + EXPECT_EQ("", path_match_criterion.matcher()); + EXPECT_EQ(Router::PathMatchType::None, path_match_criterion.matchType()); const auto& route_config = route_entry->virtualHost().routeConfig(); EXPECT_EQ("", route_config.name()); EXPECT_EQ(0, route_config.internalOnlyHeaders().size()); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index f1e88c72d5e6a..ff91e8c60453d 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -3773,6 +3773,16 @@ name: foo EnvoyException, "response body size is 4097 bytes; maximum is 4096"); } +void checkPathMatchCriterion(const Route* route, const std::string& expected_matcher, + PathMatchType expected_type) { + ASSERT_NE(nullptr, route); + const auto route_entry = route->routeEntry(); + ASSERT_NE(nullptr, route_entry); + const auto& match_criterion = route_entry->pathMatchCriterion(); + EXPECT_EQ(expected_matcher, match_criterion.matcher()); + EXPECT_EQ(expected_type, match_criterion.matchType()); +} + TEST(RouteConfigurationV2, RouteConfigGetters) { std::string yaml = R"EOF( name: foo @@ -3780,6 +3790,10 @@ name: foo - name: bar domains: ["*"] routes: + - match: { regex: "/rege[xy]" } + route: { cluster: ww2 } + - match: { path: "/exact-path" } + route: { cluster: ww2 } - match: { prefix: "/"} route: { cluster: www2 } metadata: { filter_metadata: { com.bar.foo: { baz: test_value } } } @@ -3787,10 +3801,16 @@ name: foo NiceMock runtime; NiceMock cm; - ConfigImpl config(parseRouteConfigurationFromV2Yaml(yaml), runtime, cm, true); + const ConfigImpl config(parseRouteConfigurationFromV2Yaml(yaml), runtime, cm, true); - auto* route_entry = config.route(genHeaders("www.foo.com", "/", "GET"), 0)->routeEntry(); + checkPathMatchCriterion(config.route(genHeaders("www.foo.com", "/regex", "GET"), 0).get(), + "/rege[xy]", PathMatchType::Regex); + checkPathMatchCriterion(config.route(genHeaders("www.foo.com", "/exact-path", "GET"), 0).get(), + "/exact-path", PathMatchType::Exact); + const auto route = config.route(genHeaders("www.foo.com", "/", "GET"), 0); + checkPathMatchCriterion(route.get(), "/", PathMatchType::Prefix); + const auto route_entry = route->routeEntry(); const auto& metadata = route_entry->metadata(); EXPECT_EQ("test_value", diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index 371d1b3484890..6f8b9e23a64a2 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -217,6 +217,7 @@ class MockRouteEntry : public RouteEntry { MOCK_CONST_METHOD0(includeVirtualHostRateLimits, bool()); MOCK_CONST_METHOD0(corsPolicy, const CorsPolicy*()); MOCK_CONST_METHOD0(metadata, const envoy::api::v2::core::Metadata&()); + MOCK_CONST_METHOD0(pathMatchCriterion, const PathMatchCriterion&()); std::string cluster_name_{"fake_cluster"}; std::multimap opaque_config_;