|
11 | 11 | #include "test/common/grpc/grpc_client_integration.h" |
12 | 12 | #include "test/config/v2_link_hacks.h" |
13 | 13 | #include "test/integration/http_integration.h" |
| 14 | +#include "test/integration/http_protocol_integration.h" |
14 | 15 | #include "test/integration/scoped_rds.h" |
15 | 16 | #include "test/integration/vhds.h" |
16 | 17 | #include "test/test_common/printers.h" |
@@ -761,5 +762,212 @@ TEST_P(OnDemandVhdsIntegrationTest, AttemptAddingDuplicateDomainNames) { |
761 | 762 | cleanupUpstreamAndDownstream(); |
762 | 763 | } |
763 | 764 |
|
| 765 | +// Test class for VHDS on-demand updates with request bodies |
| 766 | +class OnDemandVhdsWithBodyIntegrationTest |
| 767 | + : public testing::TestWithParam< |
| 768 | + std::tuple<HttpProtocolTestParams, std::tuple<Network::Address::IpVersion, |
| 769 | + Grpc::ClientType, Grpc::LegacyOrUnified>>>, |
| 770 | + public HttpIntegrationTest { |
| 771 | +public: |
| 772 | + using ParamType = |
| 773 | + std::tuple<HttpProtocolTestParams, |
| 774 | + std::tuple<Network::Address::IpVersion, Grpc::ClientType, Grpc::LegacyOrUnified>>; |
| 775 | + |
| 776 | + const HttpProtocolTestParams& httpProtocolParams() const { return std::get<0>(GetParam()); } |
| 777 | + |
| 778 | + OnDemandVhdsWithBodyIntegrationTest() |
| 779 | + : HttpIntegrationTest(httpProtocolParams().downstream_protocol, httpProtocolParams().version, |
| 780 | + ConfigHelper::httpProxyConfig( |
| 781 | + /*downstream_is_quic=*/httpProtocolParams().downstream_protocol == |
| 782 | + Http::CodecType::HTTP3)), |
| 783 | + use_universal_header_validator_(httpProtocolParams().use_universal_header_validator) { |
| 784 | + setupHttp2ImplOverrides(httpProtocolParams().http2_implementation); |
| 785 | + config_helper_.addRuntimeOverride("envoy.reloadable_features.enable_universal_header_validator", |
| 786 | + use_universal_header_validator_ ? "true" : "false"); |
| 787 | + use_lds_ = false; |
| 788 | + config_helper_.addRuntimeOverride( |
| 789 | + "envoy.reloadable_features.unified_mux", |
| 790 | + std::get<2>(std::get<1>(GetParam())) == Grpc::LegacyOrUnified::Unified ? "true" : "false"); |
| 791 | + config_helper_.prependFilter(R"EOF( |
| 792 | + name: envoy.filters.http.on_demand |
| 793 | + )EOF"); |
| 794 | + } |
| 795 | + |
| 796 | + void SetUp() override { |
| 797 | + setDownstreamProtocol(httpProtocolParams().downstream_protocol); |
| 798 | + setUpstreamProtocol(httpProtocolParams().upstream_protocol); |
| 799 | + } |
| 800 | + |
| 801 | + void TearDown() override { cleanUpXdsConnection(); } |
| 802 | + |
| 803 | + std::string virtualHostYaml(const std::string& name, const std::string& domain) { |
| 804 | + return fmt::format(R"EOF( |
| 805 | +name: {} |
| 806 | +domains: [{}] |
| 807 | +routes: |
| 808 | +- match: {{ prefix: "/" }} |
| 809 | + route: {{ cluster: "cluster_0" }} |
| 810 | +)EOF", |
| 811 | + name, domain); |
| 812 | + } |
| 813 | + |
| 814 | + std::string vhdsRequestResourceName(const std::string& host_header) { |
| 815 | + return "my_route/" + host_header; |
| 816 | + } |
| 817 | + |
| 818 | + envoy::config::route::v3::VirtualHost buildVirtualHost(const std::string& name, |
| 819 | + const std::string& domain) { |
| 820 | + return TestUtility::parseYaml<envoy::config::route::v3::VirtualHost>( |
| 821 | + virtualHostYaml(name, domain)); |
| 822 | + } |
| 823 | + |
| 824 | + void initialize() override { |
| 825 | + setUpstreamCount(2); // xds_cluster and cluster_0 |
| 826 | + setUpstreamProtocol(Http::CodecType::HTTP2); // xDS uses gRPC uses HTTP2 |
| 827 | + |
| 828 | + const auto ip_version = httpProtocolParams().version; |
| 829 | + config_helper_.addConfigModifier([ip_version]( |
| 830 | + envoy::config::bootstrap::v3::Bootstrap& bootstrap) { |
| 831 | + // Add xds_cluster for VHDS |
| 832 | + auto* xds_cluster = bootstrap.mutable_static_resources()->add_clusters(); |
| 833 | + xds_cluster->MergeFrom(ConfigHelper::buildStaticCluster( |
| 834 | + "xds_cluster", /*port=*/0, Network::Test::getLoopbackAddressString(ip_version))); |
| 835 | + ConfigHelper::setHttp2(*xds_cluster); |
| 836 | + |
| 837 | + auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); |
| 838 | + auto* filter_chain = listener->mutable_filter_chains(0); |
| 839 | + auto* config_blob = filter_chain->mutable_filters(0)->mutable_typed_config(); |
| 840 | + |
| 841 | + ASSERT_TRUE(config_blob->Is<envoy::extensions::filters::network::http_connection_manager::v3:: |
| 842 | + HttpConnectionManager>()); |
| 843 | + auto hcm_config = MessageUtil::anyConvert< |
| 844 | + envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager>( |
| 845 | + *config_blob); |
| 846 | + |
| 847 | + // Use RDS instead of static route_config so Envoy connects to xDS server |
| 848 | + hcm_config.clear_route_config(); |
| 849 | + auto* rds_config = hcm_config.mutable_rds(); |
| 850 | + rds_config->set_route_config_name("my_route"); |
| 851 | + auto* rds_config_source = rds_config->mutable_config_source(); |
| 852 | + rds_config_source->mutable_api_config_source()->set_api_type( |
| 853 | + envoy::config::core::v3::ApiConfigSource::GRPC); |
| 854 | + rds_config_source->mutable_api_config_source() |
| 855 | + ->add_grpc_services() |
| 856 | + ->mutable_envoy_grpc() |
| 857 | + ->set_cluster_name("xds_cluster"); |
| 858 | + |
| 859 | + config_blob->PackFrom(hcm_config); |
| 860 | + }); |
| 861 | + |
| 862 | + HttpIntegrationTest::initialize(); |
| 863 | + |
| 864 | + test_server_->waitUntilListenersReady(); |
| 865 | + registerTestServerPorts({"http"}); |
| 866 | + |
| 867 | + // Set up xDS connection (xds_cluster is the second cluster, so it maps to fake_upstreams_[1]) |
| 868 | + auto result = fake_upstreams_[1]->waitForHttpConnection(*dispatcher_, xds_connection_); |
| 869 | + RELEASE_ASSERT(result, result.message()); |
| 870 | + result = xds_connection_->waitForNewStream(*dispatcher_, xds_stream_); |
| 871 | + RELEASE_ASSERT(result, result.message()); |
| 872 | + xds_stream_->startGrpcStream(); |
| 873 | + |
| 874 | + EXPECT_TRUE(compareSotwDiscoveryRequest(Config::TestTypeUrl::get().RouteConfiguration, "", |
| 875 | + {"my_route"}, true)); |
| 876 | + envoy::config::route::v3::RouteConfiguration route_config; |
| 877 | + route_config.set_name("my_route"); |
| 878 | + auto* vhds_config_source = route_config.mutable_vhds()->mutable_config_source(); |
| 879 | + vhds_config_source->mutable_api_config_source()->set_api_type( |
| 880 | + envoy::config::core::v3::ApiConfigSource::DELTA_GRPC); |
| 881 | + vhds_config_source->mutable_api_config_source() |
| 882 | + ->add_grpc_services() |
| 883 | + ->mutable_envoy_grpc() |
| 884 | + ->set_cluster_name("xds_cluster"); |
| 885 | + sendSotwDiscoveryResponse<envoy::config::route::v3::RouteConfiguration>( |
| 886 | + Config::TestTypeUrl::get().RouteConfiguration, {route_config}, "1"); |
| 887 | + |
| 888 | + result = xds_connection_->waitForNewStream(*dispatcher_, vhds_stream_); |
| 889 | + RELEASE_ASSERT(result, result.message()); |
| 890 | + vhds_stream_->startGrpcStream(); |
| 891 | + |
| 892 | + EXPECT_TRUE(compareDeltaDiscoveryRequest(Config::TestTypeUrl::get().VirtualHost, {}, {}, |
| 893 | + vhds_stream_.get())); |
| 894 | + } |
| 895 | + |
| 896 | + FakeStreamPtr vhds_stream_; |
| 897 | + |
| 898 | +protected: |
| 899 | + const bool use_universal_header_validator_; |
| 900 | +}; |
| 901 | + |
| 902 | +INSTANTIATE_TEST_SUITE_P( |
| 903 | + ProtocolsAndGrpcTypes, OnDemandVhdsWithBodyIntegrationTest, |
| 904 | + testing::Combine( |
| 905 | + testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParamsWithoutHTTP3()), |
| 906 | + UNIFIED_LEGACY_GRPC_CLIENT_INTEGRATION_PARAMS), |
| 907 | + [](const testing::TestParamInfo< |
| 908 | + std::tuple<HttpProtocolTestParams, std::tuple<Network::Address::IpVersion, Grpc::ClientType, |
| 909 | + Grpc::LegacyOrUnified>>>& info) { |
| 910 | + return absl::StrCat( |
| 911 | + HttpProtocolIntegrationTest::protocolTestParamsToString( |
| 912 | + testing::TestParamInfo<HttpProtocolTestParams>(std::get<0>(info.param), 0)), |
| 913 | + "_", |
| 914 | + Grpc::UnifiedOrLegacyMuxIntegrationParamTest::protocolTestParamsToString( |
| 915 | + testing::TestParamInfo< |
| 916 | + std::tuple<Network::Address::IpVersion, Grpc::ClientType, Grpc::LegacyOrUnified>>( |
| 917 | + std::get<1>(info.param), 0))); |
| 918 | + }); |
| 919 | + |
| 920 | +// Test VHDS on-demand update with a request body |
| 921 | +TEST_P(OnDemandVhdsWithBodyIntegrationTest, VhdsOnDemandUpdateWithBody) { |
| 922 | + // Unified mux has issues processing on-demand VHDS responses in this test case. |
| 923 | + // Skip Unified mux until this is resolved. |
| 924 | + const bool is_unified_mux = |
| 925 | + std::get<2>(std::get<1>(GetParam())) == Grpc::LegacyOrUnified::Unified; |
| 926 | + if (is_unified_mux) { |
| 927 | + GTEST_SKIP() << "Unified mux does not properly handle on-demand VHDS updates in this test"; |
| 928 | + } |
| 929 | + initialize(); |
| 930 | + // Make a request with body to an unknown virtual host |
| 931 | + codec_client_ = makeHttpConnection(lookupPort("http")); |
| 932 | + Http::TestRequestHeaderMapImpl request_headers{{":method", "POST"}, |
| 933 | + {":path", "/"}, |
| 934 | + {":scheme", "http"}, |
| 935 | + {":authority", "vhost.with.body"}, |
| 936 | + {"x-lyft-user-id", "123"}}; |
| 937 | + const std::string request_body = "test request body"; |
| 938 | + IntegrationStreamDecoderPtr response = |
| 939 | + codec_client_->makeRequestWithBody(request_headers, request_body, true); |
| 940 | + |
| 941 | + // Verify VHDS on-demand request is sent |
| 942 | + EXPECT_TRUE(compareDeltaDiscoveryRequest(Config::TestTypeUrl::get().VirtualHost, |
| 943 | + {vhdsRequestResourceName("vhost.with.body")}, {}, |
| 944 | + vhds_stream_.get())); |
| 945 | + |
| 946 | + // Send VHDS response with the virtual host |
| 947 | + sendDeltaDiscoveryResponse<envoy::config::route::v3::VirtualHost>( |
| 948 | + Config::TestTypeUrl::get().VirtualHost, |
| 949 | + {buildVirtualHost("my_route/vhost_with_body", "vhost.with.body")}, {}, "2", |
| 950 | + vhds_stream_.get(), {"my_route/vhost.with.body"}); |
| 951 | + |
| 952 | + // Wait for VHDS ACK to ensure the response is processed |
| 953 | + EXPECT_TRUE(compareDeltaDiscoveryRequest(Config::TestTypeUrl::get().VirtualHost, {}, {}, |
| 954 | + vhds_stream_.get())); |
| 955 | + |
| 956 | + // Wait for upstream request (cluster_0 is the first cluster, so it maps to fake_upstreams_[0]) |
| 957 | + waitForNextUpstreamRequest(0); |
| 958 | + |
| 959 | + // Verify the request body was received correctly |
| 960 | + EXPECT_TRUE(upstream_request_->complete()); |
| 961 | + EXPECT_EQ(request_body, upstream_request_->body().toString()); |
| 962 | + |
| 963 | + // Send response |
| 964 | + upstream_request_->encodeHeaders(default_response_headers_, true); |
| 965 | + |
| 966 | + response->waitForHeaders(); |
| 967 | + EXPECT_EQ("200", response->headers().getStatusValue()); |
| 968 | + |
| 969 | + cleanupUpstreamAndDownstream(); |
| 970 | +} |
| 971 | + |
764 | 972 | } // namespace |
765 | 973 | } // namespace Envoy |
0 commit comments