diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 6faba0e21f1b3..be0845d25159c 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -532,13 +532,22 @@ bool RouteEntryImplBase::matchRoute(const Http::RequestHeaderMap& headers, if (match_grpc_) { matches &= Grpc::Common::isGrpcRequestHeaders(headers); + if (!matches) { + return false; + } } matches &= Http::HeaderUtility::matchHeaders(headers, config_headers_); + if (!matches) { + return false; + } if (!config_query_parameters_.empty()) { Http::Utility::QueryParams query_parameters = Http::Utility::parseQueryString(headers.getPathValue()); matches &= ConfigUtility::matchQueryParams(query_parameters, config_query_parameters_); + if (!matches) { + return false; + } } matches &= evaluateTlsContextMatch(stream_info); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index dae0573b262b2..083d0853e5cec 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6898,6 +6898,131 @@ TEST_F(RouteMatcherTest, HeaderMatchedRoutingV2) { } } +TEST_F(RouteMatcherTest, EnsureMatchingAllConditions) { + const std::string yaml = R"EOF( +virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: + prefix: "/" + tls_context: + presented: true + runtime_fraction: + default_value: + numerator: 50 + denominator: MILLION + runtime_key: "bogus_key" + headers: + - name: :path + string_match: + prefix: "/foo" + query_parameters: + - name: param + string_match: + exact: test + grpc: {} + route: + cluster: bar_cluster + - match: + prefix: "/" + route: + cluster: foo_cluster + )EOF"; + + factory_context_.cluster_manager_.initializeClusters({"foo_cluster", "bar_cluster"}, {}); + + NiceMock stream_info; + auto connection_info = std::make_shared(); + EXPECT_CALL(*connection_info, peerCertificatePresented()).WillRepeatedly(Return(true)); + EXPECT_CALL(*connection_info, peerCertificateValidated()).WillRepeatedly(Return(true)); + stream_info.downstream_address_provider_->setSslConnection(connection_info); + + // all the conditions are matched. + { + Runtime::MockSnapshot snapshot; + ON_CALL(factory_context_.runtime_loader_, snapshot()).WillByDefault(ReturnRef(snapshot)); + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, false); + + EXPECT_CALL(snapshot, + featureEnabled("bogus_key", + testing::Matcher(_), 41)) + .WillRepeatedly(Return(true)); + auto headers = genHeaders("www.lyft.com", "/foo?param=test", "GET"); + headers.addCopy("content-type", "application/grpc"); + EXPECT_EQ("bar_cluster", config.route(headers, stream_info, 41)->routeEntry()->clusterName()); + } + // not a grpc + { + Runtime::MockSnapshot snapshot; + ON_CALL(factory_context_.runtime_loader_, snapshot()).WillByDefault(ReturnRef(snapshot)); + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, false); + + EXPECT_CALL(snapshot, + featureEnabled("bogus_key", + testing::Matcher(_), 41)) + .WillRepeatedly(Return(true)); + auto headers = genHeaders("www.lyft.com", "/foo?param=test", "GET"); + EXPECT_EQ("foo_cluster", config.route(headers, stream_info, 41)->routeEntry()->clusterName()); + } + // runtime_fraction isn't matched. + { + Runtime::MockSnapshot snapshot; + ON_CALL(factory_context_.runtime_loader_, snapshot()).WillByDefault(ReturnRef(snapshot)); + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, false); + + EXPECT_CALL(snapshot, + featureEnabled("bogus_key", + testing::Matcher(_), 43)) + .WillRepeatedly(Return(false)); + auto headers = genHeaders("www.lyft.com", "/foo?param=test", "GET"); + headers.addCopy("content-type", "application/grpc"); + EXPECT_EQ("foo_cluster", config.route(headers, stream_info, 43)->routeEntry()->clusterName()); + } + // header isn't matched. + { + Runtime::MockSnapshot snapshot; + ON_CALL(factory_context_.runtime_loader_, snapshot()).WillByDefault(ReturnRef(snapshot)); + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, false); + + EXPECT_CALL(snapshot, + featureEnabled("bogus_key", + testing::Matcher(_), 41)) + .WillRepeatedly(Return(true)); + auto headers = genHeaders("www.lyft.com", "/?param=test", "GET"); + headers.addCopy("content-type", "application/grpc"); + EXPECT_EQ("foo_cluster", config.route(headers, stream_info, 41)->routeEntry()->clusterName()); + } + // no tls. + { + Runtime::MockSnapshot snapshot; + ON_CALL(factory_context_.runtime_loader_, snapshot()).WillByDefault(ReturnRef(snapshot)); + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, false); + + EXPECT_CALL(snapshot, + featureEnabled("bogus_key", + testing::Matcher(_), 41)) + .WillRepeatedly(Return(true)); + auto headers = genHeaders("www.lyft.com", "/foo?param=test", "GET"); + headers.addCopy("content-type", "application/grpc"); + EXPECT_EQ("foo_cluster", config.route(headers, 41)->routeEntry()->clusterName()); + } + // missing query parameter. + { + Runtime::MockSnapshot snapshot; + ON_CALL(factory_context_.runtime_loader_, snapshot()).WillByDefault(ReturnRef(snapshot)); + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, false); + + EXPECT_CALL(snapshot, + featureEnabled("bogus_key", + testing::Matcher(_), 41)) + .WillRepeatedly(Return(true)); + auto headers = genHeaders("www.lyft.com", "/foo", "GET"); + headers.addCopy("content-type", "application/grpc"); + EXPECT_EQ("foo_cluster", config.route(headers, stream_info, 41)->routeEntry()->clusterName()); + } +} + // Test Route Matching based on connection Tls Context. // Validate configured and default settings are routed to the correct cluster. TEST_F(RouteMatcherTest, TlsContextMatching) {