From ae5d9c4bd5f56a25149b8e2b3bcb0747b0228670 Mon Sep 17 00:00:00 2001 From: htuch Date: Thu, 10 Dec 2020 14:54:05 -0500 Subject: [PATCH 01/49] format: fix Times(1) nit. (#14353) This removes one source of constant nit nagging in PR reviews. The rewrite validity is based on https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#cardinalities-how-many-times-will-it-be-called Testing: Added check_format test. Signed-off-by: Harvey Tuch --- .../common/access_log/access_log_impl_test.cc | 4 +- test/common/conn_pool/conn_pool_base_test.cc | 6 +- test/common/event/file_event_impl_test.cc | 10 +-- test/common/http/async_client_impl_test.cc | 20 ++--- test/common/http/conn_manager_impl_test.cc | 28 +++---- test/common/http/conn_manager_utility_test.cc | 49 +++++------ test/common/http/http1/codec_impl_test.cc | 2 +- test/common/http/http2/codec_impl_test.cc | 14 ++-- test/common/http/http2/conn_pool_test.cc | 2 +- .../request_id_extension_uuid_impl_test.cc | 6 +- test/common/network/connection_impl_test.cc | 40 ++++----- test/common/router/rds_impl_test.cc | 2 +- test/common/router/router_test.cc | 54 ++++++------ test/common/router/scoped_rds_test.cc | 14 ++-- test/common/runtime/runtime_impl_test.cc | 4 +- test/common/tcp_proxy/tcp_proxy_test.cc | 3 +- .../upstream/cluster_manager_impl_test.cc | 8 +- test/common/upstream/eds_test.cc | 2 +- .../upstream/health_checker_impl_test.cc | 62 +++++++------- .../grpc/grpc_access_log_impl_test.cc | 4 +- .../clusters/redis/redis_cluster_test.cc | 26 +++--- .../aws/credentials_provider_impl_test.cc | 6 +- test/extensions/common/wasm/wasm_test.cc | 4 +- .../ext_authz/check_request_utils_test.cc | 8 +- .../filters/common/rbac/engine_impl_test.cc | 2 +- .../aws_request_signing_filter_test.cc | 4 +- .../filters/http/cache/cache_filter_test.cc | 2 +- .../filters/http/cdn_loop/filter_test.cc | 6 +- .../filters/http/common/jwks_fetcher_test.cc | 12 +-- .../http/compressor/compressor_filter_test.cc | 6 +- .../filters/http/dynamo/dynamo_filter_test.cc | 12 +-- .../filters/http/ext_authz/config_test.cc | 18 ++-- .../filters/http/ext_authz/ext_authz_test.cc | 30 +++---- .../filters/http/ext_proc/client_test.cc | 12 +-- .../filters/http/ext_proc/config_test.cc | 2 +- .../http/ip_tagging/ip_tagging_filter_test.cc | 24 +++--- .../http/jwt_authn/all_verifier_test.cc | 84 +++++++++---------- .../http/jwt_authn/authenticator_test.cc | 12 +-- .../http/jwt_authn/group_verifier_test.cc | 48 +++++------ .../http/jwt_authn/provider_verifier_test.cc | 10 +-- .../http/local_ratelimit/config_test.cc | 6 +- .../filters/http/lua/lua_filter_test.cc | 2 +- .../filters/http/oauth2/filter_test.cc | 24 +++--- .../filters/http/ratelimit/ratelimit_test.cc | 43 ++++------ .../filters/http/router/config_test.cc | 4 +- .../filters/http/wasm/wasm_filter_test.cc | 37 ++++---- .../http_inspector_config_test.cc | 2 +- .../http_inspector/http_inspector_test.cc | 2 +- .../proxy_protocol/proxy_protocol_test.cc | 4 +- .../network/dubbo_proxy/conn_manager_test.cc | 58 ++++++------- .../network/dubbo_proxy/decoder_test.cc | 14 ++-- .../network/dubbo_proxy/router_test.cc | 10 +-- .../network/ext_authz/ext_authz_test.cc | 2 +- .../http_connection_manager/config_test.cc | 4 +- .../postgres_proxy/postgres_decoder_test.cc | 10 +-- .../rocketmq_proxy/conn_manager_test.cc | 2 +- .../network/thrift_proxy/decoder_test.cc | 6 +- .../filters/ratelimit/ratelimit_test.cc | 5 +- .../filters/udp/dns_filter/dns_filter_test.cc | 18 ++-- .../common/statsd/udp_statsd_test.cc | 6 +- .../tracers/skywalking/tracer_test.cc | 2 +- .../common/passthrough_test.cc | 19 ++--- .../proxy_protocol/proxy_protocol_test.cc | 24 +++--- .../transport_sockets/tls/handshaker_test.cc | 6 +- test/server/admin/runtime_handler_test.cc | 4 +- test/server/connection_handler_test.cc | 40 ++++----- test/server/filter_chain_manager_impl_test.cc | 2 +- test/server/hot_restart_impl_test.cc | 4 +- test/server/listener_manager_impl_test.cc | 10 +-- tools/code_format/check_format.py | 20 +++++ tools/code_format/check_format_test_helper.py | 2 + .../testdata/check_format/code_conventions.cc | 11 +++ .../check_format/code_conventions.cc.gold | 10 +++ 73 files changed, 534 insertions(+), 541 deletions(-) create mode 100644 tools/testdata/check_format/code_conventions.cc create mode 100644 tools/testdata/check_format/code_conventions.cc.gold diff --git a/test/common/access_log/access_log_impl_test.cc b/test/common/access_log/access_log_impl_test.cc index b7d27d0c850c..88238473b512 100644 --- a/test/common/access_log/access_log_impl_test.cc +++ b/test/common/access_log/access_log_impl_test.cc @@ -1311,7 +1311,7 @@ name: accesslog const InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); - EXPECT_CALL(*file_, write(_)).Times(1); + EXPECT_CALL(*file_, write(_)); log->log(&request_headers_, &response_headers_, &response_trailers_, stream_info); fields_c["c"].set_bool_value(false); @@ -1397,7 +1397,7 @@ name: accesslog const InstanceSharedPtr default_true_log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(default_true_yaml), context_); - EXPECT_CALL(*file_, write(_)).Times(1); + EXPECT_CALL(*file_, write(_)); default_true_log->log(&request_headers_, &response_headers_, &response_trailers_, stream_info); } diff --git a/test/common/conn_pool/conn_pool_base_test.cc b/test/common/conn_pool/conn_pool_base_test.cc index 1ecd11f73f2a..0dcc2a02b043 100644 --- a/test/common/conn_pool/conn_pool_base_test.cc +++ b/test/common/conn_pool/conn_pool_base_test.cc @@ -120,7 +120,7 @@ TEST_F(ConnPoolImplBaseTest, PrefetchOnDisconnect) { EXPECT_CALL(pool_, onPoolFailure).WillOnce(InvokeWithoutArgs([&]() -> void { pool_.newStream(context_); })); - EXPECT_CALL(pool_, instantiateActiveClient).Times(1); + EXPECT_CALL(pool_, instantiateActiveClient); clients_[0]->close(); CHECK_STATE(0 /*active*/, 1 /*pending*/, 2 /*connecting capacity*/); @@ -136,7 +136,7 @@ TEST_F(ConnPoolImplBaseTest, NoPrefetchIfUnhealthy) { EXPECT_EQ(host_->health(), Upstream::Host::Health::Unhealthy); // On new stream, create 1 connection. - EXPECT_CALL(pool_, instantiateActiveClient).Times(1); + EXPECT_CALL(pool_, instantiateActiveClient); auto cancelable = pool_.newStream(context_); CHECK_STATE(0 /*active*/, 1 /*pending*/, 1 /*connecting capacity*/); @@ -153,7 +153,7 @@ TEST_F(ConnPoolImplBaseTest, NoPrefetchIfDegraded) { EXPECT_EQ(host_->health(), Upstream::Host::Health::Degraded); // On new stream, create 1 connection. - EXPECT_CALL(pool_, instantiateActiveClient).Times(1); + EXPECT_CALL(pool_, instantiateActiveClient); auto cancelable = pool_.newStream(context_); cancelable->cancel(ConnectionPool::CancelPolicy::CloseExcess); diff --git a/test/common/event/file_event_impl_test.cc b/test/common/event/file_event_impl_test.cc index 974c498dc905..64abdc349ea9 100644 --- a/test/common/event/file_event_impl_test.cc +++ b/test/common/event/file_event_impl_test.cc @@ -74,11 +74,11 @@ TEST_P(FileEventImplActivateTest, Activate) { Api::ApiPtr api = Api::createApiForTest(); DispatcherPtr dispatcher(api->allocateDispatcher("test_thread")); ReadyWatcher read_event; - EXPECT_CALL(read_event, ready()).Times(1); + EXPECT_CALL(read_event, ready()); ReadyWatcher write_event; - EXPECT_CALL(write_event, ready()).Times(1); + EXPECT_CALL(write_event, ready()); ReadyWatcher closed_event; - EXPECT_CALL(closed_event, ready()).Times(1); + EXPECT_CALL(closed_event, ready()); const FileTriggerType trigger = Event::PlatformDefaultTriggerType; @@ -231,9 +231,9 @@ TEST_P(FileEventImplActivateTest, SetEnableCancelsActivate) { #ifndef WIN32 // Libevent on Windows doesn't support edge trigger. TEST_F(FileEventImplTest, EdgeTrigger) { ReadyWatcher read_event; - EXPECT_CALL(read_event, ready()).Times(1); + EXPECT_CALL(read_event, ready()); ReadyWatcher write_event; - EXPECT_CALL(write_event, ready()).Times(1); + EXPECT_CALL(write_event, ready()); Event::FileEventPtr file_event = dispatcher_->createFileEvent( fds_[0], diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index e717c72c3e25..a739a1836ddb 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -56,7 +56,7 @@ class AsyncClientImplTest : public testing::Test { } virtual void expectSuccess(AsyncClient::Request* sent_request, uint64_t code) { - EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); EXPECT_CALL(callbacks_, onSuccess_(_, _)) .WillOnce(Invoke([sent_request, code](const AsyncClient::Request& request, ResponseMessage* response) -> void { @@ -545,7 +545,7 @@ TEST_F(AsyncClientImplTest, MultipleRequests) { // Finish request 2. ResponseHeaderMapPtr response_headers2(new TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(callbacks2, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks2, onBeforeFinalizeUpstreamSpan(_, _)); EXPECT_CALL(callbacks2, onSuccess_(_, _)) .WillOnce(Invoke( [request2](const AsyncClient::Request& request, ResponseMessage* response) -> void { @@ -564,7 +564,7 @@ TEST_F(AsyncClientImplTest, MultipleRequests) { // Finish request 3. ResponseHeaderMapPtr response_headers3(new TestResponseHeaderMapImpl{{":status", "500"}}); - EXPECT_CALL(callbacks3, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks3, onBeforeFinalizeUpstreamSpan(_, _)); EXPECT_CALL(callbacks3, onSuccess_(_, _)) .WillOnce(Invoke( [request3](const AsyncClient::Request& request, ResponseMessage* response) -> void { @@ -912,7 +912,7 @@ TEST_F(AsyncClientImplTest, ResetAfterResponseStart) { auto* request = client_.send(std::move(message_), callbacks_, AsyncClient::RequestOptions()); EXPECT_NE(request, nullptr); - EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); EXPECT_CALL(callbacks_, onFailure(_, _)) .WillOnce(Invoke([sent_request = request](const AsyncClient::Request& request, AsyncClient::FailureReason reason) { @@ -955,7 +955,7 @@ TEST_F(AsyncClientImplTest, CancelRequest) { EXPECT_CALL(stream_encoder_, encodeHeaders(HeaderMapEqualRef(&message_->headers()), true)); EXPECT_CALL(stream_encoder_.stream_, resetStream(_)); - EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); AsyncClient::Request* request = client_.send(std::move(message_), callbacks_, AsyncClient::RequestOptions()); request->cancel(); @@ -1028,7 +1028,7 @@ TEST_F(AsyncClientImplTest, DestroyWithActiveRequest) { EXPECT_NE(request, nullptr); EXPECT_CALL(stream_encoder_.stream_, resetStream(_)); - EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); EXPECT_CALL(callbacks_, onFailure(_, _)) .WillOnce(Invoke([sent_request = request](const AsyncClient::Request& request, AsyncClient::FailureReason reason) { @@ -1058,7 +1058,7 @@ TEST_F(AsyncClientImplTracingTest, DestroyWithActiveRequest) { auto* request = client_.send(std::move(message_), callbacks_, options); EXPECT_NE(request, nullptr); - EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); EXPECT_CALL(callbacks_, onFailure(_, _)) .WillOnce(Invoke([sent_request = request](const AsyncClient::Request& request, AsyncClient::FailureReason reason) { @@ -1089,7 +1089,7 @@ TEST_F(AsyncClientImplTest, PoolFailure) { return nullptr; })); - EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); EXPECT_CALL(callbacks_, onSuccess_(_, _)) .WillOnce(Invoke([](const AsyncClient::Request& request, ResponseMessage* response) -> void { // The callback gets called before AsyncClient::send() completes, which means that we don't @@ -1114,7 +1114,7 @@ TEST_F(AsyncClientImplTest, PoolFailureWithBody) { return nullptr; })); - EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); EXPECT_CALL(callbacks_, onSuccess_(_, _)) .WillOnce(Invoke([](const AsyncClient::Request& request, ResponseMessage* response) -> void { // The callback gets called before AsyncClient::send() completes, which means that we don't @@ -1277,7 +1277,7 @@ TEST_F(AsyncClientImplTest, DisableTimer) { AsyncClient::Request* request = client_.send(std::move(message_), callbacks_, AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(200))); - EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)).Times(1); + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); request->cancel(); } diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index 83ae516e3c7f..811a4140be6c 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -2644,7 +2644,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutCallbackDisarmsAndReturns408 std::string response_body; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); - EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); + EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)); EXPECT_CALL(*request_timer, disableTimer()).Times(AtLeast(1)); EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) @@ -2672,8 +2672,8 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsNotDisarmedOnIncompleteReq EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); - EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); - EXPECT_CALL(*request_timer, disableTimer()).Times(1); + EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)); + EXPECT_CALL(*request_timer, disableTimer()); decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ @@ -2699,7 +2699,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); - EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); + EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)); decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ @@ -2725,7 +2725,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); - EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); + EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)); decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ @@ -2752,7 +2752,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); - EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); + EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)); decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ @@ -2787,7 +2787,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnEncodeHeaders) { EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); - EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); + EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)); decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{ @@ -2826,10 +2826,10 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnConnectionTermin Buffer::OwnedImpl fake_input("1234"); - EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)).Times(1); + EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _)); conn_manager_->onData(fake_input, false); // kick off request - EXPECT_CALL(*request_timer, disableTimer()).Times(1); + EXPECT_CALL(*request_timer, disableTimer()); EXPECT_EQ(0U, stats_.named_.downstream_rq_timeout_.value()); expectOnDestroy(); @@ -2854,7 +2854,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestHeaderTimeoutDisarmedAfterHeaders) RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ {":authority", "localhost:8080"}, {":path", "/"}, {":method", "GET"}}}; - EXPECT_CALL(*request_header_timer, disableTimer).Times(1); + EXPECT_CALL(*request_header_timer, disableTimer); decoder_->decodeHeaders(std::move(headers), false); return Http::okStatus(); }); @@ -2881,7 +2881,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestHeaderTimeoutCallbackDisarmsAndRetu Event::MockTimer* request_header_timer; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { request_header_timer = setUpTimer(); - EXPECT_CALL(*request_header_timer, enableTimer(request_headers_timeout_, _)).Times(1); + EXPECT_CALL(*request_header_timer, enableTimer(request_headers_timeout_, _)); conn_manager_->newStream(response_encoder_); EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, setTrackedObject(_)).Times(2); @@ -2892,7 +2892,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestHeaderTimeoutCallbackDisarmsAndRetu conn_manager_->onData(fake_input, false); // kick off request // The client took too long to send headers. - EXPECT_CALL(*request_header_timer, disableTimer).Times(1); + EXPECT_CALL(*request_header_timer, disableTimer); request_header_timer->invokeCallback(); EXPECT_EQ(1U, stats_.named_.downstream_rq_header_timeout_.value()); @@ -2937,7 +2937,7 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationCallbackResetStream) { Event::MockTimer* duration_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - EXPECT_CALL(*duration_timer, enableTimer(max_stream_duration_.value(), _)).Times(1); + EXPECT_CALL(*duration_timer, enableTimer(max_stream_duration_.value(), _)); conn_manager_->newStream(response_encoder_); return Http::okStatus(); })); @@ -3053,7 +3053,7 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationCallbackNotCalledIfResetS Event::MockTimer* duration_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - EXPECT_CALL(*duration_timer, enableTimer(max_stream_duration_.value(), _)).Times(1); + EXPECT_CALL(*duration_timer, enableTimer(max_stream_duration_.value(), _)); conn_manager_->newStream(response_encoder_); return Http::okStatus(); })); diff --git a/test/common/http/conn_manager_utility_test.cc b/test/common/http/conn_manager_utility_test.cc index 10f755873a2b..f83e84346ed9 100644 --- a/test/common/http/conn_manager_utility_test.cc +++ b/test/common/http/conn_manager_utility_test.cc @@ -892,8 +892,7 @@ TEST_F(ConnectionManagerUtilityTest, MutateResponseHeadersReturnXRequestId) { {"x-envoy-force-trace", "true"}}; EXPECT_CALL(*request_id_extension_, - setInResponse(testing::Ref(response_headers), testing::Ref(request_headers))) - .Times(1); + setInResponse(testing::Ref(response_headers), testing::Ref(request_headers))); ConnectionManagerUtility::mutateResponseHeaders(response_headers, &request_headers, config_, ""); EXPECT_EQ("request-id", response_headers.get_("x-request-id")); } @@ -917,8 +916,7 @@ TEST_F(ConnectionManagerUtilityTest, AlwaysMutateResponseHeadersReturnXRequestId TestRequestHeaderMapImpl request_headers{{"x-request-id", "request-id"}}; EXPECT_CALL(*request_id_extension_, - setInResponse(testing::Ref(response_headers), testing::Ref(request_headers))) - .Times(1); + setInResponse(testing::Ref(response_headers), testing::Ref(request_headers))); ON_CALL(config_, alwaysSetRequestIdInResponse()).WillByDefault(Return(true)); ConnectionManagerUtility::mutateResponseHeaders(response_headers, &request_headers, config_, ""); EXPECT_EQ("request-id", response_headers.get_("x-request-id")); @@ -1222,8 +1220,7 @@ TEST_F(ConnectionManagerUtilityTest, RandomSamplingWhenGlobalSet) { Http::TestRequestHeaderMapImpl request_headers{ {"x-request-id", "125a4afb-6f55-44ba-ad80-413f09f48a28"}}; EXPECT_CALL(*request_id_extension_, - setTraceStatus(testing::Ref(request_headers), TraceStatus::Sampled)) - .Times(1); + setTraceStatus(testing::Ref(request_headers), TraceStatus::Sampled)); callMutateRequestHeaders(request_headers, Protocol::Http2); EXPECT_EQ(TraceStatus::Sampled, request_id_extension_->getTraceStatus(request_headers)); @@ -1242,8 +1239,7 @@ TEST_F(ConnectionManagerUtilityTest, SamplingWithoutRouteOverride) { Http::TestRequestHeaderMapImpl request_headers{ {"x-request-id", "125a4afb-6f55-44ba-ad80-413f09f48a28"}}; EXPECT_CALL(*request_id_extension_, - setTraceStatus(testing::Ref(request_headers), TraceStatus::Sampled)) - .Times(1); + setTraceStatus(testing::Ref(request_headers), TraceStatus::Sampled)); callMutateRequestHeaders(request_headers, Protocol::Http2); EXPECT_EQ(TraceStatus::Sampled, request_id_extension_->getTraceStatus(request_headers)); @@ -1269,8 +1265,7 @@ TEST_F(ConnectionManagerUtilityTest, SamplingWithRouteOverride) { Http::TestRequestHeaderMapImpl request_headers{ {"x-request-id", "125a4afb-6f55-44ba-ad80-413f09f48a28"}}; EXPECT_CALL(*request_id_extension_, - setTraceStatus(testing::Ref(request_headers), TraceStatus::NoTrace)) - .Times(1); + setTraceStatus(testing::Ref(request_headers), TraceStatus::NoTrace)); callMutateRequestHeaders(request_headers, Protocol::Http2); EXPECT_EQ(TraceStatus::NoTrace, request_id_extension_->getTraceStatus(request_headers)); @@ -1310,11 +1305,9 @@ TEST_F(ConnectionManagerUtilityTest, NoTraceWhenSamplingSetButGlobalNotSet) { Http::TestRequestHeaderMapImpl request_headers{ {"x-request-id", "125a4afb-6f55-44ba-ad80-413f09f48a28"}}; EXPECT_CALL(*request_id_extension_, - setTraceStatus(testing::Ref(request_headers), TraceStatus::Sampled)) - .Times(1); + setTraceStatus(testing::Ref(request_headers), TraceStatus::Sampled)); EXPECT_CALL(*request_id_extension_, - setTraceStatus(testing::Ref(request_headers), TraceStatus::NoTrace)) - .Times(1); + setTraceStatus(testing::Ref(request_headers), TraceStatus::NoTrace)); callMutateRequestHeaders(request_headers, Protocol::Http2); EXPECT_EQ(TraceStatus::NoTrace, request_id_extension_->getTraceStatus(request_headers)); @@ -1334,8 +1327,7 @@ TEST_F(ConnectionManagerUtilityTest, ClientSamplingWhenGlobalSet) { {"x-client-trace-id", "f4dca0a9-12c7-4307-8002-969403baf480"}, {"x-request-id", "125a4afb-6f55-44ba-ad80-413f09f48a28"}}; EXPECT_CALL(*request_id_extension_, - setTraceStatus(testing::Ref(request_headers), TraceStatus::Client)) - .Times(1); + setTraceStatus(testing::Ref(request_headers), TraceStatus::Client)); callMutateRequestHeaders(request_headers, Protocol::Http2); EXPECT_EQ(TraceStatus::Client, request_id_extension_->getTraceStatus(request_headers)); @@ -1376,8 +1368,7 @@ TEST_F(ConnectionManagerUtilityTest, ForcedTracedWhenGlobalSet) { runtime_.snapshot_, featureEnabled("tracing.global_enabled", An(), _)) .WillOnce(Return(true)); - EXPECT_CALL(*request_id_extension_, setTraceStatus(testing::Ref(headers), TraceStatus::Forced)) - .Times(1); + EXPECT_CALL(*request_id_extension_, setTraceStatus(testing::Ref(headers), TraceStatus::Forced)); EXPECT_EQ((MutateRequestRet{"10.0.0.1:0", true}), callMutateRequestHeaders(headers, Protocol::Http2)); @@ -1396,10 +1387,8 @@ TEST_F(ConnectionManagerUtilityTest, NoTraceWhenForcedTracedButGlobalNotSet) { runtime_.snapshot_, featureEnabled("tracing.global_enabled", An(), _)) .WillOnce(Return(false)); - EXPECT_CALL(*request_id_extension_, setTraceStatus(testing::Ref(headers), TraceStatus::Forced)) - .Times(1); - EXPECT_CALL(*request_id_extension_, setTraceStatus(testing::Ref(headers), TraceStatus::NoTrace)) - .Times(1); + EXPECT_CALL(*request_id_extension_, setTraceStatus(testing::Ref(headers), TraceStatus::Forced)); + EXPECT_CALL(*request_id_extension_, setTraceStatus(testing::Ref(headers), TraceStatus::NoTrace)); EXPECT_EQ((MutateRequestRet{"10.0.0.1:0", true}), callMutateRequestHeaders(headers, Protocol::Http2)); @@ -1526,7 +1515,7 @@ TEST_F(ConnectionManagerUtilityTest, PreserveExternalRequestId) { ON_CALL(config_, preserveExternalRequestId()).WillByDefault(Return(true)); TestRequestHeaderMapImpl headers{{"x-request-id", "my-request-id"}, {"x-forwarded-for", "198.51.100.1"}}; - EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)).Times(1); + EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)); EXPECT_CALL(*request_id_extension_, set(_, true)).Times(0); EXPECT_EQ((MutateRequestRet{"134.2.2.11:0", false}), callMutateRequestHeaders(headers, Protocol::Http2)); @@ -1540,7 +1529,7 @@ TEST_F(ConnectionManagerUtilityTest, PreseverExternalRequestIdNoReqId) { ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true)); ON_CALL(config_, preserveExternalRequestId()).WillByDefault(Return(true)); TestRequestHeaderMapImpl headers{{"x-forwarded-for", "198.51.100.1"}}; - EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)).Times(1); + EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)); EXPECT_CALL(*request_id_extension_, set(_, true)).Times(0); EXPECT_EQ((MutateRequestRet{"134.2.2.11:0", false}), callMutateRequestHeaders(headers, Protocol::Http2)); @@ -1552,7 +1541,7 @@ TEST_F(ConnectionManagerUtilityTest, PreseverExternalRequestIdNoReqId) { TEST_F(ConnectionManagerUtilityTest, PreserveExternalRequestIdNoEdgeRequestKeepRequestId) { ON_CALL(config_, preserveExternalRequestId()).WillByDefault(Return(true)); TestRequestHeaderMapImpl headers{{"x-request-id", "myReqId"}}; - EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)).Times(1); + EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)); EXPECT_CALL(*request_id_extension_, set(_, true)).Times(0); callMutateRequestHeaders(headers, Protocol::Http2); EXPECT_EQ("myReqId", headers.get_(Headers::get().RequestId)); @@ -1563,7 +1552,7 @@ TEST_F(ConnectionManagerUtilityTest, PreserveExternalRequestIdNoEdgeRequestKeepR TEST_F(ConnectionManagerUtilityTest, PreserveExternalRequestIdNoEdgeRequestGenerateNewRequestId) { ON_CALL(config_, preserveExternalRequestId()).WillByDefault(Return(true)); TestRequestHeaderMapImpl headers; - EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)).Times(1); + EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)); EXPECT_CALL(*request_id_extension_, set(_, true)).Times(0); callMutateRequestHeaders(headers, Protocol::Http2); EXPECT_EQ(random_.uuid_, headers.get_(Headers::get().RequestId)); @@ -1579,7 +1568,7 @@ TEST_F(ConnectionManagerUtilityTest, NoPreserveExternalRequestIdEdgeRequestGener ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true)); TestRequestHeaderMapImpl headers{{"x-forwarded-for", "198.51.100.1"}, {"x-request-id", "my-request-id"}}; - EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), true)).Times(1); + EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), true)); EXPECT_CALL(*request_id_extension_, set(_, false)).Times(0); EXPECT_EQ((MutateRequestRet{"134.2.2.11:0", false}), callMutateRequestHeaders(headers, Protocol::Http2)); @@ -1589,7 +1578,7 @@ TEST_F(ConnectionManagerUtilityTest, NoPreserveExternalRequestIdEdgeRequestGener // with no request id { TestRequestHeaderMapImpl headers{{"x-forwarded-for", "198.51.100.1"}}; - EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), true)).Times(1); + EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), true)); EXPECT_CALL(*request_id_extension_, set(_, false)).Times(0); EXPECT_EQ((MutateRequestRet{"134.2.2.11:0", false}), callMutateRequestHeaders(headers, Protocol::Http2)); @@ -1604,7 +1593,7 @@ TEST_F(ConnectionManagerUtilityTest, NoPreserveExternalRequestIdNoEdgeRequest) { // with no request id { TestRequestHeaderMapImpl headers; - EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)).Times(1); + EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)); EXPECT_CALL(*request_id_extension_, set(_, true)).Times(0); callMutateRequestHeaders(headers, Protocol::Http2); EXPECT_EQ(random_.uuid_, headers.get_(Headers::get().RequestId)); @@ -1613,7 +1602,7 @@ TEST_F(ConnectionManagerUtilityTest, NoPreserveExternalRequestIdNoEdgeRequest) { // with request id { TestRequestHeaderMapImpl headers{{"x-request-id", "my-request-id"}}; - EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)).Times(1); + EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), false)); EXPECT_CALL(*request_id_extension_, set(_, true)).Times(0); callMutateRequestHeaders(headers, Protocol::Http2); EXPECT_EQ("my-request-id", headers.get_(Headers::get().RequestId)); diff --git a/test/common/http/http1/codec_impl_test.cc b/test/common/http/http1/codec_impl_test.cc index 9b6a13a55c8e..b292dd1aa644 100644 --- a/test/common/http/http1/codec_impl_test.cc +++ b/test/common/http/http1/codec_impl_test.cc @@ -2628,7 +2628,7 @@ TEST_P(Http1ClientConnectionImplTest, ConnectResponseWithEarlyData) { // Send response headers and payload EXPECT_CALL(response_decoder, decodeHeaders_(_, false)); Buffer::OwnedImpl expected_data("12345abcd"); - EXPECT_CALL(response_decoder, decodeData(BufferEqual(&expected_data), false)).Times(1); + EXPECT_CALL(response_decoder, decodeData(BufferEqual(&expected_data), false)); Buffer::OwnedImpl response("HTTP/1.1 200 OK\r\n\r\n12345abcd"); auto status = codec_->dispatch(response); EXPECT_TRUE(status.ok()); diff --git a/test/common/http/http2/codec_impl_test.cc b/test/common/http/http2/codec_impl_test.cc index dd8b774490fc..dc266bfa68dd 100644 --- a/test/common/http/http2/codec_impl_test.cc +++ b/test/common/http/http2/codec_impl_test.cc @@ -1793,7 +1793,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersInvokeResetStream) { HttpTestUtility::addDefaultHeaders(request_headers); std::string long_string = std::string(63 * 1024, 'q'); request_headers.addCopy("big", long_string); - EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(1); + EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); } @@ -1836,7 +1836,7 @@ TEST_P(Http2CodecImplTest, HeaderNameWithUnderscoreAreRejectedByDefault) { TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); request_headers.addCopy("bad_header", "something"); - EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(1); + EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); EXPECT_EQ( 1, @@ -1887,7 +1887,7 @@ TEST_P(Http2CodecImplTest, ManyRequestHeadersInvokeResetStream) { for (int i = 0; i < 100; i++) { request_headers.addCopy(std::to_string(i), ""); } - EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(1); + EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); } @@ -1956,7 +1956,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersOverDefaultCodecLibraryLimit) { std::string long_string = std::string(65 * 1024, 'q'); request_headers.addCopy("big", long_string); - EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)).Times(1); + EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); } @@ -1992,7 +1992,7 @@ TEST_P(Http2CodecImplTest, ManyLargeRequestHeadersUnderPerHeaderLimit) { request_headers.addCopy(std::to_string(i), long_string); } - EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)).Times(1); + EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); } @@ -2010,7 +2010,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersAtMaxConfigurable) { request_headers.addCopy(std::to_string(i), long_string); } - EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)).Times(1); + EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); } @@ -2233,7 +2233,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFloodMitigationDisabled) { // +2 is to account for HEADERS and PING ACK, that is used to trigger mitigation EXPECT_CALL(server_connection_, write(_, _)) .Times(CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 2); - EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)).Times(1); + EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); EXPECT_CALL(response_decoder_, decodeData(_, false)) .Times(CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; diff --git a/test/common/http/http2/conn_pool_test.cc b/test/common/http/http2/conn_pool_test.cc index 4787a4d188c2..f933b7817930 100644 --- a/test/common/http/http2/conn_pool_test.cc +++ b/test/common/http/http2/conn_pool_test.cc @@ -139,7 +139,7 @@ class Http2ConnPoolImplTest : public Event::TestUsingSimulatedTime, public testi EXPECT_CALL(*cluster_, perConnectionBufferLimitBytes()) .Times(num_clients) .WillRepeatedly(Return(*buffer_limits)); - EXPECT_CALL(*test_client.connection_, setBufferLimits(*buffer_limits)).Times(1); + EXPECT_CALL(*test_client.connection_, setBufferLimits(*buffer_limits)); } } // Finally (for InSequence tests) set up createCodecClient and make sure the diff --git a/test/common/http/request_id_extension_uuid_impl_test.cc b/test/common/http/request_id_extension_uuid_impl_test.cc index a975a8a24d69..fe0988ec0223 100644 --- a/test/common/http/request_id_extension_uuid_impl_test.cc +++ b/test/common/http/request_id_extension_uuid_impl_test.cc @@ -18,11 +18,11 @@ TEST(UUIDRequestIDExtensionTest, SetRequestID) { UUIDRequestIDExtension uuid_utils(random); TestRequestHeaderMapImpl request_headers; - EXPECT_CALL(random, uuid()).Times(1).WillOnce(Return("first-request-id")); + EXPECT_CALL(random, uuid()).WillOnce(Return("first-request-id")); uuid_utils.set(request_headers, true); EXPECT_EQ("first-request-id", request_headers.get_(Headers::get().RequestId)); - EXPECT_CALL(random, uuid()).Times(1).WillOnce(Return("second-request-id")); + EXPECT_CALL(random, uuid()).WillOnce(Return("second-request-id")); uuid_utils.set(request_headers, true); EXPECT_EQ("second-request-id", request_headers.get_(Headers::get().RequestId)); } @@ -32,7 +32,7 @@ TEST(UUIDRequestIDExtensionTest, EnsureRequestID) { UUIDRequestIDExtension uuid_utils(random); TestRequestHeaderMapImpl request_headers; - EXPECT_CALL(random, uuid()).Times(1).WillOnce(Return("first-request-id")); + EXPECT_CALL(random, uuid()).WillOnce(Return("first-request-id")); uuid_utils.set(request_headers, false); EXPECT_EQ("first-request-id", request_headers.get_(Headers::get().RequestId)); diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index 20e2dcb7cd6f..6db5f0af50a6 100644 --- a/test/common/network/connection_impl_test.cc +++ b/test/common/network/connection_impl_test.cc @@ -1120,7 +1120,7 @@ TEST_P(ConnectionImplTest, WriteWithWatermarks) { // Clean up the connection. The close() (called via disconnect) will attempt to flush. The // call to write() will succeed, bringing the connection back under the low watermark. - EXPECT_CALL(client_callbacks_, onBelowWriteBufferLowWatermark()).Times(1); + EXPECT_CALL(client_callbacks_, onBelowWriteBufferLowWatermark()); disconnect(true); } @@ -1355,7 +1355,7 @@ TEST_P(ConnectionImplTest, FlushWriteCloseTest) { // reading and the connection should close gracefully via FIN. EXPECT_CALL(stats.delayed_close_timeouts_, inc()).Times(0); - EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::LocalClose)).Times(1); + EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::LocalClose)); EXPECT_CALL(*client_read_filter, onData(BufferStringEqual("data"), false)) .Times(1) .WillOnce(InvokeWithoutArgs([&]() -> FilterStatus { @@ -1363,7 +1363,7 @@ TEST_P(ConnectionImplTest, FlushWriteCloseTest) { dispatcher_->exit(); return FilterStatus::StopIteration; })); - EXPECT_CALL(client_callbacks_, onEvent(ConnectionEvent::RemoteClose)).Times(1); + EXPECT_CALL(client_callbacks_, onEvent(ConnectionEvent::RemoteClose)); server_connection_->close(ConnectionCloseType::FlushWrite); dispatcher_->run(Event::Dispatcher::RunType::Block); } @@ -1406,7 +1406,7 @@ TEST_P(ConnectionImplTest, FlushWriteAndDelayCloseTest) { // Client closes the connection so delayed close timer on the server conn should not fire. EXPECT_CALL(stats.delayed_close_timeouts_, inc()).Times(0); - EXPECT_CALL(client_callbacks_, onEvent(ConnectionEvent::LocalClose)).Times(1); + EXPECT_CALL(client_callbacks_, onEvent(ConnectionEvent::LocalClose)); EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::RemoteClose)) .Times(1) .WillOnce(Invoke([&](Network::ConnectionEvent) -> void { dispatcher_->exit(); })); @@ -1446,8 +1446,8 @@ TEST_P(ConnectionImplTest, FlushWriteAndDelayCloseTimerTriggerTest) { return FilterStatus::StopIteration; })); server_connection_->close(ConnectionCloseType::FlushWriteAndDelay); - EXPECT_CALL(stats.delayed_close_timeouts_, inc()).Times(1); - EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::LocalClose)).Times(1); + EXPECT_CALL(stats.delayed_close_timeouts_, inc()); + EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::LocalClose)); EXPECT_CALL(client_callbacks_, onEvent(ConnectionEvent::RemoteClose)) .Times(1) .WillOnce(Invoke([&](Network::ConnectionEvent) -> void { dispatcher_->exit(); })); @@ -1485,7 +1485,7 @@ TEST_P(ConnectionImplTest, FlushWriteAfterFlushWriteAndDelayWithPendingWrite) { // The socket close will happen as a result of the write flush and not due to the delayed close // timer triggering. EXPECT_CALL(stats.delayed_close_timeouts_, inc()).Times(0); - EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::LocalClose)).Times(1); + EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::LocalClose)); EXPECT_CALL(*client_read_filter, onData(BufferStringEqual("Connection: Close"), false)) .Times(1) .WillOnce(InvokeWithoutArgs([&]() -> FilterStatus { @@ -1528,7 +1528,7 @@ TEST_P(ConnectionImplTest, FlushWriteAfterFlushWriteAndDelayWithoutPendingWrite) // The write buffer has been flushed and a delayed close timer has been set. The socket close // will happen as part of the close() since the timeout is no longer required. - EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::LocalClose)).Times(1); + EXPECT_CALL(server_callbacks_, onEvent(ConnectionEvent::LocalClose)); server_connection_->close(ConnectionCloseType::FlushWrite); EXPECT_CALL(stats.delayed_close_timeouts_, inc()).Times(0); EXPECT_CALL(client_callbacks_, onEvent(ConnectionEvent::RemoteClose)) @@ -1607,7 +1607,7 @@ TEST_P(ConnectionImplTest, DelayedCloseTimerResetWithPendingWriteBufferFlushes) Buffer::OwnedImpl data("data"); server_connection->write(data, false); - EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)); server_connection->close(ConnectionCloseType::FlushWriteAndDelay); // The write ready event cb (ConnectionImpl::onWriteReady()) will reset the timer to its @@ -1619,7 +1619,7 @@ TEST_P(ConnectionImplTest, DelayedCloseTimerResetWithPendingWriteBufferFlushes) buffer.drain(bytes_drained); return IoResult{PostIoAction::KeepOpen, bytes_drained, false}; })); - EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)); (*mocks.file_ready_cb_)(Event::FileReadyType::Write); EXPECT_CALL(*transport_socket, doWrite(BufferStringEqual("ata"), _)) @@ -1629,7 +1629,7 @@ TEST_P(ConnectionImplTest, DelayedCloseTimerResetWithPendingWriteBufferFlushes) buffer.drain(buffer.length()); return IoResult{PostIoAction::KeepOpen, bytes_drained, false}; })); - EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)); (*mocks.file_ready_cb_)(Event::FileReadyType::Write); // Force the delayed close timeout to trigger so the connection is cleaned up. @@ -1668,7 +1668,7 @@ TEST_P(ConnectionImplTest, IgnoreSpuriousFdWriteEventsDuringFlushWriteAndDelay) Buffer::OwnedImpl data("data"); server_connection->write(data, false); - EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)); server_connection->close(ConnectionCloseType::FlushWriteAndDelay); // The write ready event cb (ConnectionImpl::onWriteReady()) will reset the timer to its @@ -1680,7 +1680,7 @@ TEST_P(ConnectionImplTest, IgnoreSpuriousFdWriteEventsDuringFlushWriteAndDelay) buffer.drain(bytes_drained); return IoResult{PostIoAction::KeepOpen, bytes_drained, false}; })); - EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)); (*mocks.file_ready_cb_)(Event::FileReadyType::Write); // Handle a write event and drain 0 bytes from the buffer. Verify that the timer is not reset. @@ -1702,7 +1702,7 @@ TEST_P(ConnectionImplTest, IgnoreSpuriousFdWriteEventsDuringFlushWriteAndDelay) EXPECT_EQ(server_connection->state(), Connection::State::Closing); return IoResult{PostIoAction::KeepOpen, bytes_drained, false}; })); - EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)); (*mocks.file_ready_cb_)(Event::FileReadyType::Write); // Handle a write event after entering the half-closed state. Verify that the timer is not reset @@ -1722,7 +1722,7 @@ TEST_P(ConnectionImplTest, IgnoreSpuriousFdWriteEventsDuringFlushWriteAndDelay) EXPECT_EQ(server_connection->state(), Connection::State::Closing); return IoResult{PostIoAction::KeepOpen, 1, false}; })); - EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(timeout, _)); (*mocks.file_ready_cb_)(Event::FileReadyType::Write); // Force the delayed close timeout to trigger so the connection is cleaned up. @@ -1756,10 +1756,10 @@ TEST_P(ConnectionImplTest, DelayedCloseTimeoutDisableOnSocketClose) { return IoResult{PostIoAction::KeepOpen, buffer.length(), false}; })); server_connection->write(data, false); - EXPECT_CALL(*mocks.timer_, enableTimer(_, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(_, _)); // Enable the delayed close timer. server_connection->close(ConnectionCloseType::FlushWriteAndDelay); - EXPECT_CALL(*mocks.timer_, disableTimer()).Times(1); + EXPECT_CALL(*mocks.timer_, disableTimer()); // This close() will call closeSocket(), which should disable the timer to avoid triggering it // after the connection's data structures have been reset. server_connection->close(ConnectionCloseType::NoFlush); @@ -1797,9 +1797,9 @@ TEST_P(ConnectionImplTest, DelayedCloseTimeoutNullStats) { })); server_connection->write(data, false); - EXPECT_CALL(*mocks.timer_, enableTimer(_, _)).Times(1); + EXPECT_CALL(*mocks.timer_, enableTimer(_, _)); server_connection->close(ConnectionCloseType::FlushWriteAndDelay); - EXPECT_CALL(*mocks.timer_, disableTimer()).Times(1); + EXPECT_CALL(*mocks.timer_, disableTimer()); // The following close() will call closeSocket() and reset internal data structures such as // stats. server_connection->close(ConnectionCloseType::NoFlush); @@ -2263,7 +2263,7 @@ TEST_F(MockTransportConnectionImplTest, ReadMultipleEndStream) { EXPECT_CALL(*transport_socket_, doRead(_)) .Times(2) .WillRepeatedly(Return(IoResult{PostIoAction::KeepOpen, 0, true})); - EXPECT_CALL(*read_filter, onData(_, true)).Times(1); + EXPECT_CALL(*read_filter, onData(_, true)); file_ready_cb_(Event::FileReadyType::Read); file_ready_cb_(Event::FileReadyType::Read); } diff --git a/test/common/router/rds_impl_test.cc b/test/common/router/rds_impl_test.cc index 06f0b01c3492..77fb99a23125 100644 --- a/test/common/router/rds_impl_test.cc +++ b/test/common/router/rds_impl_test.cc @@ -413,7 +413,7 @@ TEST_F(RdsRouteConfigSubscriptionTest, CreatesNoopInitManager) { rds, server_factory_context_, "stat_prefix", outer_init_manager_); RdsRouteConfigSubscription& subscription = (dynamic_cast(route_config_provider.get()))->subscription(); - init_watcher_.expectReady().Times(1); // The parent_init_target_ will call once. + init_watcher_.expectReady(); // The parent_init_target_ will call once. outer_init_manager_.initialize(init_watcher_); std::unique_ptr noop_init_manager; std::unique_ptr init_vhds; diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index de071f374da9..764b5ce9941b 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -287,7 +287,7 @@ class RouterTestBase : public testing::Test { void sendRequest(bool end_stream = true) { if (end_stream) { - EXPECT_CALL(callbacks_.dispatcher_, createTimer_(_)).Times(1); + EXPECT_CALL(callbacks_.dispatcher_, createTimer_(_)); } EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( @@ -1865,7 +1865,7 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { test_time_.advanceTimeWait(std::chrono::milliseconds(100)); EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)).Times(1); + EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)); EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); per_try_timeout_->invokeCallback(); @@ -3331,8 +3331,8 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); // Now trigger global timeout, expect everything to be reset - EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(1); - EXPECT_CALL(encoder2.stream_, resetStream(_)).Times(1); + EXPECT_CALL(encoder1.stream_, resetStream(_)); + EXPECT_CALL(encoder2.stream_, resetStream(_)); EXPECT_CALL( cm_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); @@ -3367,8 +3367,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { })); EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, - absl::optional(absl::nullopt))) - .Times(1); + absl::optional(absl::nullopt))); expectPerTryTimerCreate(); expectResponseTimerCreate(); @@ -3401,8 +3400,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { })); EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, - absl::optional(absl::nullopt))) - .Times(1); + absl::optional(absl::nullopt))); expectPerTryTimerCreate(); router_.retry_state_->callback_(); EXPECT_EQ(2U, @@ -3458,8 +3456,7 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { })); // First is reset EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, - putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)) - .Times(1); + putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))) @@ -3547,8 +3544,7 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { })); EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, - absl::optional(absl::nullopt))) - .Times(1); + absl::optional(absl::nullopt))); Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); @@ -3558,7 +3554,7 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { expectPerTryTimerCreate(); expectResponseTimerCreate(); Buffer::OwnedImpl body("test body"); - EXPECT_CALL(encoder, encodeData(_, _)).Times(1); + EXPECT_CALL(encoder, encodeData(_, _)); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); router_.retry_state_->expectHedgedPerTryTimeoutRetry(); EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, true)); @@ -3669,7 +3665,7 @@ TEST_F(RouterTest, RetryUpstreamReset) { HttpTestUtility::addDefaultHeaders(headers); router_.decodeHeaders(headers, false); EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); - EXPECT_CALL(callbacks_, addDecodedData(_, _)).Times(1); + EXPECT_CALL(callbacks_, addDecodedData(_, _)); Buffer::OwnedImpl body("test body"); router_.decodeData(body, true); EXPECT_EQ(1U, @@ -4808,7 +4804,7 @@ TEST_F(RouterTest, CrossSchemeRedirectRejectedByPolicy) { redirect_headers_->setLocation("https://www.foo.com"); - EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_, decodingBuffer()); EXPECT_CALL(callbacks_, recreateStream(_)).Times(0); response_decoder_->decodeHeaders(std::move(redirect_headers_), true); @@ -4828,8 +4824,8 @@ TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { auto mock_predicate = std::make_shared>(); - EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); - EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(callbacks_, decodingBuffer()); + EXPECT_CALL(callbacks_, clearRouteCache()); EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, predicates()) .WillOnce(Return(std::vector({mock_predicate}))); EXPECT_CALL(*mock_predicate, acceptTargetRoute(_, _, _, _)).WillOnce(Return(false)); @@ -4855,9 +4851,9 @@ TEST_F(RouterTest, HttpInternalRedirectSucceeded) { default_request_headers_.setForwardedProto("http"); sendRequest(); - EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); - EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); - EXPECT_CALL(callbacks_, recreateStream(_)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(callbacks_, decodingBuffer()); + EXPECT_CALL(callbacks_, clearRouteCache()); + EXPECT_CALL(callbacks_, recreateStream(_)).WillOnce(Return(true)); response_decoder_->decodeHeaders(std::move(redirect_headers_), false); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_succeeded_total") @@ -4880,10 +4876,10 @@ TEST_F(RouterTest, HttpsInternalRedirectSucceeded) { sendRequest(); redirect_headers_->setLocation("https://www.foo.com"); - EXPECT_CALL(connection_, ssl()).Times(1).WillOnce(Return(ssl_connection)); - EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); - EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); - EXPECT_CALL(callbacks_, recreateStream(_)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(connection_, ssl()).WillOnce(Return(ssl_connection)); + EXPECT_CALL(callbacks_, decodingBuffer()); + EXPECT_CALL(callbacks_, clearRouteCache()); + EXPECT_CALL(callbacks_, recreateStream(_)).WillOnce(Return(true)); response_decoder_->decodeHeaders(std::move(redirect_headers_), false); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_succeeded_total") @@ -4901,13 +4897,13 @@ TEST_F(RouterTest, CrossSchemeRedirectAllowedByPolicy) { sendRequest(); redirect_headers_->setLocation("http://www.foo.com"); - EXPECT_CALL(connection_, ssl()).Times(1).WillOnce(Return(ssl_connection)); - EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(connection_, ssl()).WillOnce(Return(ssl_connection)); + EXPECT_CALL(callbacks_, decodingBuffer()); EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, isCrossSchemeRedirectAllowed()) .WillOnce(Return(true)); - EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); - EXPECT_CALL(callbacks_, recreateStream(_)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(callbacks_, clearRouteCache()); + EXPECT_CALL(callbacks_, recreateStream(_)).WillOnce(Return(true)); response_decoder_->decodeHeaders(std::move(redirect_headers_), false); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_succeeded_total") @@ -6466,7 +6462,7 @@ TEST_F(WatermarkTest, RetryRequestNotComplete) { HttpTestUtility::addDefaultHeaders(headers); router_.decodeHeaders(headers, false); Buffer::OwnedImpl data("1234567890123"); - EXPECT_CALL(*router_.retry_state_, enabled()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).Times(0); EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)).Times(0); // This will result in retry_state_ being deleted. diff --git a/test/common/router/scoped_rds_test.cc b/test/common/router/scoped_rds_test.cc index 4618e47f4deb..72bc3e60c3c5 100644 --- a/test/common/router/scoped_rds_test.cc +++ b/test/common/router/scoped_rds_test.cc @@ -1063,7 +1063,7 @@ on_demand: true ScopeKeyPtr scope_key = getScopedRdsProvider()->config()->computeScopeKey( TestRequestHeaderMapImpl{{"Addr", "x-foo-key;x-bar-key"}}); - EXPECT_CALL(event_dispatcher_, post(_)).Times(1); + EXPECT_CALL(event_dispatcher_, post(_)); std::function route_config_updated_cb = [](bool) {}; getScopedRdsProvider()->onDemandRdsUpdate(std::move(scope_key), event_dispatcher_, std::move(route_config_updated_cb)); @@ -1135,8 +1135,8 @@ on_demand: true "foo_routes"); ScopeKeyPtr scope_key = getScopedRdsProvider()->config()->computeScopeKey( TestRequestHeaderMapImpl{{"Addr", "x-foo-key;x-bar-key"}}); - EXPECT_CALL(server_factory_context_.dispatcher_, post(_)).Times(1); - EXPECT_CALL(event_dispatcher_, post(_)).Times(1); + EXPECT_CALL(server_factory_context_.dispatcher_, post(_)); + EXPECT_CALL(event_dispatcher_, post(_)); std::function route_config_updated_cb = [](bool) {}; getScopedRdsProvider()->onDemandRdsUpdate(std::move(scope_key), event_dispatcher_, std::move(route_config_updated_cb)); @@ -1274,7 +1274,7 @@ on_demand: true pushRdsConfig({"foo_routes"}, "111"); // Route table have been fetched, callbacks will be executed immediately. for (int i = 0; i < 5; i++) { - EXPECT_CALL(event_dispatcher_, post(_)).Times(1); + EXPECT_CALL(event_dispatcher_, post(_)); ScopeKeyPtr scope_key = getScopedRdsProvider()->config()->computeScopeKey( TestRequestHeaderMapImpl{{"Addr", "x-foo-key;x-foo-key"}}); std::function route_config_updated_cb = [](bool) {}; @@ -1304,7 +1304,7 @@ TEST_F(ScopedRdsTest, DanglingSubscriptionOnDemandUpdate) { std::move(route_config_updated_cb)); // Destroy the scoped_rds subscription by destroying its only config provider. provider_.reset(); - EXPECT_CALL(event_dispatcher_, post(_)).Times(1); + EXPECT_CALL(event_dispatcher_, post(_)); EXPECT_NO_THROW(temp_post_cb()); } @@ -1337,7 +1337,7 @@ on_demand: true std::move(route_config_updated_cb)); } // After on demand request, push rds update, the callbacks will be executed. - EXPECT_CALL(event_dispatcher_, post(_)).Times(1); + EXPECT_CALL(event_dispatcher_, post(_)); pushRdsConfig({"foo_routes"}, "111"); ScopeKeyPtr scope_key = getScopedRdsProvider()->config()->computeScopeKey( @@ -1345,7 +1345,7 @@ on_demand: true // Delete the scope route. EXPECT_NO_THROW(srds_subscription_->onConfigUpdate({}, "2")); EXPECT_EQ(0UL, all_scopes_.value()); - EXPECT_CALL(event_dispatcher_, post(_)).Times(1); + EXPECT_CALL(event_dispatcher_, post(_)); // Scope no longer exists after srds update. std::function route_config_updated_cb = [](bool scope_exist) { EXPECT_FALSE(scope_exist); diff --git a/test/common/runtime/runtime_impl_test.cc b/test/common/runtime/runtime_impl_test.cc index cb11f2333024..c451f87d8119 100644 --- a/test/common/runtime/runtime_impl_test.cc +++ b/test/common/runtime/runtime_impl_test.cc @@ -1135,7 +1135,7 @@ TEST_F(RtdsLoaderImplTest, MultipleRtdsLayers) { foo: bar baz: meh )EOF"); - EXPECT_CALL(rtds_init_callback_, Call()).Times(1); + EXPECT_CALL(rtds_init_callback_, Call()); doOnConfigUpdateVerifyNoThrow(runtime, 0); EXPECT_EQ("bar", loader_->snapshot().get("foo").value().get()); @@ -1181,7 +1181,7 @@ TEST_F(RtdsLoaderImplTest, BadConfigSource) { rtds_layer->set_name("some_resource"); rtds_layer->mutable_rtds_config(); - EXPECT_CALL(cm_, subscriptionFactory()).Times(1); + EXPECT_CALL(cm_, subscriptionFactory()); LoaderImpl loader(dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_); diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 361db8a49e75..77fbad57cb3c 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -1168,8 +1168,7 @@ TEST_F(TcpProxyTest, ConnectAttemptsUpstreamLocalFailReentrant) { // cancellation call. EXPECT_CALL(*conn_pool_handles_.at(0), cancel(Tcp::ConnectionPool::CancelPolicy::CloseExcess)) .Times(0); - EXPECT_CALL(*conn_pool_handles_.at(1), cancel(Tcp::ConnectionPool::CancelPolicy::CloseExcess)) - .Times(1); + EXPECT_CALL(*conn_pool_handles_.at(1), cancel(Tcp::ConnectionPool::CancelPolicy::CloseExcess)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); } diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index e43b086c1d07..41aabe78ba66 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -1469,7 +1469,7 @@ TEST_F(ClusterManagerImplTest, DynamicAddRemove) { .WillOnce(Return(std::make_pair(cluster1, nullptr))); EXPECT_CALL(*cluster1, initializePhase()).Times(0); EXPECT_CALL(*cluster1, initialize(_)); - EXPECT_CALL(*callbacks, onClusterAddOrUpdate(_)).Times(1); + EXPECT_CALL(*callbacks, onClusterAddOrUpdate(_)); EXPECT_TRUE(cluster_manager_->addOrUpdateCluster(defaultStaticCluster("fake_cluster"), "")); checkStats(1 /*added*/, 0 /*modified*/, 0 /*removed*/, 0 /*active*/, 1 /*warming*/); EXPECT_EQ(1, cluster_manager_->warmingClusterCount()); @@ -1498,7 +1498,7 @@ TEST_F(ClusterManagerImplTest, DynamicAddRemove) { // Test inline init. initialize_callback(); })); - EXPECT_CALL(*callbacks, onClusterAddOrUpdate(_)).Times(1); + EXPECT_CALL(*callbacks, onClusterAddOrUpdate(_)); EXPECT_TRUE(cluster_manager_->addOrUpdateCluster(update_cluster, "")); EXPECT_EQ(cluster2->info_, cluster_manager_->getThreadLocalCluster("fake_cluster")->info()); @@ -1527,7 +1527,7 @@ TEST_F(ClusterManagerImplTest, DynamicAddRemove) { // tcp connections. Http::ConnectionPool::Instance::DrainedCb drained_cb; Tcp::ConnectionPool::Instance::DrainedCb drained_cb2; - EXPECT_CALL(*callbacks, onClusterRemoval(_)).Times(1); + EXPECT_CALL(*callbacks, onClusterRemoval(_)); EXPECT_CALL(*cp, addDrainedCallback(_)).WillOnce(SaveArg<0>(&drained_cb)); EXPECT_CALL(*cp2, addDrainedCallback(_)).WillOnce(SaveArg<0>(&drained_cb2)); EXPECT_TRUE(cluster_manager_->removeCluster("fake_cluster")); @@ -3036,7 +3036,7 @@ TEST_F(ClusterManagerImplTest, AddUpstreamFilters) { create(parseBootstrapFromV3Yaml(yaml)); Network::MockClientConnection* connection = new NiceMock(); EXPECT_CALL(*connection, addReadFilter(_)).Times(0); - EXPECT_CALL(*connection, addWriteFilter(_)).Times(1); + EXPECT_CALL(*connection, addWriteFilter(_)); EXPECT_CALL(*connection, addFilter(_)).Times(0); EXPECT_CALL(factory_.tls_.dispatcher_, createClientConnection_(_, _, _, _)) .WillOnce(Return(connection)); diff --git a/test/common/upstream/eds_test.cc b/test/common/upstream/eds_test.cc index d2c330c14153..a487b2dcc123 100644 --- a/test/common/upstream/eds_test.cc +++ b/test/common/upstream/eds_test.cc @@ -1791,7 +1791,7 @@ TEST_F(EdsAssignmentTimeoutTest, AssignmentTimeoutEnableDisable) { Protobuf::util::TimeUtil::SecondsToDuration(1)); EXPECT_CALL(*interval_timer_, enableTimer(_, _)).Times(2); // Timer enabled twice. - EXPECT_CALL(*interval_timer_, disableTimer()).Times(1); // Timer disabled once. + EXPECT_CALL(*interval_timer_, disableTimer()); // Timer disabled once. EXPECT_CALL(*interval_timer_, enabled()).Times(6); // Includes calls by test. doOnConfigUpdateVerifyNoThrow(cluster_load_assignment_lease); // Check that the timer is enabled. diff --git a/test/common/upstream/health_checker_impl_test.cc b/test/common/upstream/health_checker_impl_test.cc index f6a4653ed660..4c97ea1dd7dd 100644 --- a/test/common/upstream/health_checker_impl_test.cc +++ b/test/common/upstream/health_checker_impl_test.cc @@ -672,7 +672,7 @@ class HttpHealthCheckerImplTest : public Event::TestUsingSimulatedTime, TEST_F(HttpHealthCheckerImplTest, Success) { setupNoServiceValidationHC(); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -848,7 +848,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessIntervalJitterPercent) { TEST_F(HttpHealthCheckerImplTest, SuccessWithSpurious100Continue) { setupNoServiceValidationHC(); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -876,7 +876,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessWithSpurious100Continue) { TEST_F(HttpHealthCheckerImplTest, SuccessWithSpuriousMetadata) { setupNoServiceValidationHC(); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -993,7 +993,7 @@ TEST_F(HttpHealthCheckerImplTest, ZeroRetryInterval) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1069,7 +1069,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheck) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1104,7 +1104,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServicePrefixPatternCheck) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1139,7 +1139,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceExactPatternCheck) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1174,7 +1174,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceRegexPatternCheck) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1218,7 +1218,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithCustomHostValueOnTheHos EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = {test_host}; cluster_->info_->stats().upstream_cx_total_.inc(); @@ -1263,7 +1263,7 @@ TEST_F(HttpHealthCheckerImplTest, EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = {test_host}; cluster_->info_->stats().upstream_cx_total_.inc(); @@ -1297,7 +1297,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithCustomHostValue) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1355,7 +1355,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithAdditionalHeaders) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); auto metadata = TestUtility::parseYaml( R"EOF( filter_metadata: @@ -1418,7 +1418,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithoutUserAgent) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); auto metadata = TestUtility::parseYaml( R"EOF( filter_metadata: @@ -1461,7 +1461,7 @@ TEST_F(HttpHealthCheckerImplTest, ServiceDoesNotMatchFail) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)); EXPECT_CALL(event_logger_, logEjectUnhealthy(_, _, _)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { @@ -1492,7 +1492,7 @@ TEST_F(HttpHealthCheckerImplTest, ServicePatternDoesNotMatchFail) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)); EXPECT_CALL(event_logger_, logEjectUnhealthy(_, _, _)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { @@ -1523,7 +1523,7 @@ TEST_F(HttpHealthCheckerImplTest, ServiceNotPresentInResponseFail) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)); EXPECT_CALL(event_logger_, logEjectUnhealthy(_, _, _)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { @@ -1552,7 +1552,7 @@ TEST_F(HttpHealthCheckerImplTest, ServiceCheckRuntimeOff) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(false)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1578,7 +1578,7 @@ TEST_F(HttpHealthCheckerImplTest, ServiceCheckRuntimeOffWithStringPattern) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(false)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1609,7 +1609,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessStartFailedFailFirstServiceCheck) { TEST_F(HttpHealthCheckerImplTest, SuccessNoTraffic) { setupNoServiceValidationHC(); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1659,7 +1659,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessStartFailedSuccessFirst) { health_checker_->start(); // Test fast success immediately moves us to healthy. - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)); EXPECT_CALL(event_logger_, logAddHealthy(_, _, true)); EXPECT_CALL(runtime_.snapshot_, getInteger("health_check.max_interval", _)).WillOnce(Return(500)); EXPECT_CALL(runtime_.snapshot_, getInteger("health_check.min_interval", _)); @@ -1837,7 +1837,7 @@ TEST_F(HttpHealthCheckerImplTest, HttpFailLogError) { TEST_F(HttpHealthCheckerImplTest, Disconnect) { setupNoServiceValidationHC(); EXPECT_CALL(event_logger_, logUnhealthy(_, _, _, true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::ChangePending)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::ChangePending)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -1973,14 +1973,14 @@ TEST_F(HttpHealthCheckerImplTest, TimeoutAfterDisconnect) { EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_, _)).Times(2); health_checker_->start(); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::ChangePending)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::ChangePending)); EXPECT_CALL(*test_sessions_[0]->interval_timer_, enableTimer(_, _)).Times(2); EXPECT_CALL(*test_sessions_[0]->timeout_timer_, disableTimer()); for (auto& session : test_sessions_) { session->client_connection_->close(Network::ConnectionCloseType::NoFlush); } - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)); EXPECT_CALL(event_logger_, logEjectUnhealthy(_, _, _)); EXPECT_CALL(*test_sessions_[0]->timeout_timer_, disableTimer()); @@ -2432,7 +2432,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithAltPort) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); // Prepares a host with its designated health check port. const HostWithHealthCheckMap hosts{{"127.0.0.1:80", makeHealthCheckConfig(8000)}}; @@ -2738,7 +2738,7 @@ TEST_F(HttpHealthCheckerImplTest, DEPRECATED_FEATURE_TEST(ServiceNameMatch)) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; @@ -2772,7 +2772,7 @@ TEST_F(HttpHealthCheckerImplTest, DEPRECATED_FEATURE_TEST(ServiceNameMismatch)) EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) .WillOnce(Return(true)); - EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)).Times(1); + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)); EXPECT_CALL(event_logger_, logEjectUnhealthy(_, _, _)); cluster_->prioritySet().getMockHostSet(0)->hosts_ = { @@ -3154,7 +3154,7 @@ TEST_F(TcpHealthCheckerImplTest, DataWithoutReusingConnection) { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; expectSessionCreate(); expectClientCreate(); - EXPECT_CALL(*connection_, write(_, _)).Times(1); + EXPECT_CALL(*connection_, write(_, _)); EXPECT_CALL(*timeout_timer_, enableTimer(_, _)); health_checker_->start(); @@ -3163,7 +3163,7 @@ TEST_F(TcpHealthCheckerImplTest, DataWithoutReusingConnection) { // Expected execution flow when a healthcheck is successful and reuse_connection is false. EXPECT_CALL(*timeout_timer_, disableTimer()); EXPECT_CALL(*interval_timer_, enableTimer(_, _)); - EXPECT_CALL(*connection_, close(Network::ConnectionCloseType::NoFlush)).Times(1); + EXPECT_CALL(*connection_, close(Network::ConnectionCloseType::NoFlush)); Buffer::OwnedImpl response; addUint8(response, 2); @@ -3183,7 +3183,7 @@ TEST_F(TcpHealthCheckerImplTest, WrongData) { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; expectSessionCreate(); expectClientCreate(); - EXPECT_CALL(*connection_, write(_, _)).Times(1); + EXPECT_CALL(*connection_, write(_, _)); EXPECT_CALL(*timeout_timer_, enableTimer(_, _)); health_checker_->start(); @@ -3366,7 +3366,7 @@ TEST_F(TcpHealthCheckerImplTest, TimeoutWithoutReusingConnection) { makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; expectSessionCreate(); expectClientCreate(); - EXPECT_CALL(*connection_, write(_, _)).Times(1); + EXPECT_CALL(*connection_, write(_, _)); EXPECT_CALL(*timeout_timer_, enableTimer(_, _)); health_checker_->start(); @@ -3375,7 +3375,7 @@ TEST_F(TcpHealthCheckerImplTest, TimeoutWithoutReusingConnection) { // Expected flow when a healthcheck is successful and reuse_connection is false. EXPECT_CALL(*timeout_timer_, disableTimer()); EXPECT_CALL(*interval_timer_, enableTimer(_, _)); - EXPECT_CALL(*connection_, close(Network::ConnectionCloseType::NoFlush)).Times(1); + EXPECT_CALL(*connection_, close(Network::ConnectionCloseType::NoFlush)); Buffer::OwnedImpl response; addUint8(response, 2); diff --git a/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc b/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc index 30fdbc3948fc..ed4a980036e1 100644 --- a/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc +++ b/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc @@ -190,9 +190,9 @@ TEST_F(GrpcAccessLoggerImplTest, WatermarksOverrun) { // Now allow the flush to happen. The stored log will get logged, and the next log will succeed. EXPECT_CALL(stream, isAboveWriteBufferHighWatermark()).WillOnce(Return(false)); - EXPECT_CALL(stream, sendMessageRaw_(_, _)).Times(1); + EXPECT_CALL(stream, sendMessageRaw_(_, _)); EXPECT_CALL(stream, isAboveWriteBufferHighWatermark()).WillOnce(Return(false)); - EXPECT_CALL(stream, sendMessageRaw_(_, _)).Times(1); + EXPECT_CALL(stream, sendMessageRaw_(_, _)); logger_->log(envoy::data::accesslog::v3::HTTPAccessLogEntry(entry)); EXPECT_EQ( 2, diff --git a/test/extensions/clusters/redis/redis_cluster_test.cc b/test/extensions/clusters/redis/redis_cluster_test.cc index 508e1bf4d5b0..80a84ac10b08 100644 --- a/test/extensions/clusters/redis/redis_cluster_test.cc +++ b/test/extensions/clusters/redis/redis_cluster_test.cc @@ -474,7 +474,7 @@ class RedisClusterTest : public testing::Test, EXPECT_CALL(initialized_, ready()); cluster_->initialize([&]() -> void { initialized_.ready(); }); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); expectClusterSlotResponse(singleSlotPrimaryReplica("127.0.0.1", "127.0.0.2", 22120)); expectHealthyHosts(std::list({"127.0.0.1:22120", "127.0.0.2:22120"})); @@ -482,14 +482,14 @@ class RedisClusterTest : public testing::Test, expectRedisResolve(); EXPECT_CALL(membership_updated_, ready()); resolve_timer_->invokeCallback(); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); expectClusterSlotResponse(twoSlotsPrimaries()); expectHealthyHosts(std::list({"127.0.0.1:22120", "127.0.0.2:22120"})); // No change. expectRedisResolve(); resolve_timer_->invokeCallback(); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1).WillOnce(Return(false)); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).WillOnce(Return(false)); expectClusterSlotResponse(twoSlotsPrimaries()); expectHealthyHosts(std::list({"127.0.0.1:22120", "127.0.0.2:22120"})); @@ -497,7 +497,7 @@ class RedisClusterTest : public testing::Test, expectRedisResolve(); EXPECT_CALL(membership_updated_, ready()); resolve_timer_->invokeCallback(); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); expectClusterSlotResponse(twoSlotsPrimariesWithReplica()); expectHealthyHosts(std::list( {"127.0.0.1:22120", "127.0.0.3:22120", "127.0.0.2:22120", "127.0.0.4:22120"})); @@ -505,7 +505,7 @@ class RedisClusterTest : public testing::Test, // No change. expectRedisResolve(); resolve_timer_->invokeCallback(); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1).WillOnce(Return(false)); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).WillOnce(Return(false)); expectClusterSlotResponse(twoSlotsPrimariesWithReplica()); expectHealthyHosts(std::list( {"127.0.0.1:22120", "127.0.0.3:22120", "127.0.0.2:22120", "127.0.0.4:22120"})); @@ -514,7 +514,7 @@ class RedisClusterTest : public testing::Test, expectRedisResolve(); EXPECT_CALL(membership_updated_, ready()); resolve_timer_->invokeCallback(); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); expectClusterSlotResponse(singleSlotPrimaryReplica("127.0.0.1", "127.0.0.2", 22120)); expectHealthyHosts(std::list({"127.0.0.1:22120", "127.0.0.2:22120"})); } @@ -652,7 +652,7 @@ TEST_P(RedisDnsParamTest, ImmediateResolveDns) { std::list address_pair = std::get<2>(GetParam()); cb(Network::DnsResolver::ResolutionStatus::Success, TestUtility::makeDnsResponse(address_pair)); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); expectClusterSlotResponse( singleSlotPrimaryReplica(address_pair.front(), address_pair.back(), 22120)); return nullptr; @@ -764,7 +764,7 @@ TEST_F(RedisClusterTest, RedisResolveFailure) { resolve_timer_->invokeCallback(); EXPECT_CALL(membership_updated_, ready()); EXPECT_CALL(initialized_, ready()); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); expectClusterSlotResponse(singleSlotPrimaryReplica("127.0.0.1", "127.0.0.2", 22120)); expectHealthyHosts(std::list({"127.0.0.1:22120", "127.0.0.2:22120"})); @@ -836,7 +836,7 @@ TEST_F(RedisClusterTest, RedisErrorResponse) { resolve_timer_->invokeCallback(); EXPECT_CALL(membership_updated_, ready()); EXPECT_CALL(initialized_, ready()); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); std::bitset single_slot_primary(0xfff); std::bitset no_replica(0); expectClusterSlotResponse(createResponse(single_slot_primary, no_replica)); @@ -851,7 +851,7 @@ TEST_F(RedisClusterTest, RedisErrorResponse) { expectRedisResolve(); resolve_timer_->invokeCallback(); if (flags.all()) { - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1).WillOnce(Return(false)); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).WillOnce(Return(false)); } expectClusterSlotResponse(createResponse(flags, no_replica)); expectHealthyHosts(std::list({"127.0.0.1:22120"})); @@ -872,7 +872,7 @@ TEST_F(RedisClusterTest, RedisReplicaErrorResponse) { EXPECT_CALL(membership_updated_, ready()); EXPECT_CALL(initialized_, ready()); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); std::bitset single_slot_primary(0xfff); std::bitset no_replica(0); expectClusterSlotResponse(createResponse(single_slot_primary, no_replica)); @@ -888,7 +888,7 @@ TEST_F(RedisClusterTest, RedisReplicaErrorResponse) { resolve_timer_->invokeCallback(); if (replica_flags.all()) { EXPECT_CALL(membership_updated_, ready()); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1).WillOnce(Return(false)); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).WillOnce(Return(false)); } expectHealthyHosts(std::list({"127.0.0.1:22120"})); expectClusterSlotResponse(createResponse(single_slot_primary, replica_flags)); @@ -976,7 +976,7 @@ TEST_F(RedisClusterTest, HostRemovalAfterHcFail) { EXPECT_CALL(initialized_, ready()); cluster_->initialize([&]() -> void { initialized_.ready(); }); - EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)).Times(1); + EXPECT_CALL(*cluster_callback_, onClusterSlotUpdate(_, _)); expectClusterSlotResponse(singleSlotPrimaryReplica("127.0.0.1", "127.0.0.2", 22120)); // Verify that both hosts are initially marked with FAILED_ACTIVE_HC, then diff --git a/test/extensions/common/aws/credentials_provider_impl_test.cc b/test/extensions/common/aws/credentials_provider_impl_test.cc index 0aa4cc69f814..8c92d9e3b9f1 100644 --- a/test/extensions/common/aws/credentials_provider_impl_test.cc +++ b/test/extensions/common/aws/credentials_provider_impl_test.cc @@ -396,8 +396,8 @@ TEST(CredentialsProviderChainTest, getCredentials_noCredentials) { auto mock_provider1 = std::make_shared(); auto mock_provider2 = std::make_shared(); - EXPECT_CALL(*mock_provider1, getCredentials()).Times(1); - EXPECT_CALL(*mock_provider2, getCredentials()).Times(1); + EXPECT_CALL(*mock_provider1, getCredentials()); + EXPECT_CALL(*mock_provider2, getCredentials()); CredentialsProviderChain chain; chain.add(mock_provider1); @@ -430,7 +430,7 @@ TEST(CredentialsProviderChainTest, getCredentials_secondProviderReturns) { const Credentials creds("access_key", "secret_key"); - EXPECT_CALL(*mock_provider1, getCredentials()).Times(1); + EXPECT_CALL(*mock_provider1, getCredentials()); EXPECT_CALL(*mock_provider2, getCredentials()).WillOnce(Return(creds)); CredentialsProviderChain chain; diff --git a/test/extensions/common/wasm/wasm_test.cc b/test/extensions/common/wasm/wasm_test.cc index cd56089daf5f..8cc954a74034 100644 --- a/test/extensions/common/wasm/wasm_test.cc +++ b/test/extensions/common/wasm/wasm_test.cc @@ -615,8 +615,8 @@ TEST_P(WasmCommonTest, WASI) { wasm->setCreateContextForTesting( nullptr, [](Wasm* wasm, const std::shared_ptr& plugin) -> ContextBase* { auto root_context = new TestContext(wasm, plugin); - EXPECT_CALL(*root_context, log_(spdlog::level::info, Eq("WASI write to stdout"))).Times(1); - EXPECT_CALL(*root_context, log_(spdlog::level::err, Eq("WASI write to stderr"))).Times(1); + EXPECT_CALL(*root_context, log_(spdlog::level::info, Eq("WASI write to stdout"))); + EXPECT_CALL(*root_context, log_(spdlog::level::err, Eq("WASI write to stderr"))); return root_context; }); wasm->start(plugin); diff --git a/test/extensions/filters/common/ext_authz/check_request_utils_test.cc b/test/extensions/filters/common/ext_authz/check_request_utils_test.cc index 310b7c3b98a2..0077b8884e5b 100644 --- a/test/extensions/filters/common/ext_authz/check_request_utils_test.cc +++ b/test/extensions/filters/common/ext_authz/check_request_utils_test.cc @@ -39,11 +39,11 @@ class CheckRequestUtilsTest : public testing::Test { EXPECT_CALL(connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); EXPECT_CALL(connection_, localAddress()).WillOnce(ReturnRef(addr_)); EXPECT_CALL(Const(connection_), ssl()).Times(2).WillRepeatedly(Return(ssl_)); - EXPECT_CALL(callbacks_, streamId()).Times(1).WillOnce(Return(0)); + EXPECT_CALL(callbacks_, streamId()).WillOnce(Return(0)); EXPECT_CALL(callbacks_, decodingBuffer()).WillOnce(Return(buffer_.get())); - EXPECT_CALL(callbacks_, streamInfo()).Times(1).WillOnce(ReturnRef(req_info_)); + EXPECT_CALL(callbacks_, streamInfo()).WillOnce(ReturnRef(req_info_)); EXPECT_CALL(req_info_, protocol()).Times(2).WillRepeatedly(ReturnPointee(&protocol_)); - EXPECT_CALL(req_info_, startTime()).Times(1).WillOnce(Return(SystemTime())); + EXPECT_CALL(req_info_, startTime()).WillOnce(Return(SystemTime())); } void callHttpCheckAndValidateRequestAttributes(bool include_peer_certificate) { @@ -259,7 +259,7 @@ TEST_F(CheckRequestUtilsTest, CheckAttrContextPeer) { EXPECT_CALL(Const(connection_), ssl()).WillRepeatedly(Return(ssl_)); EXPECT_CALL(callbacks_, streamId()).WillRepeatedly(Return(0)); EXPECT_CALL(callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); - EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_, decodingBuffer()); EXPECT_CALL(req_info_, protocol()).WillRepeatedly(ReturnPointee(&protocol_)); EXPECT_CALL(*ssl_, uriSanPeerCertificate()).WillOnce(Return(std::vector{"source"})); EXPECT_CALL(*ssl_, uriSanLocalCertificate()) diff --git a/test/extensions/filters/common/rbac/engine_impl_test.cc b/test/extensions/filters/common/rbac/engine_impl_test.cc index e3727da57d7c..dc09323ef9ff 100644 --- a/test/extensions/filters/common/rbac/engine_impl_test.cc +++ b/test/extensions/filters/common/rbac/engine_impl_test.cc @@ -413,7 +413,7 @@ TEST(RoleBasedAccessControlEngineImpl, ConjunctiveCondition) { NiceMock info; Envoy::Network::Address::InstanceConstSharedPtr addr = Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 123, false); - EXPECT_CALL(Const(info), downstreamLocalAddress()).Times(1).WillRepeatedly(ReturnRef(addr)); + EXPECT_CALL(Const(info), downstreamLocalAddress()).WillOnce(ReturnRef(addr)); checkEngine(engine, false, LogResult::Undecided, info, conn, headers); } diff --git a/test/extensions/filters/http/aws_request_signing/aws_request_signing_filter_test.cc b/test/extensions/filters/http/aws_request_signing/aws_request_signing_filter_test.cc index b280b21eee92..9fb6968faed7 100644 --- a/test/extensions/filters/http/aws_request_signing/aws_request_signing_filter_test.cc +++ b/test/extensions/filters/http/aws_request_signing/aws_request_signing_filter_test.cc @@ -41,7 +41,7 @@ class AwsRequestSigningFilterTest : public testing::Test { // Verify filter functionality when signing works. TEST_F(AwsRequestSigningFilterTest, SignSucceeds) { setup(); - EXPECT_CALL(*(filter_config_->signer_), sign(_)).Times(1); + EXPECT_CALL(*(filter_config_->signer_), sign(_)); Http::TestRequestHeaderMapImpl headers; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, false)); @@ -52,7 +52,7 @@ TEST_F(AwsRequestSigningFilterTest, SignSucceeds) { TEST_F(AwsRequestSigningFilterTest, SignWithHostRewrite) { setup(); filter_config_->host_rewrite_ = "foo"; - EXPECT_CALL(*(filter_config_->signer_), sign(_)).Times(1); + EXPECT_CALL(*(filter_config_->signer_), sign(_)); Http::TestRequestHeaderMapImpl headers; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, false)); diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index e74890235b1e..e3fb0e98efb8 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -49,7 +49,7 @@ class CacheFilterTest : public ::testing::Test { Http::FilterHeadersStatus::StopAllIterationAndWatermark); // The filter should continue decoding when the cache lookup result (miss) is ready. - EXPECT_CALL(decoder_callbacks_, continueDecoding).Times(1); + EXPECT_CALL(decoder_callbacks_, continueDecoding); // The cache lookup callback should be posted to the dispatcher. // Run events on the dispatcher so that the callback is invoked. diff --git a/test/extensions/filters/http/cdn_loop/filter_test.cc b/test/extensions/filters/http/cdn_loop/filter_test.cc index 9c96e26dca2d..a4bd33ef3d04 100644 --- a/test/extensions/filters/http/cdn_loop/filter_test.cc +++ b/test/extensions/filters/http/cdn_loop/filter_test.cc @@ -41,7 +41,7 @@ TEST(CdnLoopFilterTest, OtherCdnsInHeader) { TEST(CdnLoopFilterTest, LoopDetected) { NiceMock decoder_callbacks; - EXPECT_CALL(decoder_callbacks, sendLocalReply(Http::Code::BadGateway, _, _, _, _)).Times(1); + EXPECT_CALL(decoder_callbacks, sendLocalReply(Http::Code::BadGateway, _, _, _, _)); CdnLoopFilter filter("cdn", 0); filter.setDecoderFilterCallbacks(decoder_callbacks); @@ -52,7 +52,7 @@ TEST(CdnLoopFilterTest, LoopDetected) { TEST(CdnLoopFilterTest, MultipleTransitsAllowed) { NiceMock decoder_callbacks; - EXPECT_CALL(decoder_callbacks, sendLocalReply(Http::Code::BadGateway, _, _, _, _)).Times(1); + EXPECT_CALL(decoder_callbacks, sendLocalReply(Http::Code::BadGateway, _, _, _, _)); CdnLoopFilter filter("cdn", 3); filter.setDecoderFilterCallbacks(decoder_callbacks); @@ -101,7 +101,7 @@ TEST(CdnLoopFilterTest, MultipleHeadersAllowed) { TEST(CdnLoopFilterTest, UnparseableHeader) { NiceMock decoder_callbacks; - EXPECT_CALL(decoder_callbacks, sendLocalReply(Http::Code::BadRequest, _, _, _, _)).Times(1); + EXPECT_CALL(decoder_callbacks, sendLocalReply(Http::Code::BadRequest, _, _, _, _)); CdnLoopFilter filter("cdn", 0); filter.setDecoderFilterCallbacks(decoder_callbacks); diff --git a/test/extensions/filters/http/common/jwks_fetcher_test.cc b/test/extensions/filters/http/common/jwks_fetcher_test.cc index a8f7d95ad63a..90a9cc48f40d 100644 --- a/test/extensions/filters/http/common/jwks_fetcher_test.cc +++ b/test/extensions/filters/http/common/jwks_fetcher_test.cc @@ -70,7 +70,7 @@ TEST_F(JwksFetcherTest, TestGetSuccess) { MockJwksReceiver receiver; std::unique_ptr fetcher(JwksFetcher::create(mock_factory_ctx_.cluster_manager_)); EXPECT_TRUE(fetcher != nullptr); - EXPECT_CALL(receiver, onJwksSuccessImpl(testing::_)).Times(1); + EXPECT_CALL(receiver, onJwksSuccessImpl(testing::_)); EXPECT_CALL(receiver, onJwksError(testing::_)).Times(0); // Act @@ -84,7 +84,7 @@ TEST_F(JwksFetcherTest, TestGet400) { std::unique_ptr fetcher(JwksFetcher::create(mock_factory_ctx_.cluster_manager_)); EXPECT_TRUE(fetcher != nullptr); EXPECT_CALL(receiver, onJwksSuccessImpl(testing::_)).Times(0); - EXPECT_CALL(receiver, onJwksError(JwksFetcher::JwksReceiver::Failure::Network)).Times(1); + EXPECT_CALL(receiver, onJwksError(JwksFetcher::JwksReceiver::Failure::Network)); // Act fetcher->fetch(uri_, parent_span_, receiver); @@ -97,7 +97,7 @@ TEST_F(JwksFetcherTest, TestGetNoBody) { std::unique_ptr fetcher(JwksFetcher::create(mock_factory_ctx_.cluster_manager_)); EXPECT_TRUE(fetcher != nullptr); EXPECT_CALL(receiver, onJwksSuccessImpl(testing::_)).Times(0); - EXPECT_CALL(receiver, onJwksError(JwksFetcher::JwksReceiver::Failure::Network)).Times(1); + EXPECT_CALL(receiver, onJwksError(JwksFetcher::JwksReceiver::Failure::Network)); // Act fetcher->fetch(uri_, parent_span_, receiver); @@ -110,7 +110,7 @@ TEST_F(JwksFetcherTest, TestGetInvalidJwks) { std::unique_ptr fetcher(JwksFetcher::create(mock_factory_ctx_.cluster_manager_)); EXPECT_TRUE(fetcher != nullptr); EXPECT_CALL(receiver, onJwksSuccessImpl(testing::_)).Times(0); - EXPECT_CALL(receiver, onJwksError(JwksFetcher::JwksReceiver::Failure::InvalidJwks)).Times(1); + EXPECT_CALL(receiver, onJwksError(JwksFetcher::JwksReceiver::Failure::InvalidJwks)); // Act fetcher->fetch(uri_, parent_span_, receiver); @@ -124,7 +124,7 @@ TEST_F(JwksFetcherTest, TestHttpFailure) { std::unique_ptr fetcher(JwksFetcher::create(mock_factory_ctx_.cluster_manager_)); EXPECT_TRUE(fetcher != nullptr); EXPECT_CALL(receiver, onJwksSuccessImpl(testing::_)).Times(0); - EXPECT_CALL(receiver, onJwksError(JwksFetcher::JwksReceiver::Failure::Network)).Times(1); + EXPECT_CALL(receiver, onJwksError(JwksFetcher::JwksReceiver::Failure::Network)); // Act fetcher->fetch(uri_, parent_span_, receiver); @@ -137,7 +137,7 @@ TEST_F(JwksFetcherTest, TestCancel) { MockJwksReceiver receiver; std::unique_ptr fetcher(JwksFetcher::create(mock_factory_ctx_.cluster_manager_)); EXPECT_TRUE(fetcher != nullptr); - EXPECT_CALL(request, cancel()).Times(1); + EXPECT_CALL(request, cancel()); EXPECT_CALL(receiver, onJwksSuccessImpl(testing::_)).Times(0); EXPECT_CALL(receiver, onJwksError(testing::_)).Times(0); diff --git a/test/extensions/filters/http/compressor/compressor_filter_test.cc b/test/extensions/filters/http/compressor/compressor_filter_test.cc index a8f2571f6266..7f209da033ef 100644 --- a/test/extensions/filters/http/compressor/compressor_filter_test.cc +++ b/test/extensions/filters/http/compressor/compressor_filter_test.cc @@ -19,9 +19,9 @@ TEST(CompressorFilterConfigTests, MakeCompressorTest) { NiceMock runtime; Stats::TestUtil::TestStore stats; auto compressor_factory(std::make_unique()); - EXPECT_CALL(*compressor_factory, createCompressor()).Times(1); - EXPECT_CALL(*compressor_factory, statsPrefix()).Times(1); - EXPECT_CALL(*compressor_factory, contentEncoding()).Times(1); + EXPECT_CALL(*compressor_factory, createCompressor()); + EXPECT_CALL(*compressor_factory, statsPrefix()); + EXPECT_CALL(*compressor_factory, contentEncoding()); CompressorFilterConfig config(compressor_cfg, "test.compressor.", stats, runtime, std::move(compressor_factory)); Envoy::Compression::Compressor::CompressorPtr compressor = config.makeCompressor(); diff --git a/test/extensions/filters/http/dynamo/dynamo_filter_test.cc b/test/extensions/filters/http/dynamo/dynamo_filter_test.cc index 29df4be7e78c..7519814552c6 100644 --- a/test/extensions/filters/http/dynamo/dynamo_filter_test.cc +++ b/test/extensions/filters/http/dynamo/dynamo_filter_test.cc @@ -635,11 +635,9 @@ TEST_F(DynamoFilterTest, PartitionIdStats) { Property(&Stats::Metric::name, "prefix.dynamodb.table.locations.upstream_rq_time"), _)); EXPECT_CALL(stats_, - counter("prefix.dynamodb.table.locations.capacity.GetItem.__partition_id=ition_1")) - .Times(1); + counter("prefix.dynamodb.table.locations.capacity.GetItem.__partition_id=ition_1")); EXPECT_CALL(stats_, - counter("prefix.dynamodb.table.locations.capacity.GetItem.__partition_id=ition_2")) - .Times(1); + counter("prefix.dynamodb.table.locations.capacity.GetItem.__partition_id=ition_2")); Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, @@ -812,12 +810,10 @@ TEST_F(DynamoFilterTest, PartitionIdStatsForSingleTableBatchOperation) { EXPECT_CALL( stats_, - counter("prefix.dynamodb.table.locations.capacity.BatchGetItem.__partition_id=ition_1")) - .Times(1); + counter("prefix.dynamodb.table.locations.capacity.BatchGetItem.__partition_id=ition_1")); EXPECT_CALL( stats_, - counter("prefix.dynamodb.table.locations.capacity.BatchGetItem.__partition_id=ition_2")) - .Times(1); + counter("prefix.dynamodb.table.locations.capacity.BatchGetItem.__partition_id=ition_2")); Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, diff --git a/test/extensions/filters/http/ext_authz/config_test.cc b/test/extensions/filters/http/ext_authz/config_test.cc index ba587f599aff..86c267939004 100644 --- a/test/extensions/filters/http/ext_authz/config_test.cc +++ b/test/extensions/filters/http/ext_authz/config_test.cc @@ -42,11 +42,11 @@ void expectCorrectProtoGrpc(envoy::config::core::v3::ApiVersion api_version) { EXPECT_CALL(context, getServerFactoryContext()) .Times(1) .WillOnce(testing::ReturnRef(server_context)); - EXPECT_CALL(server_context, singletonManager()).Times(1); - EXPECT_CALL(context, threadLocal()).Times(1); - EXPECT_CALL(context, messageValidationVisitor()).Times(1); - EXPECT_CALL(context, clusterManager()).Times(1); - EXPECT_CALL(context, runtime()).Times(1); + EXPECT_CALL(server_context, singletonManager()); + EXPECT_CALL(context, threadLocal()); + EXPECT_CALL(context, messageValidationVisitor()); + EXPECT_CALL(context, clusterManager()); + EXPECT_CALL(context, runtime()); EXPECT_CALL(context, scope()).Times(2); EXPECT_CALL(context.cluster_manager_.async_client_manager_, factoryForGrpcService(_, _, _)) .WillOnce(Invoke([](const envoy::config::core::v3::GrpcService&, Stats::Scope&, bool) { @@ -115,10 +115,10 @@ TEST(HttpExtAuthzConfigTest, CorrectProtoHttp) { ProtobufTypes::MessagePtr proto_config = factory.createEmptyConfigProto(); TestUtility::loadFromYaml(yaml, *proto_config); testing::StrictMock context; - EXPECT_CALL(context, messageValidationVisitor()).Times(1); - EXPECT_CALL(context, clusterManager()).Times(1); - EXPECT_CALL(context, runtime()).Times(1); - EXPECT_CALL(context, scope()).Times(1); + EXPECT_CALL(context, messageValidationVisitor()); + EXPECT_CALL(context, clusterManager()); + EXPECT_CALL(context, runtime()); + EXPECT_CALL(context, scope()); Http::FilterFactoryCb cb = factory.createFilterFactoryFromProto(*proto_config, "stats", context); testing::StrictMock filter_callback; EXPECT_CALL(filter_callback, addStreamDecoderFilter(_)); diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index a850dcc8314a..d0671967e180 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -199,7 +199,7 @@ TEST_F(HttpFilterTest, StatsWithPrefix) { EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(filter_callbacks_, encodeHeaders_(_, _)).Times(1); + EXPECT_CALL(filter_callbacks_, encodeHeaders_(_, _)); Filters::Common::ExtAuthz::Response response{}; response.status = Filters::Common::ExtAuthz::CheckStatus::Error; @@ -399,7 +399,7 @@ TEST_F(HttpFilterTest, RequestDataIsTooLarge) { )EOF"); ON_CALL(filter_callbacks_, connection()).WillByDefault(Return(&connection_)); - EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)).Times(1); + EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)); EXPECT_CALL(connection_, remoteAddress()).Times(0); EXPECT_CALL(connection_, localAddress()).Times(0); EXPECT_CALL(*client_, check(_, _, _, _)).Times(0); @@ -435,7 +435,7 @@ TEST_F(HttpFilterTest, RequestDataWithPartialMessage) { EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)).Times(0); EXPECT_CALL(connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); EXPECT_CALL(connection_, localAddress()).WillOnce(ReturnRef(addr_)); - EXPECT_CALL(*client_, check(_, _, _, _)).Times(1); + EXPECT_CALL(*client_, check(_, _, _, _)); EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(request_headers_, false)); @@ -533,7 +533,7 @@ TEST_F(HttpFilterTest, RequestDataWithSmallBuffer) { EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)).Times(0); EXPECT_CALL(connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); EXPECT_CALL(connection_, localAddress()).WillOnce(ReturnRef(addr_)); - EXPECT_CALL(*client_, check(_, _, _, _)).Times(1); + EXPECT_CALL(*client_, check(_, _, _, _)); EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(request_headers_, false)); @@ -816,7 +816,7 @@ TEST_F(HttpFilterTest, ClearCache) { WithArgs<0>(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks) -> void { request_callbacks_ = &callbacks; }))); - EXPECT_CALL(filter_callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(filter_callbacks_, clearRouteCache()); EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); @@ -861,7 +861,7 @@ TEST_F(HttpFilterTest, ClearCacheRouteHeadersToAppendOnly) { WithArgs<0>(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks) -> void { request_callbacks_ = &callbacks; }))); - EXPECT_CALL(filter_callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(filter_callbacks_, clearRouteCache()); EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); @@ -903,7 +903,7 @@ TEST_F(HttpFilterTest, ClearCacheRouteHeadersToAddOnly) { WithArgs<0>(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks) -> void { request_callbacks_ = &callbacks; }))); - EXPECT_CALL(filter_callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(filter_callbacks_, clearRouteCache()); EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); @@ -945,7 +945,7 @@ TEST_F(HttpFilterTest, ClearCacheRouteHeadersToRemoveOnly) { WithArgs<0>(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks) -> void { request_callbacks_ = &callbacks; }))); - EXPECT_CALL(filter_callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(filter_callbacks_, clearRouteCache()); EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); @@ -1192,7 +1192,7 @@ TEST_F(HttpFilterTest, FilterEnabled) { .WillByDefault(Return(true)); // Make sure check is called once. - EXPECT_CALL(*client_, check(_, _, _, _)).Times(1); + EXPECT_CALL(*client_, check(_, _, _, _)); // Engage the filter. EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); @@ -1259,7 +1259,7 @@ TEST_F(HttpFilterTest, MetadataEnabled) { prepareCheck(); // Make sure check is called once. - EXPECT_CALL(*client_, check(_, _, _, _)).Times(1); + EXPECT_CALL(*client_, check(_, _, _, _)); // Engage the filter. EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); @@ -1394,7 +1394,7 @@ TEST_F(HttpFilterTest, FilterEnabledAndMetadataEnabled) { prepareCheck(); // Make sure check is called once. - EXPECT_CALL(*client_, check(_, _, _, _)).Times(1); + EXPECT_CALL(*client_, check(_, _, _, _)); // Engage the filter. EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); @@ -1531,7 +1531,7 @@ TEST_P(HttpFilterTestParam, DisabledOnRoute) { // baseline: make sure that when not disabled, check is called test_disable(false); - EXPECT_CALL(*client_, check(_, _, testing::A(), _)).Times(1); + EXPECT_CALL(*client_, check(_, _, testing::A(), _)); // Engage the filter. EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); @@ -1573,7 +1573,7 @@ TEST_P(HttpFilterTestParam, DisabledOnRouteWithRequestBody) { test_disable(false); ON_CALL(filter_callbacks_, connection()).WillByDefault(Return(&connection_)); // When filter is not disabled, setDecoderBufferLimit is called. - EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)).Times(1); + EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)); EXPECT_CALL(connection_, remoteAddress()).Times(0); EXPECT_CALL(connection_, localAddress()).Times(0); EXPECT_CALL(*client_, check(_, _, _, _)).Times(0); @@ -2076,7 +2076,7 @@ TEST_P(HttpFilterTestParam, DisableRequestBodyBufferingOnRoute) { test_disable_request_body_buffering(false); ON_CALL(filter_callbacks_, connection()).WillByDefault(Return(&connection_)); // When request body buffering is not skipped, setDecoderBufferLimit is called. - EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)).Times(1); + EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)); EXPECT_CALL(connection_, remoteAddress()).Times(0); EXPECT_CALL(connection_, localAddress()).Times(0); EXPECT_CALL(*client_, check(_, _, _, _)).Times(0); @@ -2089,7 +2089,7 @@ TEST_P(HttpFilterTestParam, DisableRequestBodyBufferingOnRoute) { EXPECT_CALL(filter_callbacks_, setDecoderBufferLimit(_)).Times(0); EXPECT_CALL(connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); EXPECT_CALL(connection_, localAddress()).WillOnce(ReturnRef(addr_)); - EXPECT_CALL(*client_, check(_, _, _, _)).Times(1); + EXPECT_CALL(*client_, check(_, _, _, _)); EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); diff --git a/test/extensions/filters/http/ext_proc/client_test.cc b/test/extensions/filters/http/ext_proc/client_test.cc index d027934fc72b..6a080196d32d 100644 --- a/test/extensions/filters/http/ext_proc/client_test.cc +++ b/test/extensions/filters/http/ext_proc/client_test.cc @@ -85,7 +85,7 @@ class ExtProcStreamTest : public testing::Test, public ExternalProcessorCallback TEST_F(ExtProcStreamTest, OpenCloseStream) { auto stream = client_->start(*this, 200ms); - EXPECT_CALL(stream_, closeStream()).Times(1); + EXPECT_CALL(stream_, closeStream()); stream->close(); } @@ -95,13 +95,13 @@ TEST_F(ExtProcStreamTest, SendToStream) { EXPECT_CALL(stream_, sendMessageRaw_(_, false)); ProcessingRequest req; stream->send(std::move(req), false); - EXPECT_CALL(stream_, closeStream()).Times(1); + EXPECT_CALL(stream_, closeStream()); stream->close(); } TEST_F(ExtProcStreamTest, SendAndClose) { auto stream = client_->start(*this, 200ms); - EXPECT_CALL(stream_, sendMessageRaw_(_, true)).Times(1); + EXPECT_CALL(stream_, sendMessageRaw_(_, true)); ProcessingRequest req; stream->send(std::move(req), true); } @@ -131,7 +131,7 @@ TEST_F(ExtProcStreamTest, ReceiveFromStream) { auto empty_response_trailers = Http::ResponseTrailerMapImpl::create(); stream_callbacks_->onReceiveTrailingMetadata(std::move(empty_response_trailers)); - EXPECT_CALL(stream_, closeStream()).Times(1); + EXPECT_CALL(stream_, closeStream()); stream->close(); } @@ -146,7 +146,7 @@ TEST_F(ExtProcStreamTest, StreamClosed) { EXPECT_TRUE(grpc_closed_); EXPECT_EQ(grpc_status_, 0); - EXPECT_CALL(stream_, closeStream()).Times(1); + EXPECT_CALL(stream_, closeStream()); stream->close(); } @@ -161,7 +161,7 @@ TEST_F(ExtProcStreamTest, StreamError) { EXPECT_FALSE(grpc_closed_); EXPECT_EQ(grpc_status_, 123); - EXPECT_CALL(stream_, closeStream()).Times(1); + EXPECT_CALL(stream_, closeStream()); stream->close(); } diff --git a/test/extensions/filters/http/ext_proc/config_test.cc b/test/extensions/filters/http/ext_proc/config_test.cc index bcab939634c1..59d3757457ab 100644 --- a/test/extensions/filters/http/ext_proc/config_test.cc +++ b/test/extensions/filters/http/ext_proc/config_test.cc @@ -37,7 +37,7 @@ TEST(HttpExtProcConfigTest, CorrectConfig) { TestUtility::loadFromYaml(yaml, *proto_config); testing::StrictMock context; - EXPECT_CALL(context, messageValidationVisitor()).Times(1); + EXPECT_CALL(context, messageValidationVisitor()); Http::FilterFactoryCb cb = factory.createFilterFactoryFromProto(*proto_config, "stats", context); Http::MockFilterChainFactoryCallbacks filter_callback; EXPECT_CALL(filter_callback, addStreamFilter(_)); diff --git a/test/extensions/filters/http/ip_tagging/ip_tagging_filter_test.cc b/test/extensions/filters/http/ip_tagging/ip_tagging_filter_test.cc index 492600503f60..7fd73e30f8d8 100644 --- a/test/extensions/filters/http/ip_tagging/ip_tagging_filter_test.cc +++ b/test/extensions/filters/http/ip_tagging/ip_tagging_filter_test.cc @@ -69,8 +69,8 @@ TEST_F(IpTaggingFilterTest, InternalRequest) { EXPECT_CALL(filter_callbacks_.stream_info_, downstreamRemoteAddress()) .WillOnce(ReturnRef(remote_address)); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.internal_request.hit")).Times(1); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")).Times(1); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.internal_request.hit")); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); EXPECT_EQ("internal_request", request_headers.get_(Http::Headers::get().EnvoyIpTags)); @@ -97,8 +97,8 @@ request_type: external EXPECT_EQ(FilterRequestType::EXTERNAL, config_->requestType()); Http::TestRequestHeaderMapImpl request_headers; - EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")).Times(1); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.external_request.hit")).Times(1); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.external_request.hit")); Network::Address::InstanceConstSharedPtr remote_address = Network::Utility::parseInternetAddress("1.2.3.4"); @@ -135,8 +135,8 @@ request_type: both Http::TestRequestHeaderMapImpl request_headers{{"x-envoy-internal", "true"}}; EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")).Times(2); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.internal_request.hit")).Times(1); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.external_request.hit")).Times(1); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.internal_request.hit")); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.external_request.hit")); Network::Address::InstanceConstSharedPtr remote_address = Network::Utility::parseInternetAddress("1.2.3.5"); @@ -163,8 +163,8 @@ TEST_F(IpTaggingFilterTest, NoHits) { Network::Utility::parseInternetAddress("10.2.3.5"); EXPECT_CALL(filter_callbacks_.stream_info_, downstreamRemoteAddress()) .WillOnce(ReturnRef(remote_address)); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.no_hit")).Times(1); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")).Times(1); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.no_hit")); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); EXPECT_FALSE(request_headers.has(Http::Headers::get().EnvoyIpTags)); @@ -213,9 +213,9 @@ request_type: both EXPECT_CALL(filter_callbacks_.stream_info_, downstreamRemoteAddress()) .WillOnce(ReturnRef(remote_address)); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")).Times(1); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.internal_request.hit")).Times(1); - EXPECT_CALL(stats_, counter("prefix.ip_tagging.duplicate_request.hit")).Times(1); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.total")); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.internal_request.hit")); + EXPECT_CALL(stats_, counter("prefix.ip_tagging.duplicate_request.hit")); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); @@ -276,7 +276,7 @@ TEST_F(IpTaggingFilterTest, ClearRouteCache) { EXPECT_CALL(filter_callbacks_.stream_info_, downstreamRemoteAddress()) .WillOnce(ReturnRef(remote_address)); - EXPECT_CALL(filter_callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(filter_callbacks_, clearRouteCache()); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); EXPECT_EQ("internal_request", request_headers.get_(Http::Headers::get().EnvoyIpTags)); diff --git a/test/extensions/filters/http/jwt_authn/all_verifier_test.cc b/test/extensions/filters/http/jwt_authn/all_verifier_test.cc index 2afa53ed0c98..08cea40b9251 100644 --- a/test/extensions/filters/http/jwt_authn/all_verifier_test.cc +++ b/test/extensions/filters/http/jwt_authn/all_verifier_test.cc @@ -116,14 +116,14 @@ class AllowFailedInSingleRequirementTest : public AllVerifierTest { }; TEST_F(AllowFailedInSingleRequirementTest, NoJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); } TEST_F(AllowFailedInSingleRequirementTest, BadJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ExpiredToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -131,7 +131,7 @@ TEST_F(AllowFailedInSingleRequirementTest, BadJwt) { } TEST_F(AllowFailedInSingleRequirementTest, MissingIssToken) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ES256WithoutIssToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -139,7 +139,7 @@ TEST_F(AllowFailedInSingleRequirementTest, MissingIssToken) { } TEST_F(AllowFailedInSingleRequirementTest, OneGoodJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -147,7 +147,7 @@ TEST_F(AllowFailedInSingleRequirementTest, OneGoodJwt) { } TEST_F(AllowFailedInSingleRequirementTest, TwoGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -157,7 +157,7 @@ TEST_F(AllowFailedInSingleRequirementTest, TwoGoodJwts) { } TEST_F(AllowFailedInSingleRequirementTest, GoodAndBadJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, ExpiredToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -182,14 +182,14 @@ class SingleAllowMissingInOrListTest : public AllVerifierTest { }; TEST_F(SingleAllowMissingInOrListTest, NoJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); } TEST_F(SingleAllowMissingInOrListTest, BadJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ExpiredToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -197,7 +197,7 @@ TEST_F(SingleAllowMissingInOrListTest, BadJwt) { } TEST_F(SingleAllowMissingInOrListTest, MissingIssToken) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ES256WithoutIssToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -205,7 +205,7 @@ TEST_F(SingleAllowMissingInOrListTest, MissingIssToken) { } TEST_F(SingleAllowMissingInOrListTest, OneGoodJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -213,7 +213,7 @@ TEST_F(SingleAllowMissingInOrListTest, OneGoodJwt) { } TEST_F(SingleAllowMissingInOrListTest, TwoGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -223,7 +223,7 @@ TEST_F(SingleAllowMissingInOrListTest, TwoGoodJwts) { } TEST_F(SingleAllowMissingInOrListTest, GoodAndBadJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, ExpiredToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -249,14 +249,14 @@ class AllowFailedInOrListTest : public AllVerifierTest { }; TEST_F(AllowFailedInOrListTest, NoJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); } TEST_F(AllowFailedInOrListTest, BadJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ExpiredToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -264,7 +264,7 @@ TEST_F(AllowFailedInOrListTest, BadJwt) { } TEST_F(AllowFailedInOrListTest, GoodAndBadJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, NonExistKidToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -274,7 +274,7 @@ TEST_F(AllowFailedInOrListTest, GoodAndBadJwt) { } TEST_F(AllowFailedInOrListTest, TwoGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -286,7 +286,7 @@ TEST_F(AllowFailedInOrListTest, TwoGoodJwts) { } TEST_F(AllowFailedInOrListTest, BadAndGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ExpiredToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -313,14 +313,14 @@ class AllowFailedInAndListTest : public AllVerifierTest { }; TEST_F(AllowFailedInAndListTest, NoJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::JwtMissed)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtMissed)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); } TEST_F(AllowFailedInAndListTest, BadJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ExpiredToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -328,7 +328,7 @@ TEST_F(AllowFailedInAndListTest, BadJwt) { } TEST_F(AllowFailedInAndListTest, OneGoodJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{ {kExampleHeader, GoodToken}, }; @@ -338,7 +338,7 @@ TEST_F(AllowFailedInAndListTest, OneGoodJwt) { } TEST_F(AllowFailedInAndListTest, GoodAndBadJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, NonExistKidToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -349,7 +349,7 @@ TEST_F(AllowFailedInAndListTest, GoodAndBadJwts) { } TEST_F(AllowFailedInAndListTest, TwoGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -380,14 +380,14 @@ class AllowFailedInAndOfOrListTest : public AllVerifierTest { }; TEST_F(AllowFailedInAndOfOrListTest, NoJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); } TEST_F(AllowFailedInAndOfOrListTest, BadJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ExpiredToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -395,7 +395,7 @@ TEST_F(AllowFailedInAndOfOrListTest, BadJwt) { } TEST_F(AllowFailedInAndOfOrListTest, OneGoodJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -403,7 +403,7 @@ TEST_F(AllowFailedInAndOfOrListTest, OneGoodJwt) { } TEST_F(AllowFailedInAndOfOrListTest, OtherGoodJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -411,7 +411,7 @@ TEST_F(AllowFailedInAndOfOrListTest, OtherGoodJwt) { } TEST_F(AllowFailedInAndOfOrListTest, BadAndGoodJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ExpiredToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -421,7 +421,7 @@ TEST_F(AllowFailedInAndOfOrListTest, BadAndGoodJwt) { } TEST_F(AllowFailedInAndOfOrListTest, TwoGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -447,7 +447,7 @@ class AllowMissingInOrListTest : public AllVerifierTest { }; TEST_F(AllowMissingInOrListTest, NoJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -455,7 +455,7 @@ TEST_F(AllowMissingInOrListTest, NoJwt) { TEST_F(AllowMissingInOrListTest, BadJwt) { // Bad JWT should fail. - EXPECT_CALL(mock_cb_, onComplete(Status::JwtVerificationFail)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtVerificationFail)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, NonExistKidToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -463,7 +463,7 @@ TEST_F(AllowMissingInOrListTest, BadJwt) { } TEST_F(AllowMissingInOrListTest, OtherGoodJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -472,7 +472,7 @@ TEST_F(AllowMissingInOrListTest, OtherGoodJwt) { } TEST_F(AllowMissingInOrListTest, BadAndGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::JwtVerificationFail)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtVerificationFail)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, NonExistKidToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -498,7 +498,7 @@ class AllowMissingInAndListTest : public AllVerifierTest { }; TEST_F(AllowMissingInAndListTest, NoJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::JwtMissed)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtMissed)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -506,7 +506,7 @@ TEST_F(AllowMissingInAndListTest, NoJwt) { TEST_F(AllowMissingInAndListTest, BadJwt) { // Bad JWT should fail. - EXPECT_CALL(mock_cb_, onComplete(Status::JwtVerificationFail)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtVerificationFail)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, NonExistKidToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -515,7 +515,7 @@ TEST_F(AllowMissingInAndListTest, BadJwt) { TEST_F(AllowMissingInAndListTest, GoodJwt) { // Bad JWT should fail. - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -524,7 +524,7 @@ TEST_F(AllowMissingInAndListTest, GoodJwt) { TEST_F(AllowMissingInAndListTest, TwoGoodJwts) { // Bad JWT should fail. - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -555,7 +555,7 @@ class AllowMissingInAndOfOrListTest : public AllVerifierTest { }; TEST_F(AllowMissingInAndOfOrListTest, NoJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -563,7 +563,7 @@ TEST_F(AllowMissingInAndOfOrListTest, NoJwt) { TEST_F(AllowMissingInAndOfOrListTest, BadJwt) { // Bad JWT should fail. - EXPECT_CALL(mock_cb_, onComplete(Status::JwtVerificationFail)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtVerificationFail)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, NonExistKidToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -571,7 +571,7 @@ TEST_F(AllowMissingInAndOfOrListTest, BadJwt) { } TEST_F(AllowMissingInAndOfOrListTest, OneGoodJwt) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -579,7 +579,7 @@ TEST_F(AllowMissingInAndOfOrListTest, OneGoodJwt) { } TEST_F(AllowMissingInAndOfOrListTest, TwoGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -589,7 +589,7 @@ TEST_F(AllowMissingInAndOfOrListTest, TwoGoodJwts) { } TEST_F(AllowMissingInAndOfOrListTest, GoodAndBadJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); // Use the token with example.com issuer for x-other. auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, GoodToken}, {kOtherHeader, GoodToken}}; @@ -600,7 +600,7 @@ TEST_F(AllowMissingInAndOfOrListTest, GoodAndBadJwts) { } TEST_F(AllowMissingInAndOfOrListTest, BadAndGoodJwts) { - EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)); auto headers = Http::TestRequestHeaderMapImpl{{kExampleHeader, ExpiredToken}, {kOtherHeader, OtherGoodToken}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); diff --git a/test/extensions/filters/http/jwt_authn/authenticator_test.cc b/test/extensions/filters/http/jwt_authn/authenticator_test.cc index fbb732a63298..4cc683db8caa 100644 --- a/test/extensions/filters/http/jwt_authn/authenticator_test.cc +++ b/test/extensions/filters/http/jwt_authn/authenticator_test.cc @@ -204,7 +204,7 @@ TEST_F(AuthenticatorTest, TestMissedJWT) { // Test multiple tokens; the one from query parameter is bad, verification should fail. TEST_F(AuthenticatorTest, TestMultipleJWTOneBadFromQuery) { - EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)).Times(1); + EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)); // headers with multiple tokens: one good, one bad Http::TestRequestHeaderMapImpl headers{ @@ -217,7 +217,7 @@ TEST_F(AuthenticatorTest, TestMultipleJWTOneBadFromQuery) { // Test multiple tokens; the one from header is bad, verification should fail. TEST_F(AuthenticatorTest, TestMultipleJWTOneBadFromHeader) { - EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)).Times(1); + EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)); // headers with multiple tokens: one good, one bad Http::TestRequestHeaderMapImpl headers{ @@ -230,7 +230,7 @@ TEST_F(AuthenticatorTest, TestMultipleJWTOneBadFromHeader) { // Test multiple tokens; all are good, verification is ok. TEST_F(AuthenticatorTest, TestMultipleJWTAllGood) { - EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)).Times(1); + EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)); // headers with multiple tokens: all are good Http::TestRequestHeaderMapImpl headers{ @@ -245,7 +245,7 @@ TEST_F(AuthenticatorTest, TestMultipleJWTAllGood) { TEST_F(AuthenticatorTest, TestMultipleJWTOneBadAllowFails) { createAuthenticator(nullptr, absl::make_optional(ProviderName), /*allow_failed=*/true, /*all_missing=*/false); - EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)).Times(1); + EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)); // headers with multiple tokens: one good, one bad Http::TestRequestHeaderMapImpl headers{ @@ -382,10 +382,10 @@ TEST_F(AuthenticatorTest, TestPubkeyFetchFail) { // onComplete() callback should not be called, but internal request->cancel() should be called. // Most importantly, no crash. TEST_F(AuthenticatorTest, TestOnDestroy) { - EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)).Times(1); + EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)); // Cancel is called once. - EXPECT_CALL(*raw_fetcher_, cancel()).Times(1); + EXPECT_CALL(*raw_fetcher_, cancel()); Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; initTokenExtractor(); diff --git a/test/extensions/filters/http/jwt_authn/group_verifier_test.cc b/test/extensions/filters/http/jwt_authn/group_verifier_test.cc index d9315ce42c70..ab14d6243cb0 100644 --- a/test/extensions/filters/http/jwt_authn/group_verifier_test.cc +++ b/test/extensions/filters/http/jwt_authn/group_verifier_test.cc @@ -91,7 +91,7 @@ class GroupVerifierTest : public testing::Test { } callback(status); })); - EXPECT_CALL(*mock_auth, onDestroy()).Times(1); + EXPECT_CALL(*mock_auth, onDestroy()); mock_auths_[it.first] = std::move(mock_auth); } createVerifier(); @@ -118,7 +118,7 @@ class GroupVerifierTest : public testing::Test { SetPayloadCallback, AuthenticatorCallback callback) { callbacks_[iss] = std::move(callback); })); - EXPECT_CALL(*mock_auth, onDestroy()).Times(1); + EXPECT_CALL(*mock_auth, onDestroy()); mock_auths_[provider] = std::move(mock_auth); } createVerifier(); @@ -171,7 +171,7 @@ TEST_F(GroupVerifierTest, DeeplyNestedAnys) { EXPECT_TRUE(TestUtility::protoEqual(payload, getExpectedPayload({"example_provider"}))); })); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{ {"sec-istio-auth-userinfo", ""}, }; @@ -208,7 +208,7 @@ TEST_F(GroupVerifierTest, CanHandleUnexpectedEnd) { mock_auths_["example_provider"] = std::move(mock_auth); createVerifier(); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -225,7 +225,7 @@ TEST_F(GroupVerifierTest, TestRequiresAll) { payload, getExpectedPayload({"example_provider", "other_provider"}))); })); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -243,7 +243,7 @@ TEST_F(GroupVerifierTest, TestRequiresAllBadFormat) { // onComplete with failure status, not payload EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtBadFormat)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtBadFormat)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -266,7 +266,7 @@ TEST_F(GroupVerifierTest, TestRequiresAllMissing) { // onComplete with failure status, not payload EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtMissed)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtMissed)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -289,7 +289,7 @@ TEST_F(GroupVerifierTest, TestRequiresAllBothFailed) { // onComplete with failure status, not payload EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtUnknownIssuer)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtUnknownIssuer)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -311,7 +311,7 @@ TEST_F(GroupVerifierTest, TestRequiresAnyFirstAuthOK) { EXPECT_TRUE(TestUtility::protoEqual(payload, getExpectedPayload({"example_provider"}))); })); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -332,7 +332,7 @@ TEST_F(GroupVerifierTest, TestRequiresAnyLastAuthOk) { EXPECT_TRUE(TestUtility::protoEqual(payload, getExpectedPayload({"other_provider"}))); })); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -353,7 +353,7 @@ TEST_F(GroupVerifierTest, TestRequiresAnyAllAuthFailed) { // onComplete with failure status, not payload EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtHeaderBadKid)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtHeaderBadKid)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -377,7 +377,7 @@ TEST_F(GroupVerifierTest, TestRequiresAnyLastIsJwtMissed) { // onComplete with failure status, not payload EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtHeaderBadKid)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtHeaderBadKid)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -398,7 +398,7 @@ TEST_F(GroupVerifierTest, TestRequiresAnyLastIsJwtUnknownIssuer) { // onComplete with failure status, not payload EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtHeaderBadKid)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtHeaderBadKid)); auto headers = Http::TestRequestHeaderMapImpl{ {"example-auth-userinfo", ""}, {"other-auth-userinfo", ""}, @@ -419,7 +419,7 @@ TEST_F(GroupVerifierTest, TestAnyInAllFirstAnyIsOk) { EXPECT_TRUE(TestUtility::protoEqual(payload, getExpectedPayload({"provider_1", "provider_3"}))); })); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -437,7 +437,7 @@ TEST_F(GroupVerifierTest, TestAnyInAllLastAnyIsOk) { EXPECT_TRUE(TestUtility::protoEqual(payload, getExpectedPayload({"provider_2", "provider_3"}))); })); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -452,7 +452,7 @@ TEST_F(GroupVerifierTest, TestAnyInAllBothInRequireAnyIsOk) { // AsyncMockVerifier doesn't set payload EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -469,7 +469,7 @@ TEST_F(GroupVerifierTest, TestAnyInAllBothInRequireAnyFailed) { std::vector{"provider_1", "provider_2", "provider_3"}); EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::JwksFetchFail)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwksFetchFail)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -487,7 +487,7 @@ TEST_F(GroupVerifierTest, TestAllInAnyBothRequireAllFailed) { StatusMap{{"provider_1", Status::JwksFetchFail}, {"provider_3", Status::JwtExpired}}); EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -502,7 +502,7 @@ TEST_F(GroupVerifierTest, TestAllInAnyFirstAllIsOk) { // AsyncMockVerifier doesn't set payload EXPECT_CALL(mock_cb_, setPayload(_)).Times(0); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -518,7 +518,7 @@ TEST_F(GroupVerifierTest, TestAllInAnyLastAllIsOk) { createAsyncMockAuthsAndVerifier( std::vector{"provider_1", "provider_2", "provider_3", "provider_4"}); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -534,7 +534,7 @@ TEST_F(GroupVerifierTest, TestAllInAnyBothRequireAllAreOk) { createAsyncMockAuthsAndVerifier( std::vector{"provider_1", "provider_2", "provider_3", "provider_4"}); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); verifier_->verify(context_); @@ -554,7 +554,7 @@ TEST_F(GroupVerifierTest, TestRequiresAnyWithAllowFailed) { ->mutable_allow_missing_or_failed(); createAsyncMockAuthsAndVerifier(std::vector{"example_provider", "other_provider"}); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -573,7 +573,7 @@ TEST_F(GroupVerifierTest, TestRequiresAnyWithAllowMissingButFailed) { ->mutable_allow_missing(); createAsyncMockAuthsAndVerifier(std::vector{"example_provider", "other_provider"}); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtExpired)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -592,7 +592,7 @@ TEST_F(GroupVerifierTest, TestRequiresAnyWithAllowMissingButOk) { ->mutable_allow_missing(); createAsyncMockAuthsAndVerifier(std::vector{"example_provider", "other_provider"}); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); diff --git a/test/extensions/filters/http/jwt_authn/provider_verifier_test.cc b/test/extensions/filters/http/jwt_authn/provider_verifier_test.cc index e7dd327729e0..8c7d2e1cf837 100644 --- a/test/extensions/filters/http/jwt_authn/provider_verifier_test.cc +++ b/test/extensions/filters/http/jwt_authn/provider_verifier_test.cc @@ -62,7 +62,7 @@ TEST_F(ProviderVerifierTest, TestOkJWT) { EXPECT_TRUE(TestUtility::protoEqual(payload, getExpectedPayload("my_payload"))); })); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto headers = Http::TestRequestHeaderMapImpl{ {"Authorization", "Bearer " + std::string(GoodToken)}, @@ -84,13 +84,13 @@ TEST_F(ProviderVerifierTest, TestSpanPassedDown) { EXPECT_TRUE(TestUtility::protoEqual(payload, getExpectedPayload("my_payload"))); })); - EXPECT_CALL(mock_cb_, onComplete(Status::Ok)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::Ok)); auto options = Http::AsyncClient::RequestOptions() .setTimeout(std::chrono::milliseconds(5 * 1000)) .setParentSpan(parent_span_) .setChildSpanName("JWT Remote PubKey Fetch"); - EXPECT_CALL(mock_factory_ctx_.cluster_manager_.async_client_, send_(_, _, Eq(options))).Times(1); + EXPECT_CALL(mock_factory_ctx_.cluster_manager_.async_client_, send_(_, _, Eq(options))); auto headers = Http::TestRequestHeaderMapImpl{ {"Authorization", "Bearer " + std::string(GoodToken)}, @@ -104,7 +104,7 @@ TEST_F(ProviderVerifierTest, TestMissedJWT) { TestUtility::loadFromYaml(ExampleConfig, proto_config_); createVerifier(); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtMissed)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtMissed)); auto headers = Http::TestRequestHeaderMapImpl{{"sec-istio-auth-userinfo", ""}}; context_ = Verifier::createContext(headers, parent_span_, &mock_cb_); @@ -139,7 +139,7 @@ TEST_F(ProviderVerifierTest, TestTokenRequirementProviderMismatch) { TestUtility::loadFromYaml(config, proto_config_); createVerifier(); - EXPECT_CALL(mock_cb_, onComplete(Status::JwtUnknownIssuer)).Times(1); + EXPECT_CALL(mock_cb_, onComplete(Status::JwtUnknownIssuer)); auto headers = Http::TestRequestHeaderMapImpl{ {"Authorization", "Bearer " + std::string(GoodToken)}, diff --git a/test/extensions/filters/http/local_ratelimit/config_test.cc b/test/extensions/filters/http/local_ratelimit/config_test.cc index 3bbf10ad1ee1..3f48a5830e21 100644 --- a/test/extensions/filters/http/local_ratelimit/config_test.cc +++ b/test/extensions/filters/http/local_ratelimit/config_test.cc @@ -59,7 +59,7 @@ stat_prefix: test NiceMock context; - EXPECT_CALL(context.dispatcher_, createTimer_(_)).Times(1); + EXPECT_CALL(context.dispatcher_, createTimer_(_)); const auto route_config = factory.createRouteSpecificFilterConfig( *proto_config, context, ProtobufMessage::getNullValidationVisitor()); const auto* config = dynamic_cast(route_config.get()); @@ -81,7 +81,7 @@ stat_prefix: test NiceMock context; - EXPECT_CALL(context.dispatcher_, createTimer_(_)).Times(1); + EXPECT_CALL(context.dispatcher_, createTimer_(_)); const auto route_config = factory.createRouteSpecificFilterConfig( *proto_config, context, ProtobufMessage::getNullValidationVisitor()); const auto* config = dynamic_cast(route_config.get()); @@ -119,7 +119,7 @@ stat_prefix: test NiceMock context; - EXPECT_CALL(context.dispatcher_, createTimer_(_)).Times(1); + EXPECT_CALL(context.dispatcher_, createTimer_(_)); EXPECT_THROW(factory.createRouteSpecificFilterConfig(*proto_config, context, ProtobufMessage::getNullValidationVisitor()), EnvoyException); diff --git a/test/extensions/filters/http/lua/lua_filter_test.cc b/test/extensions/filters/http/lua/lua_filter_test.cc index b7e2b7cccec2..de03c5204d27 100644 --- a/test/extensions/filters/http/lua/lua_filter_test.cc +++ b/test/extensions/filters/http/lua/lua_filter_test.cc @@ -106,7 +106,7 @@ class LuaHttpFilterTest : public testing::Test { void setupSecureConnection(const bool secure) { ssl_ = std::make_shared>(); EXPECT_CALL(decoder_callbacks_, connection()).WillOnce(Return(&connection_)); - EXPECT_CALL(Const(connection_), ssl()).Times(1).WillOnce(Return(secure ? ssl_ : nullptr)); + EXPECT_CALL(Const(connection_), ssl()).WillOnce(Return(secure ? ssl_ : nullptr)); } void setupMetadata(const std::string& yaml) { diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index e0321f2c2248..e52e2f83d13d 100644 --- a/test/extensions/filters/http/oauth2/filter_test.cc +++ b/test/extensions/filters/http/oauth2/filter_test.cc @@ -204,7 +204,7 @@ TEST_F(OAuth2Test, OAuthOkPass) { }; // cookie-validation mocking - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(true)); // Sanitized return reference mocking @@ -250,7 +250,7 @@ TEST_F(OAuth2Test, OAuthErrorNonOAuthHttpCallback) { }; // explicitly tell the validator to fail the validation - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); @@ -275,7 +275,7 @@ TEST_F(OAuth2Test, OAuthErrorQueryString) { {Http::Headers::get().ContentType.get(), "text/plain"}, }; - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); @@ -303,7 +303,7 @@ TEST_F(OAuth2Test, OAuthCallbackStartsAuthentication) { }; // Deliberately fail the HMAC Validation check. - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); EXPECT_CALL(*oauth_client_, asyncGetAccessToken("123", TEST_CLIENT_ID, "asdf_client_secret_fdsa", @@ -327,7 +327,7 @@ TEST_F(OAuth2Test, OAuthOptionsRequestAndContinue) { {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Options}, }; - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); } @@ -414,7 +414,7 @@ TEST_F(OAuth2Test, OAuthTestInvalidUrlInStateQueryParam) { }; // Succeed the HMAC validation. - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(true)); std::string legit_token{"legit_token"}; @@ -447,7 +447,7 @@ TEST_F(OAuth2Test, OAuthTestCallbackUrlInStateQueryParam) { }; // Succeed the HMAC validation. - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(true)); std::string legit_token{"legit_token"}; @@ -502,7 +502,7 @@ TEST_F(OAuth2Test, OAuthTestUpdatePathAfterSuccess) { }; // Succeed the HMAC validation. - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(true)); std::string legit_token{"legit_token"}; @@ -557,7 +557,7 @@ TEST_F(OAuth2Test, OAuthTestFullFlowPostWithParameters) { }; // Fail the validation to trigger the OAuth flow. - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); // Check that the redirect includes the escaped parameter characters, '?', '&' and '='. @@ -577,7 +577,7 @@ TEST_F(OAuth2Test, OAuthTestFullFlowPostWithParameters) { }; // Deliberately fail the HMAC validation check. - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); EXPECT_CALL(*oauth_client_, asyncGetAccessToken("123", TEST_CLIENT_ID, "asdf_client_secret_fdsa", @@ -629,7 +629,7 @@ TEST_F(OAuth2Test, OAuthBearerTokenFlowFromHeader) { }; // Fail the validation to trigger the OAuth flow. - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, @@ -655,7 +655,7 @@ TEST_F(OAuth2Test, OAuthBearerTokenFlowFromQueryParameters) { }; // Fail the validation to trigger the OAuth flow. - EXPECT_CALL(*validator_, setParams(_, _)).Times(1); + EXPECT_CALL(*validator_, setParams(_, _)); EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, diff --git a/test/extensions/filters/http/ratelimit/ratelimit_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_test.cc index 4eff42850721..a14a2c4463d6 100644 --- a/test/extensions/filters/http/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/http/ratelimit/ratelimit_test.cc @@ -167,8 +167,8 @@ TEST_F(HttpRateLimitFilterTest, NoApplicableRateLimit) { TEST_F(HttpRateLimitFilterTest, NoDescriptor) { SetUpTest(filter_config_); - EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)).Times(1); - EXPECT_CALL(vh_rate_limit_, populateDescriptors(_, _, _, _, _, _)).Times(1); + EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)); + EXPECT_CALL(vh_rate_limit_, populateDescriptors(_, _, _, _, _, _)); EXPECT_CALL(*client_, limit(_, _, _, _, _)).Times(0); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); @@ -199,15 +199,13 @@ TEST_F(HttpRateLimitFilterTest, OkResponse) { SetUpTest(filter_config_); InSequence s; - EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)) - .Times(1); + EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)); EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_)); EXPECT_CALL(filter_callbacks_.route_->route_entry_.virtual_host_.rate_limit_policy_, - getApplicableRateLimit(0)) - .Times(1); + getApplicableRateLimit(0)); EXPECT_CALL(*client_, limit(_, "foo", testing::ContainerEq(std::vector{ @@ -246,15 +244,13 @@ TEST_F(HttpRateLimitFilterTest, OkResponseWithHeaders) { SetUpTest(filter_config_); InSequence s; - EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)) - .Times(1); + EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)); EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_)); EXPECT_CALL(filter_callbacks_.route_->route_entry_.virtual_host_.rate_limit_policy_, - getApplicableRateLimit(0)) - .Times(1); + getApplicableRateLimit(0)); EXPECT_CALL(*client_, limit(_, "foo", testing::ContainerEq(std::vector{ @@ -304,15 +300,13 @@ TEST_F(HttpRateLimitFilterTest, OkResponseWithFilterHeaders) { SetUpTest(enable_x_ratelimit_headers_config_); InSequence s; - EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)) - .Times(1); + EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)); EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_)); EXPECT_CALL(filter_callbacks_.route_->route_entry_.virtual_host_.rate_limit_policy_, - getApplicableRateLimit(0)) - .Times(1); + getApplicableRateLimit(0)); EXPECT_CALL(*client_, limit(_, "foo", testing::ContainerEq(std::vector{ @@ -830,15 +824,13 @@ TEST_F(HttpRateLimitFilterTest, InternalRequestType) { Http::TestRequestHeaderMapImpl request_headers{{"x-envoy-internal", "true"}}; InSequence s; - EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)) - .Times(1); + EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)); EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_)); EXPECT_CALL(filter_callbacks_.route_->route_entry_.virtual_host_.rate_limit_policy_, - getApplicableRateLimit(0)) - .Times(1); + getApplicableRateLimit(0)); EXPECT_CALL(*client_, limit(_, "foo", testing::ContainerEq(std::vector{ @@ -876,15 +868,13 @@ TEST_F(HttpRateLimitFilterTest, ExternalRequestType) { Http::TestRequestHeaderMapImpl request_headers{{"x-envoy-internal", "false"}}; InSequence s; - EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)) - .Times(1); + EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)); EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_)); EXPECT_CALL(filter_callbacks_.route_->route_entry_.virtual_host_.rate_limit_policy_, - getApplicableRateLimit(0)) - .Times(1); + getApplicableRateLimit(0)); EXPECT_CALL(*client_, limit(_, "foo", testing::ContainerEq(std::vector{ @@ -1037,8 +1027,7 @@ TEST_F(HttpRateLimitFilterTest, OverrideVHRateLimitOptionWithoutRouteRateLimit) .WillOnce(Return(true)); EXPECT_CALL(filter_callbacks_.route_->route_entry_.virtual_host_.rate_limit_policy_, - getApplicableRateLimit(0)) - .Times(1); + getApplicableRateLimit(0)); EXPECT_CALL(vh_rate_limit_, populateDescriptors(_, _, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_)); @@ -1086,8 +1075,7 @@ TEST_F(HttpRateLimitFilterTest, IncludeVHRateLimitOptionWithOnlyVHRateLimitSet) .WillOnce(Return(&per_route_config_)); EXPECT_CALL(filter_callbacks_.route_->route_entry_.virtual_host_.rate_limit_policy_, - getApplicableRateLimit(0)) - .Times(1); + getApplicableRateLimit(0)); EXPECT_CALL(vh_rate_limit_, populateDescriptors(_, _, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_two_)); @@ -1137,8 +1125,7 @@ TEST_F(HttpRateLimitFilterTest, IncludeVHRateLimitOptionWithRouteAndVHRateLimitS .WillOnce(Return(&per_route_config_)); EXPECT_CALL(filter_callbacks_.route_->route_entry_.virtual_host_.rate_limit_policy_, - getApplicableRateLimit(0)) - .Times(1); + getApplicableRateLimit(0)); EXPECT_CALL(vh_rate_limit_, populateDescriptors(_, _, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_two_)); diff --git a/test/extensions/filters/http/router/config_test.cc b/test/extensions/filters/http/router/config_test.cc index 1808920fd610..a8d03d27e5d6 100644 --- a/test/extensions/filters/http/router/config_test.cc +++ b/test/extensions/filters/http/router/config_test.cc @@ -79,7 +79,7 @@ TEST(RouterFilterConfigTest, RouterV2Filter) { RouterFilterConfig factory; Http::FilterFactoryCb cb = factory.createFilterFactoryFromProto(router_config, "stats.", context); Http::MockFilterChainFactoryCallbacks filter_callback; - EXPECT_CALL(filter_callback, addStreamDecoderFilter(_)).Times(1); + EXPECT_CALL(filter_callback, addStreamDecoderFilter(_)); cb(filter_callback); } @@ -89,7 +89,7 @@ TEST(RouterFilterConfigTest, RouterFilterWithEmptyProtoConfig) { Http::FilterFactoryCb cb = factory.createFilterFactoryFromProto(*factory.createEmptyConfigProto(), "stats.", context); Http::MockFilterChainFactoryCallbacks filter_callback; - EXPECT_CALL(filter_callback, addStreamDecoderFilter(_)).Times(1); + EXPECT_CALL(filter_callback, addStreamDecoderFilter(_)); cb(filter_callback); } diff --git a/test/extensions/filters/http/wasm/wasm_filter_test.cc b/test/extensions/filters/http/wasm/wasm_filter_test.cc index 06bce097b0a0..8e71c7f6f847 100644 --- a/test/extensions/filters/http/wasm/wasm_filter_test.cc +++ b/test/extensions/filters/http/wasm/wasm_filter_test.cc @@ -367,17 +367,16 @@ TEST_P(WasmHttpFilterTest, BodyRequestBufferBody) { Buffer::OwnedImpl data1("hello"); bufferedBody.add(data1); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello")))).Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello")))); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter().decodeData(data1, false)); Buffer::OwnedImpl data2(" again "); bufferedBody.add(data2); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello again ")))) - .Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello again ")))); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter().decodeData(data2, false)); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello again hello")))) - .Times(1); + EXPECT_CALL(filter(), + log_(spdlog::level::err, Eq(absl::string_view("onBody hello again hello")))); Buffer::OwnedImpl data3("hello"); bufferedBody.add(data3); EXPECT_EQ(Http::FilterDataStatus::Continue, filter().decodeData(data3, true)); @@ -452,33 +451,29 @@ TEST_P(WasmHttpFilterTest, BodyRequestBufferThenStreamBody) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter().encodeHeaders(response_headers, false)); Buffer::OwnedImpl data1("hello"); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello")))).Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello")))); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter().decodeData(data1, false)); bufferedBody.add(data1); Buffer::OwnedImpl data2(", there, "); bufferedBody.add(data2); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello, there, ")))) - .Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello, there, ")))); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter().decodeData(data2, false)); // Previous callbacks returned "Buffer" so we have buffered so far Buffer::OwnedImpl data3("world!"); bufferedBody.add(data3); EXPECT_CALL(filter(), - log_(spdlog::level::err, Eq(absl::string_view("onBody hello, there, world!")))) - .Times(1); + log_(spdlog::level::err, Eq(absl::string_view("onBody hello, there, world!")))); EXPECT_EQ(Http::FilterDataStatus::Continue, filter().decodeData(data3, false)); // Last callback returned "continue" so we just see individual chunks. Buffer::OwnedImpl data4("So it's "); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody So it's ")))) - .Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody So it's ")))); EXPECT_EQ(Http::FilterDataStatus::Continue, filter().decodeData(data4, false)); Buffer::OwnedImpl data5("goodbye, then!"); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody goodbye, then!")))) - .Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody goodbye, then!")))); EXPECT_EQ(Http::FilterDataStatus::Continue, filter().decodeData(data5, true)); filter().onDestroy(); @@ -501,33 +496,29 @@ TEST_P(WasmHttpFilterTest, BodyResponseBufferThenStreamBody) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter().encodeHeaders(response_headers, false)); Buffer::OwnedImpl data1("hello"); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello")))).Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello")))); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter().encodeData(data1, false)); bufferedBody.add(data1); Buffer::OwnedImpl data2(", there, "); bufferedBody.add(data2); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello, there, ")))) - .Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody hello, there, ")))); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter().encodeData(data2, false)); // Previous callbacks returned "Buffer" so we have buffered so far Buffer::OwnedImpl data3("world!"); bufferedBody.add(data3); EXPECT_CALL(filter(), - log_(spdlog::level::err, Eq(absl::string_view("onBody hello, there, world!")))) - .Times(1); + log_(spdlog::level::err, Eq(absl::string_view("onBody hello, there, world!")))); EXPECT_EQ(Http::FilterDataStatus::Continue, filter().encodeData(data3, false)); // Last callback returned "continue" so we just see individual chunks. Buffer::OwnedImpl data4("So it's "); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody So it's ")))) - .Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody So it's ")))); EXPECT_EQ(Http::FilterDataStatus::Continue, filter().encodeData(data4, false)); Buffer::OwnedImpl data5("goodbye, then!"); - EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody goodbye, then!")))) - .Times(1); + EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("onBody goodbye, then!")))); EXPECT_EQ(Http::FilterDataStatus::Continue, filter().encodeData(data5, true)); filter().onDestroy(); diff --git a/test/extensions/filters/listener/http_inspector/http_inspector_config_test.cc b/test/extensions/filters/listener/http_inspector/http_inspector_config_test.cc index 4fba684513d2..97e76d527d95 100644 --- a/test/extensions/filters/listener/http_inspector/http_inspector_config_test.cc +++ b/test/extensions/filters/listener/http_inspector/http_inspector_config_test.cc @@ -29,7 +29,7 @@ TEST(HttpInspectorConfigFactoryTest, TestCreateFactory) { TestUtility::loadFromYaml(yaml, *proto_config); Server::Configuration::MockListenerFactoryContext context; - EXPECT_CALL(context, scope()).Times(1); + EXPECT_CALL(context, scope()); Network::ListenerFilterFactoryCb cb = factory->createListenerFilterFactoryFromProto(*proto_config, nullptr, context); diff --git a/test/extensions/filters/listener/http_inspector/http_inspector_test.cc b/test/extensions/filters/listener/http_inspector/http_inspector_test.cc index afba3ed07191..d0216ef9a71b 100644 --- a/test/extensions/filters/listener/http_inspector/http_inspector_test.cc +++ b/test/extensions/filters/listener/http_inspector/http_inspector_test.cc @@ -86,7 +86,7 @@ TEST_F(HttpInspectorTest, InlineReadIoError) { })); EXPECT_CALL(dispatcher_, createFileEvent_(_, _, _, _)).Times(0); EXPECT_CALL(socket_, setRequestedApplicationProtocols(_)).Times(0); - EXPECT_CALL(socket_, close()).Times(1); + EXPECT_CALL(socket_, close()); auto accepted = filter_->onAccept(cb_); EXPECT_EQ(accepted, Network::FilterStatus::StopIteration); // It's arguable if io error should bump the not_found counter diff --git a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc index e51ebd62fbde..56a2e6081276 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc @@ -1460,8 +1460,8 @@ TEST(ProxyProtocolConfigFactoryTest, TestCreateFactory) { TestUtility::loadFromYaml(yaml, *proto_config); Server::Configuration::MockListenerFactoryContext context; - EXPECT_CALL(context, scope()).Times(1); - EXPECT_CALL(context, messageValidationVisitor()).Times(1); + EXPECT_CALL(context, scope()); + EXPECT_CALL(context, messageValidationVisitor()); Network::ListenerFilterFactoryCb cb = factory->createListenerFilterFactoryFromProto(*proto_config, nullptr, context); diff --git a/test/extensions/filters/network/dubbo_proxy/conn_manager_test.cc b/test/extensions/filters/network/dubbo_proxy/conn_manager_test.cc index 54951ea41c31..40f26d0fe804 100644 --- a/test/extensions/filters/network/dubbo_proxy/conn_manager_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/conn_manager_test.cc @@ -446,7 +446,7 @@ TEST_F(ConnectionManagerTest, OnDataHandlesProtocolErrorOnWrite) { callbacks->startUpstreamResponse(); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_NE(DubboFilters::UpstreamResponseStatus::Complete, callbacks->upstreamData(write_buffer_)); EXPECT_EQ(1U, store_.counter("test.response_decoding_error").value()); @@ -506,7 +506,7 @@ TEST_F(ConnectionManagerTest, OnWriteHandlesResponse) { EXPECT_EQ(callbacks->connection(), &(filter_callbacks_.connection_)); EXPECT_GE(callbacks->streamId(), 0); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(DubboFilters::UpstreamResponseStatus::Complete, callbacks->upstreamData(write_buffer_)); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -540,7 +540,7 @@ TEST_F(ConnectionManagerTest, HandlesResponseContainExceptionInfo) { callbacks->startUpstreamResponse(); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(DubboFilters::UpstreamResponseStatus::Complete, callbacks->upstreamData(write_buffer_)); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -574,7 +574,7 @@ TEST_F(ConnectionManagerTest, HandlesResponseError) { callbacks->startUpstreamResponse(); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(DubboFilters::UpstreamResponseStatus::Complete, callbacks->upstreamData(write_buffer_)); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -605,7 +605,7 @@ TEST_F(ConnectionManagerTest, OnWriteHandlesResponseException) { callbacks->startUpstreamResponse(); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(DubboFilters::UpstreamResponseStatus::Reset, callbacks->upstreamData(write_buffer_)); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -759,7 +759,7 @@ TEST_F(ConnectionManagerTest, OnEvent) { writePartialHessianRequestMessage(buffer_, false, false, 1, true); EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); conn_manager_->onEvent(Network::ConnectionEvent::RemoteClose); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -772,7 +772,7 @@ TEST_F(ConnectionManagerTest, OnEvent) { writePartialHessianRequestMessage(buffer_, false, false, 1, true); EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -787,7 +787,7 @@ TEST_F(ConnectionManagerTest, OnEvent) { writeHessianRequestMessage(buffer_, false, false, 1); EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); conn_manager_->onEvent(Network::ConnectionEvent::RemoteClose); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -802,7 +802,7 @@ TEST_F(ConnectionManagerTest, OnEvent) { writeHessianRequestMessage(buffer_, false, false, 1); EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -867,7 +867,7 @@ TEST_F(ConnectionManagerTest, OnDataWithFilterSendsLocalReply) { .WillOnce(Invoke([&](Buffer::Instance& buffer, bool) -> void { EXPECT_EQ(fake_response, buffer.toString()); })); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); EXPECT_EQ(SerializationType::Hessian2, callbacks->serializationType()); EXPECT_EQ(ProtocolType::Dubbo, callbacks->protocolType()); @@ -912,7 +912,7 @@ TEST_F(ConnectionManagerTest, OnDataWithFilterSendsLocalErrorReply) { .WillOnce(Invoke([&](Buffer::Instance& buffer, bool) -> void { EXPECT_EQ(fake_response, buffer.toString()); })); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); @@ -935,9 +935,8 @@ TEST_F(ConnectionManagerTest, TwoWayRequestWithEndStream) { return FilterStatus::StopIteration; })); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)) - .Times(1); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(conn_manager_->onData(buffer_, true), Network::FilterStatus::StopIteration); EXPECT_EQ(1U, store_.counter("test.cx_destroy_remote_with_active_rq").value()); } @@ -954,9 +953,8 @@ TEST_F(ConnectionManagerTest, OneWayRequestWithEndStream) { .WillOnce(Invoke([&](MessageMetadataSharedPtr, ContextSharedPtr) -> FilterStatus { return FilterStatus::StopIteration; })); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)) - .Times(1); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(conn_manager_->onData(buffer_, true), Network::FilterStatus::StopIteration); EXPECT_EQ(1U, store_.counter("test.cx_destroy_remote_with_active_rq").value()); } @@ -1017,8 +1015,7 @@ TEST_F(ConnectionManagerTest, SendsLocalReplyWithCloseConnection) { buffer.add(fake_response); return DubboFilters::DirectResponse::ResponseType::ErrorReply; })); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)) - .Times(1); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)); MessageMetadata metadata; conn_manager_->sendLocalReply(metadata, direct_response, true); @@ -1041,9 +1038,8 @@ TEST_F(ConnectionManagerTest, ContinueDecodingWithHalfClose) { .WillOnce(Invoke([&](MessageMetadataSharedPtr, ContextSharedPtr) -> FilterStatus { return FilterStatus::StopIteration; })); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)) - .Times(1); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(conn_manager_->onData(buffer_, true), Network::FilterStatus::StopIteration); EXPECT_EQ(1U, store_.counter("test.cx_destroy_remote_with_active_rq").value()); @@ -1106,7 +1102,7 @@ TEST_F(ConnectionManagerTest, ResetStream) { EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); callbacks->resetStream(); } @@ -1289,7 +1285,7 @@ TEST_F(ConnectionManagerTest, SendLocalReplyInMessageDecoded) { })); // The sendLocalReply is called, the ActiveMessage object should be destroyed. - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); writeHessianRequestMessage(buffer_, false, false, 1); @@ -1316,9 +1312,9 @@ TEST_F(ConnectionManagerTest, HandleResponseWithEncoderFilter) { EXPECT_CALL(*decoder_filter, setDecoderFilterCallbacks(_)) .WillOnce(Invoke([&](DubboFilters::DecoderFilterCallbacks& cb) -> void { callbacks = &cb; })); - EXPECT_CALL(*encoder_filter, setEncoderFilterCallbacks(_)).Times(1); + EXPECT_CALL(*encoder_filter, setEncoderFilterCallbacks(_)); - EXPECT_CALL(*decoder_filter, onDestroy()).Times(1); + EXPECT_CALL(*decoder_filter, onDestroy()); EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); EXPECT_EQ(1U, store_.counter("test.request").value()); @@ -1341,9 +1337,9 @@ TEST_F(ConnectionManagerTest, HandleResponseWithEncoderFilter) { return FilterStatus::Continue; })); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(DubboFilters::UpstreamResponseStatus::Complete, callbacks->upstreamData(write_buffer_)); - EXPECT_CALL(*encoder_filter, onDestroy()).Times(1); + EXPECT_CALL(*encoder_filter, onDestroy()); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); EXPECT_EQ(1U, store_.counter("test.response").value()); @@ -1368,7 +1364,7 @@ TEST_F(ConnectionManagerTest, HandleResponseWithCodecFilter) { return FilterStatus::Continue; })); - EXPECT_CALL(*mock_codec_filter, setEncoderFilterCallbacks(_)).Times(1); + EXPECT_CALL(*mock_codec_filter, setEncoderFilterCallbacks(_)); EXPECT_EQ(conn_manager_->onData(buffer_, false), Network::FilterStatus::StopIteration); EXPECT_EQ(1U, store_.counter("test.request").value()); @@ -1391,9 +1387,9 @@ TEST_F(ConnectionManagerTest, HandleResponseWithCodecFilter) { return FilterStatus::Continue; })); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); EXPECT_EQ(DubboFilters::UpstreamResponseStatus::Complete, callbacks->upstreamData(write_buffer_)); - EXPECT_CALL(*mock_codec_filter, onDestroy()).Times(1); + EXPECT_CALL(*mock_codec_filter, onDestroy()); filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); diff --git a/test/extensions/filters/network/dubbo_proxy/decoder_test.cc b/test/extensions/filters/network/dubbo_proxy/decoder_test.cc index bcc3997772cd..c18cb691c9eb 100644 --- a/test/extensions/filters/network/dubbo_proxy/decoder_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/decoder_test.cc @@ -66,7 +66,7 @@ class DubboDecoderTest : public testing::Test { }; TEST_F(DubboDecoderStateMachineTest, EmptyData) { - EXPECT_CALL(protocol_, decodeHeader(_, _)).Times(1); + EXPECT_CALL(protocol_, decodeHeader(_, _)); EXPECT_CALL(delegate_, newStream(_, _)).Times(0); EXPECT_CALL(delegate_, onHeartbeat(_)).Times(0); @@ -93,7 +93,7 @@ TEST_F(DubboDecoderStateMachineTest, RequestMessageCallbacks) { EXPECT_CALL(delegate_, onHeartbeat(_)).Times(0); EXPECT_CALL(protocol_, decodeData(_, _, _)).WillOnce(Return(true)); - EXPECT_CALL(handler_, onStreamDecoded(_, _)).Times(1); + EXPECT_CALL(handler_, onStreamDecoded(_, _)); DecoderStateMachine dsm(protocol_, delegate_); Buffer::OwnedImpl buffer; @@ -108,7 +108,7 @@ TEST_F(DubboDecoderStateMachineTest, ResponseMessageCallbacks) { EXPECT_CALL(delegate_, onHeartbeat(_)).Times(0); EXPECT_CALL(protocol_, decodeData(_, _, _)).WillOnce(Return(true)); - EXPECT_CALL(handler_, onStreamDecoded(_, _)).Times(1); + EXPECT_CALL(handler_, onStreamDecoded(_, _)); DecoderStateMachine dsm(protocol_, delegate_); Buffer::OwnedImpl buffer; @@ -121,7 +121,7 @@ TEST_F(DubboDecoderStateMachineTest, SerializeRpcInvocationException) { initHandler(); initProtocolDecoder(MessageType::Request, 0); - EXPECT_CALL(delegate_, newStream(_, _)).Times(1); + EXPECT_CALL(delegate_, newStream(_, _)); EXPECT_CALL(delegate_, onHeartbeat(_)).Times(0); EXPECT_CALL(handler_, onStreamDecoded(_, _)).Times(0); @@ -141,7 +141,7 @@ TEST_F(DubboDecoderStateMachineTest, SerializeRpcResultException) { initHandler(); initProtocolDecoder(MessageType::Response, 0); - EXPECT_CALL(delegate_, newStream(_, _)).Times(1); + EXPECT_CALL(delegate_, newStream(_, _)); EXPECT_CALL(delegate_, onHeartbeat(_)).Times(0); EXPECT_CALL(handler_, onStreamDecoded(_, _)).Times(0); @@ -235,7 +235,7 @@ TEST_F(DubboDecoderTest, DecodeResponseMessage) { EXPECT_CALL(protocol_, decodeData(_, _, _)).WillOnce(Return(true)); EXPECT_CALL(response_callbacks_, newStream()).WillOnce(ReturnRef(handler_)); EXPECT_CALL(response_callbacks_, onHeartbeat(_)).Times(0); - EXPECT_CALL(handler_, onStreamDecoded(_, _)).Times(1); + EXPECT_CALL(handler_, onStreamDecoded(_, _)); ResponseDecoder decoder(protocol_, response_callbacks_); @@ -258,7 +258,7 @@ TEST_F(DubboDecoderTest, DecodeResponseMessage) { EXPECT_CALL(protocol_, decodeData(_, _, _)).WillOnce(Return(true)); EXPECT_CALL(response_callbacks_, newStream()).WillOnce(ReturnRef(handler_)); EXPECT_CALL(response_callbacks_, onHeartbeat(_)).Times(0); - EXPECT_CALL(handler_, onStreamDecoded(_, _)).Times(1); + EXPECT_CALL(handler_, onStreamDecoded(_, _)); buffer_underflow = false; EXPECT_EQ(decoder.onData(buffer, buffer_underflow), FilterStatus::Continue); diff --git a/test/extensions/filters/network/dubbo_proxy/router_test.cc b/test/extensions/filters/network/dubbo_proxy/router_test.cc index 12537e310919..29edf4df53c4 100644 --- a/test/extensions/filters/network/dubbo_proxy/router_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/router_test.cc @@ -331,7 +331,7 @@ TEST_F(DubboRouterTest, PoolConnectionFailureWithOnewayMessage) { EXPECT_CALL(callbacks_, protocolType()).WillOnce(Return(ProtocolType::Dubbo)); EXPECT_CALL(callbacks_, serializationType()).WillOnce(Return(SerializationType::Hessian2)); EXPECT_CALL(callbacks_, sendLocalReply(_, _)).Times(0); - EXPECT_CALL(callbacks_, resetStream()).Times(1); + EXPECT_CALL(callbacks_, resetStream()); EXPECT_EQ(FilterStatus::StopIteration, router_->onMessageDecoded(metadata_, message_context_)); context_.cluster_manager_.tcp_conn_pool_.poolFailure( @@ -443,8 +443,8 @@ TEST_F(DubboRouterTest, DecoderFilterCallbacks) { initializeMetadata(MessageType::Request); EXPECT_CALL(upstream_connection_, write(_, false)); - EXPECT_CALL(callbacks_, startUpstreamResponse()).Times(1); - EXPECT_CALL(callbacks_, upstreamData(_)).Times(1); + EXPECT_CALL(callbacks_, startUpstreamResponse()); + EXPECT_CALL(callbacks_, upstreamData(_)); startRequest(MessageType::Request); connectUpstream(); @@ -460,7 +460,7 @@ TEST_F(DubboRouterTest, UpstreamDataReset) { initializeRouter(); initializeMetadata(MessageType::Request); - EXPECT_CALL(callbacks_, startUpstreamResponse()).Times(1); + EXPECT_CALL(callbacks_, startUpstreamResponse()); EXPECT_CALL(callbacks_, upstreamData(_)) .WillOnce(Return(DubboFilters::UpstreamResponseStatus::Reset)); EXPECT_CALL(upstream_connection_, close(Network::ConnectionCloseType::NoFlush)); @@ -506,7 +506,7 @@ TEST_F(DubboRouterTest, LocalClosedWhileResponseComplete) { initializeRouter(); initializeMetadata(MessageType::Request); - EXPECT_CALL(callbacks_, startUpstreamResponse()).Times(1); + EXPECT_CALL(callbacks_, startUpstreamResponse()); EXPECT_CALL(callbacks_, upstreamData(_)) .WillOnce(Return(DubboFilters::UpstreamResponseStatus::Complete)); EXPECT_CALL(callbacks_, sendLocalReply(_, _)).Times(0); diff --git a/test/extensions/filters/network/ext_authz/ext_authz_test.cc b/test/extensions/filters/network/ext_authz/ext_authz_test.cc index bea5c4ae9359..813dc8b4159f 100644 --- a/test/extensions/filters/network/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/network/ext_authz/ext_authz_test.cc @@ -266,7 +266,7 @@ TEST_F(ExtAuthzFilterTest, FailClose) { Buffer::OwnedImpl data("hello"); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); - EXPECT_CALL(filter_callbacks_.connection_, close(_)).Times(1); + EXPECT_CALL(filter_callbacks_.connection_, close(_)); EXPECT_CALL(filter_callbacks_, continueReading()).Times(0); request_callbacks_->onComplete(makeAuthzResponse(Filters::Common::ExtAuthz::CheckStatus::Error)); diff --git a/test/extensions/filters/network/http_connection_manager/config_test.cc b/test/extensions/filters/network/http_connection_manager/config_test.cc index 83c65187572f..192b7de90908 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_test.cc @@ -2164,13 +2164,13 @@ TEST_F(FilterChainTest, CreateCustomUpgradeFilterChain) { { Http::MockFilterChainFactoryCallbacks callbacks; - EXPECT_CALL(callbacks, addStreamDecoderFilter(_)).Times(1); + EXPECT_CALL(callbacks, addStreamDecoderFilter(_)); EXPECT_TRUE(config.createUpgradeFilterChain("websocket", nullptr, callbacks)); } { Http::MockFilterChainFactoryCallbacks callbacks; - EXPECT_CALL(callbacks, addStreamDecoderFilter(_)).Times(1); + EXPECT_CALL(callbacks, addStreamDecoderFilter(_)); EXPECT_CALL(callbacks, addStreamFilter(_)).Times(2); // Buffer EXPECT_TRUE(config.createUpgradeFilterChain("Foo", nullptr, callbacks)); } diff --git a/test/extensions/filters/network/postgres_proxy/postgres_decoder_test.cc b/test/extensions/filters/network/postgres_proxy/postgres_decoder_test.cc index 9f234951f813..b2c81d77de1f 100644 --- a/test/extensions/filters/network/postgres_proxy/postgres_decoder_test.cc +++ b/test/extensions/filters/network/postgres_proxy/postgres_decoder_test.cc @@ -232,14 +232,14 @@ TEST_F(PostgresProxyDecoderTest, TwoMessagesInOneBuffer) { TEST_F(PostgresProxyDecoderTest, Unknown) { // Create invalid message. The first byte is invalid "=" // Message must be at least 5 bytes to be parsed. - EXPECT_CALL(callbacks_, incMessagesUnknown()).Times(1); + EXPECT_CALL(callbacks_, incMessagesUnknown()); createPostgresMsg(data_, "=", "some not important string which will be ignored anyways"); decoder_->onData(data_, true); } // Test if each frontend command calls incMessagesFrontend() method. TEST_P(PostgresProxyFrontendDecoderTest, FrontendInc) { - EXPECT_CALL(callbacks_, incMessagesFrontend()).Times(1); + EXPECT_CALL(callbacks_, incMessagesFrontend()); createPostgresMsg(data_, GetParam(), "SELECT 1;"); decoder_->onData(data_, true); @@ -262,7 +262,7 @@ TEST_F(PostgresProxyFrontendDecoderTest, TerminateMessage) { // Now set the decoder to be in_transaction state. decoder_->getSession().setInTransaction(true); - EXPECT_CALL(callbacks_, incTransactionsRollback()).Times(1); + EXPECT_CALL(callbacks_, incTransactionsRollback()); createPostgresMsg(data_, "X"); decoder_->onData(data_, true); ASSERT_FALSE(decoder_->getSession().inTransaction()); @@ -270,7 +270,7 @@ TEST_F(PostgresProxyFrontendDecoderTest, TerminateMessage) { // Query message should invoke filter's callback message TEST_F(PostgresProxyFrontendDecoderTest, QueryMessage) { - EXPECT_CALL(callbacks_, processQuery).Times(1); + EXPECT_CALL(callbacks_, processQuery); createPostgresMsg(data_, "Q", "SELECT * FROM whatever;"); decoder_->onData(data_, true); } @@ -307,7 +307,7 @@ TEST_F(PostgresProxyFrontendDecoderTest, ParseMessage) { // Test if each backend command calls incMessagesBackend()) method. TEST_P(PostgresProxyBackendDecoderTest, BackendInc) { - EXPECT_CALL(callbacks_, incMessagesBackend()).Times(1); + EXPECT_CALL(callbacks_, incMessagesBackend()); createPostgresMsg(data_, GetParam(), "Some not important message"); decoder_->onData(data_, false); } diff --git a/test/extensions/filters/network/rocketmq_proxy/conn_manager_test.cc b/test/extensions/filters/network/rocketmq_proxy/conn_manager_test.cc index db12975a385a..272706d89f95 100644 --- a/test/extensions/filters/network/rocketmq_proxy/conn_manager_test.cc +++ b/test/extensions/filters/network/rocketmq_proxy/conn_manager_test.cc @@ -233,7 +233,7 @@ TEST_F(RocketmqConnectionManagerTest, OnHeartbeatWithDownstreamConnecitonClosed) BufferUtility::fillRequestBuffer(buffer_, RequestCode::HeartBeat); NiceMock connection; - EXPECT_CALL(connection, state()).Times(1).WillOnce(Invoke([&]() -> Network::Connection::State { + EXPECT_CALL(connection, state()).WillOnce(Invoke([&]() -> Network::Connection::State { return Network::Connection::State::Closed; })); EXPECT_CALL(filter_callbacks_, connection()).WillRepeatedly(Invoke([&]() -> Network::Connection& { diff --git a/test/extensions/filters/network/thrift_proxy/decoder_test.cc b/test/extensions/filters/network/thrift_proxy/decoder_test.cc index 73c8ac0b85dc..87f505b06e2c 100644 --- a/test/extensions/filters/network/thrift_proxy/decoder_test.cc +++ b/test/extensions/filters/network/thrift_proxy/decoder_test.cc @@ -870,7 +870,7 @@ TEST(DecoderTest, OnData) { EXPECT_EQ(100U, metadata->sequenceId()); return FilterStatus::Continue; })); - EXPECT_CALL(callbacks, passthroughEnabled()).Times(1).WillRepeatedly(Return(false)); + EXPECT_CALL(callbacks, passthroughEnabled()).WillOnce(Return(false)); EXPECT_CALL(proto, readStructBegin(Ref(buffer), _)).WillOnce(Return(true)); EXPECT_CALL(handler, structBegin(absl::string_view())).WillOnce(Return(FilterStatus::Continue)); @@ -938,7 +938,7 @@ TEST(DecoderTest, OnDataWithProtocolHint) { EXPECT_EQ(100U, metadata->sequenceId()); return FilterStatus::Continue; })); - EXPECT_CALL(callbacks, passthroughEnabled()).Times(1); + EXPECT_CALL(callbacks, passthroughEnabled()); EXPECT_CALL(proto, readStructBegin(Ref(buffer), _)).WillOnce(Return(true)); EXPECT_CALL(handler, structBegin(absl::string_view())).WillOnce(Return(FilterStatus::Continue)); @@ -1181,7 +1181,7 @@ TEST(DecoderTest, OnDataHandlesStopIterationAndResumes) { EXPECT_EQ(100U, metadata->sequenceId()); return FilterStatus::StopIteration; })); - EXPECT_CALL(callbacks, passthroughEnabled()).Times(1).WillRepeatedly(Return(false)); + EXPECT_CALL(callbacks, passthroughEnabled()).WillOnce(Return(false)); EXPECT_EQ(FilterStatus::StopIteration, decoder.onData(buffer, underflow)); EXPECT_FALSE(underflow); diff --git a/test/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit_test.cc b/test/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit_test.cc index 29a863e1f68b..131bb6513f58 100644 --- a/test/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit_test.cc @@ -185,7 +185,7 @@ TEST_F(ThriftRateLimitFilterTest, NoApplicableRateLimit) { TEST_F(ThriftRateLimitFilterTest, NoDescriptor) { setupTest(filter_config_); - EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _)).Times(1); + EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _)); EXPECT_CALL(*client_, limit(_, _, _, _, _)).Times(0); EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->messageBegin(request_metadata_)); @@ -204,8 +204,7 @@ TEST_F(ThriftRateLimitFilterTest, OkResponse) { setupTest(filter_config_); InSequence s; - EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)) - .Times(1); + EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)); EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _)) .WillOnce(SetArgReferee<1>(descriptor_)); diff --git a/test/extensions/filters/udp/dns_filter/dns_filter_test.cc b/test/extensions/filters/udp/dns_filter/dns_filter_test.cc index dbb385a2eeb4..c4e0ca81177a 100644 --- a/test/extensions/filters/udp/dns_filter/dns_filter_test.cc +++ b/test/extensions/filters/udp/dns_filter/dns_filter_test.cc @@ -639,7 +639,7 @@ TEST_F(DnsFilterTest, ExternalResolutionReturnSingleAddress) { InSequence s; auto timeout_timer = new NiceMock(&dispatcher_); - EXPECT_CALL(*timeout_timer, enableTimer(_, _)).Times(1); + EXPECT_CALL(*timeout_timer, enableTimer(_, _)); const std::string expected_address("130.207.244.251"); const std::string domain("www.foobaz.com"); @@ -691,7 +691,7 @@ TEST_F(DnsFilterTest, ExternalResolutionIpv6SingleAddress) { InSequence s; auto timeout_timer = new NiceMock(&dispatcher_); - EXPECT_CALL(*timeout_timer, enableTimer(_, _)).Times(1); + EXPECT_CALL(*timeout_timer, enableTimer(_, _)); const std::string expected_address("2a04:4e42:d::323"); const std::string domain("www.foobaz.com"); @@ -710,7 +710,7 @@ TEST_F(DnsFilterTest, ExternalResolutionIpv6SingleAddress) { // Send a query to for a name not in our configuration sendQueryFromClient("10.0.0.1:1000", query); - EXPECT_CALL(*timeout_timer, disableTimer()).Times(1); + EXPECT_CALL(*timeout_timer, disableTimer()); // Execute resolve callback resolve_cb(Network::DnsResolver::ResolutionStatus::Success, @@ -744,7 +744,7 @@ TEST_F(DnsFilterTest, ExternalResolutionReturnMultipleAddresses) { InSequence s; auto timeout_timer = new NiceMock(&dispatcher_); - EXPECT_CALL(*timeout_timer, enableTimer(_, _)).Times(1); + EXPECT_CALL(*timeout_timer, enableTimer(_, _)); const std::list expected_address{"130.207.244.251", "130.207.244.252", "130.207.244.253", "130.207.244.254"}; @@ -763,7 +763,7 @@ TEST_F(DnsFilterTest, ExternalResolutionReturnMultipleAddresses) { // Send a query to for a name not in our configuration sendQueryFromClient("10.0.0.1:1000", query); - EXPECT_CALL(*timeout_timer, disableTimer()).Times(1); + EXPECT_CALL(*timeout_timer, disableTimer()); // Execute resolve callback resolve_cb(Network::DnsResolver::ResolutionStatus::Success, @@ -798,7 +798,7 @@ TEST_F(DnsFilterTest, ExternalResolutionReturnNoAddresses) { InSequence s; auto timeout_timer = new NiceMock(&dispatcher_); - EXPECT_CALL(*timeout_timer, enableTimer(_, _)).Times(1); + EXPECT_CALL(*timeout_timer, enableTimer(_, _)); const std::string domain("www.foobaz.com"); setup(forward_query_on_config); @@ -815,7 +815,7 @@ TEST_F(DnsFilterTest, ExternalResolutionReturnNoAddresses) { // Send a query to for a name not in our configuration sendQueryFromClient("10.0.0.1:1000", query); - EXPECT_CALL(*timeout_timer, disableTimer()).Times(1); + EXPECT_CALL(*timeout_timer, disableTimer()); // Execute resolve callback resolve_cb(Network::DnsResolver::ResolutionStatus::Success, TestUtility::makeDnsResponse({})); @@ -841,7 +841,7 @@ TEST_F(DnsFilterTest, ExternalResolutionTimeout) { InSequence s; auto timeout_timer = new NiceMock(&dispatcher_); - EXPECT_CALL(*timeout_timer, enableTimer(_, _)).Times(1); + EXPECT_CALL(*timeout_timer, enableTimer(_, _)); const std::string domain("www.foobaz.com"); setup(forward_query_on_config); @@ -880,7 +880,7 @@ TEST_F(DnsFilterTest, ExternalResolutionTimeout2) { InSequence s; auto timeout_timer = new NiceMock(&dispatcher_); - EXPECT_CALL(*timeout_timer, enableTimer(_, _)).Times(1); + EXPECT_CALL(*timeout_timer, enableTimer(_, _)); const std::string domain("www.foobaz.com"); setup(forward_query_on_config); diff --git a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc index ced09ffb6a45..2499cd362e0d 100644 --- a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc @@ -174,8 +174,7 @@ TEST(UdpStatsdSinkTest, CheckActualStats) { counter.latch_ = 1; snapshot.counters_.push_back({1, counter}); - EXPECT_CALL(*std::dynamic_pointer_cast>(writer_ptr), writeBuffer(_)) - .Times(1); + EXPECT_CALL(*std::dynamic_pointer_cast>(writer_ptr), writeBuffer(_)); sink.flush(snapshot); EXPECT_EQ(writer_ptr->buffer_writes.size(), 1); EXPECT_EQ(writer_ptr->buffer_writes.at(0), "envoy.test_counter:1|c"); @@ -256,8 +255,7 @@ TEST(UdpStatsdSinkTest, CheckBufferedWritesWithinBufferSize) { snapshot.gauges_.push_back(gauge); // Expect both metrics to be present in single write - EXPECT_CALL(*std::dynamic_pointer_cast>(writer_ptr), writeBuffer(_)) - .Times(1); + EXPECT_CALL(*std::dynamic_pointer_cast>(writer_ptr), writeBuffer(_)); sink.flush(snapshot); EXPECT_EQ(writer_ptr->buffer_writes.size(), 1); EXPECT_EQ(writer_ptr->buffer_writes.at(0), "envoy.test_counter:1|c\nenvoy.test_gauge:1|g"); diff --git a/test/extensions/tracers/skywalking/tracer_test.cc b/test/extensions/tracers/skywalking/tracer_test.cc index c0d9356f1d50..843421740d72 100644 --- a/test/extensions/tracers/skywalking/tracer_test.cc +++ b/test/extensions/tracers/skywalking/tracer_test.cc @@ -164,7 +164,7 @@ TEST_F(TracerTest, TracerTestCreateNewSpanWithNoPropagationHeaders) { EXPECT_EQ(2, second_child_span->spanStore()->spanId()); EXPECT_EQ(0, second_child_span->spanStore()->parentSpanId()); - EXPECT_CALL(*mock_stream_ptr_, sendMessageRaw_(_, _)).Times(1); + EXPECT_CALL(*mock_stream_ptr_, sendMessageRaw_(_, _)); // When the child span ends, the data is not reported immediately, but the end time is set. first_child_span->finishSpan(); diff --git a/test/extensions/transport_sockets/common/passthrough_test.cc b/test/extensions/transport_sockets/common/passthrough_test.cc index 067caab6611e..3f3164a4df84 100644 --- a/test/extensions/transport_sockets/common/passthrough_test.cc +++ b/test/extensions/transport_sockets/common/passthrough_test.cc @@ -29,58 +29,57 @@ class PassthroughTest : public testing::Test { // Test setTransportSocketCallbacks method defers to inner socket TEST_F(PassthroughTest, SetTransportSocketCallbacksDefersToInnerSocket) { auto transport_callbacks = std::make_unique>(); - EXPECT_CALL(*inner_socket_, setTransportSocketCallbacks(Ref(*transport_callbacks))).Times(1); + EXPECT_CALL(*inner_socket_, setTransportSocketCallbacks(Ref(*transport_callbacks))); passthrough_socket_->setTransportSocketCallbacks(*transport_callbacks); } // Test protocol method defers to inner socket TEST_F(PassthroughTest, ProtocolDefersToInnerSocket) { - EXPECT_CALL(*inner_socket_, protocol()).Times(1); + EXPECT_CALL(*inner_socket_, protocol()); passthrough_socket_->protocol(); } // Test failureReason method defers to inner socket TEST_F(PassthroughTest, FailureReasonDefersToInnerSocket) { - EXPECT_CALL(*inner_socket_, failureReason()).Times(1); + EXPECT_CALL(*inner_socket_, failureReason()); passthrough_socket_->failureReason(); } // Test canFlushClose method defers to inner socket TEST_F(PassthroughTest, CanFlushCloseDefersToInnerSocket) { - EXPECT_CALL(*inner_socket_, canFlushClose()).Times(1); + EXPECT_CALL(*inner_socket_, canFlushClose()); passthrough_socket_->canFlushClose(); } // Test closeSocket method defers to inner socket TEST_F(PassthroughTest, CloseSocketDefersToInnerSocket) { - EXPECT_CALL(*inner_socket_, closeSocket(testing::Eq(Network::ConnectionEvent::LocalClose))) - .Times(1); + EXPECT_CALL(*inner_socket_, closeSocket(testing::Eq(Network::ConnectionEvent::LocalClose))); passthrough_socket_->closeSocket(Network::ConnectionEvent::LocalClose); } // Test doRead method defers to inner socket TEST_F(PassthroughTest, DoReadDefersToInnerSocket) { auto buff = Buffer::OwnedImpl("data"); - EXPECT_CALL(*inner_socket_, doRead(BufferEqual(&buff))).Times(1); + EXPECT_CALL(*inner_socket_, doRead(BufferEqual(&buff))); passthrough_socket_->doRead(buff); } // Test doWrite method defers to inner socket TEST_F(PassthroughTest, DoWriteDefersToInnerSocket) { auto buff = Buffer::OwnedImpl("data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&buff), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&buff), false)); passthrough_socket_->doWrite(buff, false); } // Test onConnected method defers to inner socket TEST_F(PassthroughTest, OnConnectedDefersToInnerSocket) { - EXPECT_CALL(*inner_socket_, onConnected()).Times(1); + EXPECT_CALL(*inner_socket_, onConnected()); passthrough_socket_->onConnected(); } // Test ssl method defers to inner socket TEST_F(PassthroughTest, SslDefersToInnerSocket) { - EXPECT_CALL(*inner_socket_, ssl()).Times(1); + EXPECT_CALL(*inner_socket_, ssl()); passthrough_socket_->ssl(); } diff --git a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc index 16f56b4f9489..7a6e2f53419c 100644 --- a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc @@ -74,8 +74,8 @@ TEST_F(ProxyProtocolTest, InjectesHeaderOnlyOnce) { { InSequence s; - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg2), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg2), false)); } proxy_protocol_socket_->doWrite(msg, false); @@ -195,7 +195,7 @@ TEST_F(ProxyProtocolTest, V1IPV4LocalAddressWhenTransportOptionsAreNull) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -219,7 +219,7 @@ TEST_F(ProxyProtocolTest, V1IPV4LocalAddressesWhenHeaderOptionsAreNull) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -243,7 +243,7 @@ TEST_F(ProxyProtocolTest, V1IPV6LocalAddressesWhenHeaderOptionsAreNull) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -275,7 +275,7 @@ TEST_F(ProxyProtocolTest, V1IPV4DownstreamAddresses) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -307,7 +307,7 @@ TEST_F(ProxyProtocolTest, V1IPV6DownstreamAddresses) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -329,7 +329,7 @@ TEST_F(ProxyProtocolTest, V2IPV4LocalCommandWhenTransportOptionsAreNull) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -352,7 +352,7 @@ TEST_F(ProxyProtocolTest, V2IPV4LocalCommandWhenHeaderOptionsAreNull) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -384,7 +384,7 @@ TEST_F(ProxyProtocolTest, V2IPV4DownstreamAddresses) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -416,7 +416,7 @@ TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddresses) { return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); })); auto msg = Buffer::OwnedImpl("some data"); - EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)).Times(1); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); proxy_protocol_socket_->doWrite(msg, false); } @@ -438,7 +438,7 @@ TEST_F(ProxyProtocolTest, OnConnectedCallsInnerOnConnected) { Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080"); initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, socket_options); - EXPECT_CALL(*inner_socket_, onConnected()).Times(1); + EXPECT_CALL(*inner_socket_, onConnected()); proxy_protocol_socket_->onConnected(); } diff --git a/test/extensions/transport_sockets/tls/handshaker_test.cc b/test/extensions/transport_sockets/tls/handshaker_test.cc index 860eca5152fe..8822d778e76b 100644 --- a/test/extensions/transport_sockets/tls/handshaker_test.cc +++ b/test/extensions/transport_sockets/tls/handshaker_test.cc @@ -125,7 +125,7 @@ TEST_F(HandshakerTest, NormalOperation) { ON_CALL(mock_connection, state).WillByDefault(Return(Network::Connection::State::Closed)); NiceMock handshake_callbacks; - EXPECT_CALL(handshake_callbacks, onSuccess).Times(1); + EXPECT_CALL(handshake_callbacks, onSuccess); ON_CALL(handshake_callbacks, connection()).WillByDefault(ReturnRef(mock_connection)); SslHandshakerImpl handshaker(std::move(server_ssl_), 0, &handshake_callbacks); @@ -153,7 +153,7 @@ TEST_F(HandshakerTest, ErrorCbOnAbnormalOperation) { SSL_set_bio(client_ssl_.get(), bio, bio); StrictMock handshake_callbacks; - EXPECT_CALL(handshake_callbacks, onFailure).Times(1); + EXPECT_CALL(handshake_callbacks, onFailure); SslHandshakerImpl handshaker(std::move(server_ssl_), 0, &handshake_callbacks); @@ -222,7 +222,7 @@ TEST_F(HandshakerTest, NormalOperationWithSslHandshakerImplForTest) { ::testing::MockFunction requested_cert_cb; StrictMock handshake_callbacks; - EXPECT_CALL(handshake_callbacks, onSuccess).Times(1); + EXPECT_CALL(handshake_callbacks, onSuccess); SslHandshakerImplForTest handshaker(std::move(server_ssl_), &handshake_callbacks, requested_cert_cb.AsStdFunction()); diff --git a/test/server/admin/runtime_handler_test.cc b/test/server/admin/runtime_handler_test.cc index dfd7fc0bf1f9..1d793d44a3ea 100644 --- a/test/server/admin/runtime_handler_test.cc +++ b/test/server/admin/runtime_handler_test.cc @@ -85,7 +85,7 @@ TEST_P(AdminInstanceTest, RuntimeModify) { overrides["foo"] = "bar"; overrides["x"] = "42"; overrides["nothing"] = ""; - EXPECT_CALL(loader, mergeValues(overrides)).Times(1); + EXPECT_CALL(loader, mergeValues(overrides)); EXPECT_EQ(Http::Code::OK, postCallback("/runtime_modify?foo=bar&x=42¬hing=", header_map, response)); EXPECT_EQ("OK\n", response.toString()); @@ -98,7 +98,7 @@ TEST_P(AdminInstanceTest, RuntimeModifyParamsInBody) { const std::string key = "routing.traffic_shift.foo"; const std::string value = "numerator: 1\ndenominator: TEN_THOUSAND\n"; const absl::node_hash_map overrides = {{key, value}}; - EXPECT_CALL(loader, mergeValues(overrides)).Times(1); + EXPECT_CALL(loader, mergeValues(overrides)); const std::string body = fmt::format("{}={}", key, value); Http::TestResponseHeaderMapImpl header_map; diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index 99f4516b6120..c4a2f8959a21 100644 --- a/test/server/connection_handler_test.cc +++ b/test/server/connection_handler_test.cc @@ -289,7 +289,7 @@ TEST_F(ConnectionHandlerTest, RemoveListenerDuringRebalance) { Network::MockConnectionSocket* connection = new NiceMock(); current_handler->incNumConnections(); #ifndef NDEBUG - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); #endif current_handler->post(Network::ConnectionSocketPtr{connection}); @@ -354,7 +354,7 @@ TEST_F(ConnectionHandlerTest, ListenerConnectionLimitEnforced) { // We expect that listener 2 accepts the connection, so there will be a call to // createServerConnection and active cx should increase, while cx overflow remains the same. - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); listener_callbacks2->onAccept( Network::ConnectionSocketPtr{new NiceMock()}); EXPECT_EQ(0, handler_->numConnections()); @@ -407,7 +407,7 @@ TEST_F(ConnectionHandlerTest, RemoveListener) { handler_->addListener(absl::nullopt, *test_listener); Network::MockConnectionSocket* connection = new NiceMock(); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); listener_callbacks->onAccept(Network::ConnectionSocketPtr{connection}); EXPECT_EQ(0UL, handler_->numConnections()); @@ -508,7 +508,7 @@ TEST_F(ConnectionHandlerTest, SetsTransportSocketConnectTimeout) { .WillOnce(Return(std::chrono::seconds(5))); EXPECT_CALL(*server_connection, setTransportSocketConnectTimeout(std::chrono::milliseconds(5 * 1000))); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); listener_callbacks->onAccept(std::make_unique>()); @@ -526,7 +526,7 @@ TEST_F(ConnectionHandlerTest, DestroyCloseConnections) { handler_->addListener(absl::nullopt, *test_listener); Network::MockConnectionSocket* connection = new NiceMock(); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); listener_callbacks->onAccept(Network::ConnectionSocketPtr{connection}); EXPECT_EQ(0UL, handler_->numConnections()); @@ -551,7 +551,7 @@ TEST_F(ConnectionHandlerTest, CloseDuringFilterChainCreate) { EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true)); EXPECT_CALL(*connection, state()).WillOnce(Return(Network::Connection::State::Closed)); EXPECT_CALL(*connection, addConnectionCallbacks(_)).Times(0); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); Network::MockConnectionSocket* accepted_socket = new NiceMock(); listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket}); EXPECT_EQ(0UL, handler_->numConnections()); @@ -575,7 +575,7 @@ TEST_F(ConnectionHandlerTest, CloseConnectionOnEmptyFilterChain) { EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(false)); EXPECT_CALL(*connection, close(Network::ConnectionCloseType::NoFlush)); EXPECT_CALL(*connection, addConnectionCallbacks(_)).Times(0); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); Network::MockConnectionSocket* accepted_socket = new NiceMock(); listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket}); EXPECT_EQ(0UL, handler_->numConnections()); @@ -703,7 +703,7 @@ TEST_F(ConnectionHandlerTest, FallbackToWildcardListener) { EXPECT_CALL(*listener2, onDestroy()); EXPECT_CALL(*listener1, onDestroy()); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); } TEST_F(ConnectionHandlerTest, WildcardListenerWithOriginalDst) { @@ -746,7 +746,7 @@ TEST_F(ConnectionHandlerTest, WildcardListenerWithOriginalDst) { EXPECT_EQ(1UL, handler_->numConnections()); EXPECT_CALL(*listener1, onDestroy()); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); } TEST_F(ConnectionHandlerTest, WildcardListenerWithNoOriginalDst) { @@ -782,7 +782,7 @@ TEST_F(ConnectionHandlerTest, WildcardListenerWithNoOriginalDst) { EXPECT_EQ(1UL, handler_->numConnections()); EXPECT_CALL(*listener1, onDestroy()); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); } TEST_F(ConnectionHandlerTest, TransportProtocolDefault) { @@ -798,7 +798,7 @@ TEST_F(ConnectionHandlerTest, TransportProtocolDefault) { .WillOnce(Return(absl::string_view(""))); EXPECT_CALL(*accepted_socket, setDetectedTransportProtocol(absl::string_view("raw_buffer"))); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket}); EXPECT_CALL(*listener, onDestroy()); @@ -829,7 +829,7 @@ TEST_F(ConnectionHandlerTest, TransportProtocolCustom) { EXPECT_CALL(*accepted_socket, setDetectedTransportProtocol(dummy)); EXPECT_CALL(*accepted_socket, detectedTransportProtocol()).WillOnce(Return(dummy)); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket}); EXPECT_CALL(*listener, onDestroy()); @@ -867,7 +867,7 @@ TEST_F(ConnectionHandlerTest, ListenerFilterTimeout) { EXPECT_EQ(1UL, downstream_pre_cx_active.value()); EXPECT_CALL(*timeout, disableTimer()); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); timeout->invokeCallback(); EXPECT_CALL(*test_filter, destroy_()); dispatcher_.clearDeferredDeleteList(); @@ -914,7 +914,7 @@ TEST_F(ConnectionHandlerTest, ContinueOnListenerFilterTimeout) { EXPECT_CALL(*test_filter, destroy_()); // Barrier: test_filter must be destructed before findFilterChain EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); EXPECT_CALL(*timeout, disableTimer()); timeout->invokeCallback(); dispatcher_.clearDeferredDeleteList(); @@ -959,7 +959,7 @@ TEST_F(ConnectionHandlerTest, ListenerFilterTimeoutResetOnSuccess) { listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket}); EXPECT_CALL(*test_filter, destroy_()); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); EXPECT_CALL(*timeout, disableTimer()); listener_filter_cb->continueFilterChain(true); @@ -988,7 +988,7 @@ TEST_F(ConnectionHandlerTest, ListenerFilterDisabledTimeout) { .WillOnce(Invoke([&](Network::ListenerFilterCallbacks&) -> Network::FilterStatus { return Network::FilterStatus::StopIteration; })); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); EXPECT_CALL(dispatcher_, createTimer_(_)).Times(0); EXPECT_CALL(*test_filter, destroy_()); Network::MockConnectionSocket* accepted_socket = new NiceMock(); @@ -1022,7 +1022,7 @@ TEST_F(ConnectionHandlerTest, ListenerFilterReportError) { cb.socket().close(); return Network::FilterStatus::StopIteration; })); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); // The last filter won't be invoked EXPECT_CALL(*last_filter, onAccept(_)).Times(0); EXPECT_CALL(*first_filter, destroy_()); @@ -1099,7 +1099,7 @@ TEST_F(ConnectionHandlerTest, TcpListenerInplaceUpdate) { .WillOnce(ReturnRef(*current_handler)); EXPECT_CALL(manager_, findFilterChain(_)).Times(0); EXPECT_CALL(*overridden_filter_chain_manager, findFilterChain(_)).WillOnce(Return(nullptr)); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); EXPECT_CALL(*mock_connection_balancer, unregisterHandler(_)); old_listener_callbacks->onAccept(Network::ConnectionSocketPtr{connection}); EXPECT_EQ(0UL, handler_->numConnections()); @@ -1121,7 +1121,7 @@ TEST_F(ConnectionHandlerTest, TcpListenerRemoveFilterChain) { auto* server_connection = new NiceMock(); EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(server_connection)); EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true)); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); listener_callbacks->onAccept(Network::ConnectionSocketPtr{connection}); @@ -1208,7 +1208,7 @@ TEST_F(ConnectionHandlerTest, ListenerFilterWorks) { EXPECT_CALL(*disabled_listener_filter, destroy_()); EXPECT_CALL(*enabled_filter, destroy_()); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); - EXPECT_CALL(*access_log_, log(_, _, _, _)).Times(1); + EXPECT_CALL(*access_log_, log(_, _, _, _)); listener_callbacks->onAccept(std::make_unique>()); EXPECT_CALL(*listener, onDestroy()); } diff --git a/test/server/filter_chain_manager_impl_test.cc b/test/server/filter_chain_manager_impl_test.cc index bf009afbf27e..766a9bd51b7b 100644 --- a/test/server/filter_chain_manager_impl_test.cc +++ b/test/server/filter_chain_manager_impl_test.cc @@ -199,7 +199,7 @@ TEST_F(FilterChainManagerImplTest, DuplicateContextsAreNotBuilt) { filter_chain_messages.push_back(std::move(new_filter_chain)); } - EXPECT_CALL(filter_chain_factory_builder_, buildFilterChain(_, _)).Times(1); + EXPECT_CALL(filter_chain_factory_builder_, buildFilterChain(_, _)); filter_chain_manager_.addFilterChains( std::vector{&filter_chain_messages[0]}, nullptr, filter_chain_factory_builder_, filter_chain_manager_); diff --git a/test/server/hot_restart_impl_test.cc b/test/server/hot_restart_impl_test.cc index 14133189d03a..3c61523ebfdf 100644 --- a/test/server/hot_restart_impl_test.cc +++ b/test/server/hot_restart_impl_test.cc @@ -85,7 +85,7 @@ TEST_F(HotRestartImplTest, VersionString) { TEST_F(HotRestartImplTest, DomainSocketAlreadyInUse) { EXPECT_CALL(os_sys_calls_, bind(_, _, _)) .WillOnce(Return(Api::SysCallIntResult{-1, SOCKET_ERROR_ADDR_IN_USE})); - EXPECT_CALL(os_sys_calls_, close(_)).Times(1); + EXPECT_CALL(os_sys_calls_, close(_)); EXPECT_THROW(std::make_unique(0, 0, "@envoy_domain_socket", 0), Server::HotRestartDomainSocketInUseException); @@ -96,7 +96,7 @@ TEST_F(HotRestartImplTest, DomainSocketAlreadyInUse) { TEST_F(HotRestartImplTest, DomainSocketError) { EXPECT_CALL(os_sys_calls_, bind(_, _, _)) .WillOnce(Return(Api::SysCallIntResult{-1, SOCKET_ERROR_ACCESS})); - EXPECT_CALL(os_sys_calls_, close(_)).Times(1); + EXPECT_CALL(os_sys_calls_, close(_)); EXPECT_THROW(std::make_unique(0, 0, "@envoy_domain_socket", 0), EnvoyException); } diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index 18ab26995322..98a6977d19b4 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -856,11 +856,11 @@ version_info: version1 seconds: 1001001001 nanos: 1000000 )EOF"); - EXPECT_CALL(listener_foo->target_, initialize()).Times(1); + EXPECT_CALL(listener_foo->target_, initialize()); server_init_mgr.initialize(server_init_watcher); // Since listener_foo->target_ is not ready, the listener's listener_init_target will not be // ready until the destruction happens. - server_init_watcher.expectReady().Times(1); + server_init_watcher.expectReady(); EXPECT_CALL(*listener_foo, onDestroy()); EXPECT_TRUE(manager_->removeListener("foo")); } @@ -884,7 +884,7 @@ version_info: version1 ListenerHandle* listener_foo2 = expectListenerCreate(true, true); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); // Version 2 listener will be initialized by listener manager directly. - EXPECT_CALL(listener_foo2->target_, initialize()).Times(1); + EXPECT_CALL(listener_foo2->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "version2", true)); // Version2 is in warming list as listener_foo2->target_ is not ready yet. @@ -1844,8 +1844,8 @@ traffic_direction: OUTBOUND EXPECT_EQ(2UL, manager_->listeners().size()); // Validate that stop listener is only called once - for inbound listeners. - EXPECT_CALL(*worker_, stopListener(_, _)).Times(1); - EXPECT_CALL(*listener_factory_.socket_, close()).Times(1); + EXPECT_CALL(*worker_, stopListener(_, _)); + EXPECT_CALL(*listener_factory_.socket_, close()); manager_->stopListeners(ListenerManager::StopListenersType::InboundOnly); EXPECT_EQ(1, server_.stats_store_.counterFromString("listener_manager.listener_stopped").value()); diff --git a/tools/code_format/check_format.py b/tools/code_format/check_format.py index a9ce6e3deaf1..7e666ed0619f 100755 --- a/tools/code_format/check_format.py +++ b/tools/code_format/check_format.py @@ -169,10 +169,22 @@ "ProtobufWkt::MapPair": "Protobuf::MapPair", "ProtobufUtil::MessageDifferencer": "Protobuf::util::MessageDifferencer" } + LIBCXX_REPLACEMENTS = { "absl::make_unique<": "std::make_unique<", } +CODE_CONVENTION_REPLACEMENTS = { + # We can't just remove Times(1) everywhere, since .Times(1).WillRepeatedly + # is a legitimate pattern. See + # https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#cardinalities-how-many-times-will-it-be-called + ".Times(1);": ";", + # These may miss some cases, due to line breaks, but should reduce the + # Times(1) noise. + ".Times(1).WillOnce": ".WillOnce", + ".Times(1).WillRepeatedly": ".WillOnce", +} + UNOWNED_EXTENSIONS = { "extensions/filters/http/ratelimit", "extensions/filters/http/buffer", @@ -568,6 +580,10 @@ def fixSourceLine(self, line, line_number): for invalid_construct, valid_construct in LIBCXX_REPLACEMENTS.items(): line = line.replace(invalid_construct, valid_construct) + # Fix code conventions violations. + for invalid_construct, valid_construct in CODE_CONVENTION_REPLACEMENTS.items(): + line = line.replace(invalid_construct, valid_construct) + return line # We want to look for a call to condvar.waitFor, but there's no strong pattern @@ -634,6 +650,10 @@ def checkSourceLine(self, line, file_path, reportError): if invalid_construct in line: reportError("term %s should be replaced with standard library term %s" % (invalid_construct, valid_construct)) + for invalid_construct, valid_construct in CODE_CONVENTION_REPLACEMENTS.items(): + if invalid_construct in line: + reportError("term %s should be replaced with preferred term %s" % + (invalid_construct, valid_construct)) # Do not include the virtual_includes headers. if re.search("#include.*/_virtual_includes/", line): reportError("Don't include the virtual includes headers.") diff --git a/tools/code_format/check_format_test_helper.py b/tools/code_format/check_format_test_helper.py index aa90d12848ec..25c8ec12c107 100755 --- a/tools/code_format/check_format_test_helper.py +++ b/tools/code_format/check_format_test_helper.py @@ -303,6 +303,8 @@ def runChecks(): errors += checkAndFixError( "cpp_std.cc", "term absl::make_unique< should be replaced with standard library term std::make_unique<") + errors += checkAndFixError("code_conventions.cc", + "term .Times(1); should be replaced with preferred term ;") errors += checkFileExpectingOK("real_time_source_override.cc") errors += checkFileExpectingOK("duration_value_zero.cc") diff --git a/tools/testdata/check_format/code_conventions.cc b/tools/testdata/check_format/code_conventions.cc new file mode 100644 index 000000000000..7e0ec253b005 --- /dev/null +++ b/tools/testdata/check_format/code_conventions.cc @@ -0,0 +1,11 @@ +namespace Envoy { + +void foo() { + EXPECT_CALL(a, b).Times(1); + EXPECT_CALL(a, b) + .Times(1); + EXPECT_CALL(a, b).Times(1).WillRepeatedly(foo); + EXPECT_CALL(a, b).Times(1).WillOnce(foo); +} + +} diff --git a/tools/testdata/check_format/code_conventions.cc.gold b/tools/testdata/check_format/code_conventions.cc.gold new file mode 100644 index 000000000000..6dda2aa8b807 --- /dev/null +++ b/tools/testdata/check_format/code_conventions.cc.gold @@ -0,0 +1,10 @@ +namespace Envoy { + +void foo() { + EXPECT_CALL(a, b); + EXPECT_CALL(a, b); + EXPECT_CALL(a, b).WillOnce(foo); + EXPECT_CALL(a, b).WillOnce(foo); +} + +} // namespace Envoy From 52b616f09dceb603e6481dc8bf8d39eda2f3adfd Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 10 Dec 2020 17:03:14 -0500 Subject: [PATCH 02/49] tcp_proxy: logs consistently (#14368) moving the lone ENVOY_LOG in tcp proxy session to ENVOY_CONN_LOG Risk Level: low Testing: tests pass Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- source/common/tcp_proxy/tcp_proxy.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index 6745d1cde5ee..5b25c48bafff 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -632,8 +632,8 @@ void Filter::onUpstreamConnection() { Upstream::Outlier::Result::LocalOriginConnectSuccessFinal); getStreamInfo().setRequestedServerName(read_callbacks_->connection().requestedServerName()); - ENVOY_LOG(debug, "TCP:onUpstreamEvent(), requestedServerName: {}", - getStreamInfo().requestedServerName()); + ENVOY_CONN_LOG(debug, "TCP:onUpstreamEvent(), requestedServerName: {}", + read_callbacks_->connection(), getStreamInfo().requestedServerName()); if (config_->idleTimeout()) { // The idle_timer_ can be moved to a Drainer, so related callbacks call into From 8a28e06cd15cf08a4a5cdd920dc60857df11cc86 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 10 Dec 2020 17:40:45 -0500 Subject: [PATCH 03/49] get most of the cluster stats into the new 2-phase macro system. (#14340) Signed-off-by: Joshua Marantz Commit Message: Capture StatNames in the factory for extra categories of cluster stats. This is a follow-up to #14028 which established the stat-macro infrastructure needed to make this easy most of the time. Additional Description: This still leaves behind the stats for circuit breakers, which are difficult to convert to the new stat-name macro-creation structure due to some pretty creative use of the existing macros. Resolving that can be a follow-up, which I think might require abandoning the macros for circuit-breaker stats and just using direct API calls. Risk Level: low Testing: //test/... Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a --- include/envoy/upstream/cluster_manager.h | 4 ++ include/envoy/upstream/upstream.h | 39 ++++++++----------- .../common/upstream/cluster_manager_impl.cc | 3 ++ source/common/upstream/cluster_manager_impl.h | 13 +++++++ source/common/upstream/upstream_impl.cc | 39 ++++++++++++------- source/common/upstream/upstream_impl.h | 12 ++++-- test/mocks/upstream/cluster_info.cc | 14 +++++-- test/mocks/upstream/cluster_info.h | 3 ++ test/mocks/upstream/cluster_manager.cc | 5 ++- test/mocks/upstream/cluster_manager.h | 12 ++++++ 10 files changed, 97 insertions(+), 47 deletions(-) diff --git a/include/envoy/upstream/cluster_manager.h b/include/envoy/upstream/cluster_manager.h index 15665dd658d5..8729458cb69d 100644 --- a/include/envoy/upstream/cluster_manager.h +++ b/include/envoy/upstream/cluster_manager.h @@ -345,6 +345,10 @@ class ClusterManager { * @return the stat names. */ virtual const ClusterStatNames& clusterStatNames() const PURE; + virtual const ClusterLoadReportStatNames& clusterLoadReportStatNames() const PURE; + virtual const ClusterRequestResponseSizeStatNames& + clusterRequestResponseSizeStatNames() const PURE; + virtual const ClusterTimeoutBudgetStatNames& clusterTimeoutBudgetStatNames() const PURE; }; using ClusterManagerPtr = std::unique_ptr; diff --git a/include/envoy/upstream/upstream.h b/include/envoy/upstream/upstream.h index d863b0caeb54..647706fd371d 100644 --- a/include/envoy/upstream/upstream.h +++ b/include/envoy/upstream/upstream.h @@ -606,7 +606,8 @@ class PrioritySet { * stats sink. See envoy.api.v2.endpoint.ClusterStats for the definition of upstream_rq_dropped. * These are latched by LoadStatsReporter, independent of the normal stats sink flushing. */ -#define ALL_CLUSTER_LOAD_REPORT_STATS(COUNTER) COUNTER(upstream_rq_dropped) +#define ALL_CLUSTER_LOAD_REPORT_STATS(COUNTER, GAUGE, HISTOGRAM, TEXT_READOUT, STATNAME) \ + COUNTER(upstream_rq_dropped) /** * Cluster circuit breakers stats. Open circuit breaker stats and remaining resource stats @@ -627,7 +628,7 @@ class PrioritySet { /** * All stats tracking request/response headers and body sizes. Not used by default. */ -#define ALL_CLUSTER_REQUEST_RESPONSE_SIZE_STATS(HISTOGRAM) \ +#define ALL_CLUSTER_REQUEST_RESPONSE_SIZE_STATS(COUNTER, GAUGE, HISTOGRAM, TEXT_READOUT, STATNAME) \ HISTOGRAM(upstream_rq_headers_size, Bytes) \ HISTOGRAM(upstream_rq_body_size, Bytes) \ HISTOGRAM(upstream_rs_headers_size, Bytes) \ @@ -636,7 +637,7 @@ class PrioritySet { /** * All stats around timeout budgets. Not used by default. */ -#define ALL_CLUSTER_TIMEOUT_BUDGET_STATS(HISTOGRAM) \ +#define ALL_CLUSTER_TIMEOUT_BUDGET_STATS(COUNTER, GAUGE, HISTOGRAM, TEXT_READOUT, STATNAME) \ HISTOGRAM(upstream_rq_timeout_budget_percent_used, Unspecified) \ HISTOGRAM(upstream_rq_timeout_budget_per_try_percent_used, Unspecified) @@ -646,12 +647,18 @@ class PrioritySet { MAKE_STAT_NAMES_STRUCT(ClusterStatNames, ALL_CLUSTER_STATS); MAKE_STATS_STRUCT(ClusterStats, ClusterStatNames, ALL_CLUSTER_STATS); -/** - * Struct definition for all cluster load report stats. @see stats_macros.h - */ -struct ClusterLoadReportStats { - ALL_CLUSTER_LOAD_REPORT_STATS(GENERATE_COUNTER_STRUCT) -}; +MAKE_STAT_NAMES_STRUCT(ClusterLoadReportStatNames, ALL_CLUSTER_LOAD_REPORT_STATS); +MAKE_STATS_STRUCT(ClusterLoadReportStats, ClusterLoadReportStatNames, + ALL_CLUSTER_LOAD_REPORT_STATS); + +MAKE_STAT_NAMES_STRUCT(ClusterRequestResponseSizeStatNames, + ALL_CLUSTER_REQUEST_RESPONSE_SIZE_STATS); +MAKE_STATS_STRUCT(ClusterRequestResponseSizeStats, ClusterRequestResponseSizeStatNames, + ALL_CLUSTER_REQUEST_RESPONSE_SIZE_STATS); + +MAKE_STAT_NAMES_STRUCT(ClusterTimeoutBudgetStatNames, ALL_CLUSTER_TIMEOUT_BUDGET_STATS); +MAKE_STATS_STRUCT(ClusterTimeoutBudgetStats, ClusterTimeoutBudgetStatNames, + ALL_CLUSTER_TIMEOUT_BUDGET_STATS); /** * Struct definition for cluster circuit breakers stats. @see stats_macros.h @@ -660,24 +667,10 @@ struct ClusterCircuitBreakersStats { ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(GENERATE_GAUGE_STRUCT, GENERATE_GAUGE_STRUCT) }; -/** - * Struct definition for cluster timeout budget stats. @see stats_macros.h - */ -struct ClusterRequestResponseSizeStats { - ALL_CLUSTER_REQUEST_RESPONSE_SIZE_STATS(GENERATE_HISTOGRAM_STRUCT) -}; - using ClusterRequestResponseSizeStatsPtr = std::unique_ptr; using ClusterRequestResponseSizeStatsOptRef = absl::optional>; -/** - * Struct definition for cluster timeout budget stats. @see stats_macros.h - */ -struct ClusterTimeoutBudgetStats { - ALL_CLUSTER_TIMEOUT_BUDGET_STATS(GENERATE_HISTOGRAM_STRUCT) -}; - using ClusterTimeoutBudgetStatsPtr = std::unique_ptr; using ClusterTimeoutBudgetStatsOptRef = absl::optional>; diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 822ba56c609c..f171f8b388cf 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -260,6 +260,9 @@ ClusterManagerImpl::ClusterManagerImpl( time_source_(main_thread_dispatcher.timeSource()), dispatcher_(main_thread_dispatcher), http_context_(http_context), router_context_(router_context), cluster_stat_names_(stats.symbolTable()), + cluster_load_report_stat_names_(stats.symbolTable()), + cluster_request_response_size_stat_names_(stats.symbolTable()), + cluster_timeout_budget_stat_names_(stats.symbolTable()), subscription_factory_(local_info, main_thread_dispatcher, *this, validation_context.dynamicValidationVisitor(), api, runtime_) { async_client_manager_ = std::make_unique( diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index f12361bc2830..013547194ab6 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -299,6 +299,15 @@ class ClusterManagerImpl : public ClusterManager, Logger::LoggablesymbolTable()), - load_report_stats_(generateLoadReportStats(load_report_stats_store_)), + load_report_stats_(generateLoadReportStats( + load_report_stats_store_, factory_context.clusterManager().clusterLoadReportStatNames())), optional_cluster_stats_((config.has_track_cluster_stats() || config.track_timeout_budgets()) - ? std::make_unique(config, *stats_scope_) + ? std::make_unique( + config, *stats_scope_, factory_context.clusterManager()) : nullptr), features_(parseFeatures(config, http_protocol_options_)), resource_managers_(config, runtime, name_, *stats_scope_), @@ -1139,15 +1145,18 @@ void ClusterImplBase::validateEndpointsForZoneAwareRouting( } ClusterInfoImpl::OptionalClusterStats::OptionalClusterStats( - const envoy::config::cluster::v3::Cluster& config, Stats::Scope& stats_scope) + const envoy::config::cluster::v3::Cluster& config, Stats::Scope& stats_scope, + const ClusterManager& manager) : timeout_budget_stats_( (config.track_cluster_stats().timeout_budgets() || config.track_timeout_budgets()) - ? std::make_unique(generateTimeoutBudgetStats(stats_scope)) + ? std::make_unique(generateTimeoutBudgetStats( + stats_scope, manager.clusterTimeoutBudgetStatNames())) : nullptr), - request_response_size_stats_(config.track_cluster_stats().request_response_sizes() - ? std::make_unique( - generateRequestResponseSizeStats(stats_scope)) - : nullptr) {} + request_response_size_stats_( + (config.track_cluster_stats().request_response_sizes() + ? std::make_unique(generateRequestResponseSizeStats( + stats_scope, manager.clusterRequestResponseSizeStatNames())) + : nullptr)) {} ClusterInfoImpl::ResourceManagers::ResourceManagers( const envoy::config::cluster::v3::Cluster& config, Runtime::Loader& runtime, diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 356596e4d5da..3b22d6577482 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -528,12 +528,16 @@ class ClusterInfoImpl : public ClusterInfo, protected Logger::Loggable()), - load_report_stats_(ClusterInfoImpl::generateLoadReportStats(load_report_stats_store_)), + load_report_stats_(ClusterInfoImpl::generateLoadReportStats(load_report_stats_store_, + cluster_load_report_stat_names_)), request_response_size_stats_(std::make_unique( - ClusterInfoImpl::generateRequestResponseSizeStats(request_response_size_stats_store_))), - timeout_budget_stats_(std::make_unique( - ClusterInfoImpl::generateTimeoutBudgetStats(timeout_budget_stats_store_))), + ClusterInfoImpl::generateRequestResponseSizeStats( + request_response_size_stats_store_, cluster_request_response_size_stat_names_))), + timeout_budget_stats_( + std::make_unique(ClusterInfoImpl::generateTimeoutBudgetStats( + timeout_budget_stats_store_, cluster_timeout_budget_stat_names_))), circuit_breakers_stats_( ClusterInfoImpl::generateCircuitBreakersStats(stats_store_, "default", true)), resource_manager_(new Upstream::ResourceManagerImpl( diff --git a/test/mocks/upstream/cluster_info.h b/test/mocks/upstream/cluster_info.h index 0ff62d774c2e..fc2fb337e21e 100644 --- a/test/mocks/upstream/cluster_info.h +++ b/test/mocks/upstream/cluster_info.h @@ -157,6 +157,9 @@ class MockClusterInfo : public ClusterInfo { uint32_t max_response_headers_count_{Http::DEFAULT_MAX_HEADERS_COUNT}; NiceMock stats_store_; ClusterStatNames stat_names_; + ClusterLoadReportStatNames cluster_load_report_stat_names_; + ClusterRequestResponseSizeStatNames cluster_request_response_size_stat_names_; + ClusterTimeoutBudgetStatNames cluster_timeout_budget_stat_names_; ClusterStats stats_; Upstream::TransportSocketMatcherPtr transport_socket_matcher_; NiceMock load_report_stats_store_; diff --git a/test/mocks/upstream/cluster_manager.cc b/test/mocks/upstream/cluster_manager.cc index 0adcadba2348..2aaf1d5140f1 100644 --- a/test/mocks/upstream/cluster_manager.cc +++ b/test/mocks/upstream/cluster_manager.cc @@ -16,7 +16,10 @@ using ::testing::ReturnRef; MockClusterManager::MockClusterManager(TimeSource&) : MockClusterManager() {} -MockClusterManager::MockClusterManager() : cluster_stat_names_(*symbol_table_) { +MockClusterManager::MockClusterManager() + : cluster_stat_names_(*symbol_table_), cluster_load_report_stat_names_(*symbol_table_), + cluster_request_response_size_stat_names_(*symbol_table_), + cluster_timeout_budget_stat_names_(*symbol_table_) { ON_CALL(*this, httpConnPoolForCluster(_, _, _, _)).WillByDefault(Return(&conn_pool_)); ON_CALL(*this, tcpConnPoolForCluster(_, _, _)).WillByDefault(Return(&tcp_conn_pool_)); ON_CALL(*this, httpAsyncClientForCluster(_)).WillByDefault(ReturnRef(async_client_)); diff --git a/test/mocks/upstream/cluster_manager.h b/test/mocks/upstream/cluster_manager.h index 1e583c456d2e..445408d4eb5c 100644 --- a/test/mocks/upstream/cluster_manager.h +++ b/test/mocks/upstream/cluster_manager.h @@ -71,6 +71,15 @@ class MockClusterManager : public ClusterManager { (ClusterUpdateCallbacks & callbacks)); MOCK_METHOD(Config::SubscriptionFactory&, subscriptionFactory, ()); const ClusterStatNames& clusterStatNames() const override { return cluster_stat_names_; } + const ClusterLoadReportStatNames& clusterLoadReportStatNames() const override { + return cluster_load_report_stat_names_; + } + const ClusterRequestResponseSizeStatNames& clusterRequestResponseSizeStatNames() const override { + return cluster_request_response_size_stat_names_; + } + const ClusterTimeoutBudgetStatNames& clusterTimeoutBudgetStatNames() const override { + return cluster_timeout_budget_stat_names_; + } NiceMock conn_pool_; NiceMock async_client_; @@ -86,6 +95,9 @@ class MockClusterManager : public ClusterManager { absl::flat_hash_map> warming_clusters_; Stats::TestSymbolTable symbol_table_; ClusterStatNames cluster_stat_names_; + ClusterLoadReportStatNames cluster_load_report_stat_names_; + ClusterRequestResponseSizeStatNames cluster_request_response_size_stat_names_; + ClusterTimeoutBudgetStatNames cluster_timeout_budget_stat_names_; }; } // namespace Upstream From 709414ba93e2be1923f74d7b46542a74553eecfd Mon Sep 17 00:00:00 2001 From: htuch Date: Thu, 10 Dec 2020 19:05:33 -0500 Subject: [PATCH 04/49] dependencies: new external dep policy. (#14334) This patch converts https://docs.google.com/document/d/1HbREo7pv7rgeIIjQn6mNpySzQE5rx2Yv9dXm5NqR2N8/edit# to Markdown and provides a PR-based review of the policy, following discussion in the doc and various offline threads. Fixes #10471 Signed-off-by: Harvey Tuch --- DEPENDENCY_POLICY.md | 51 ++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/DEPENDENCY_POLICY.md b/DEPENDENCY_POLICY.md index 777d7e64adf4..588dad437ad2 100644 --- a/DEPENDENCY_POLICY.md +++ b/DEPENDENCY_POLICY.md @@ -69,24 +69,39 @@ Pure developer tooling and documentation builds may reference Python via standal ## New external dependencies -* Any new dependency on the Envoy data or control plane that impacts Envoy core (i.e. is not - specific to a single non-core extension) must be cleared with the Envoy security team, please file - an issue and tag - [@envoyproxy/security-team](https://github.com/orgs/envoyproxy/teams/security-team). While policy - is still [evolving](robust_to_untrusted_downstream_and_upstream), criteria that will be used in - evaluation include: - * Does the project have release versions? How often do releases happen? - * Does the project have a security vulnerability disclosure process and contact details? - * Does the project have effective governance, e.g. multiple maintainers, a governance policy? - * Does the project have a code review culture? Are patches reviewed by independent maintainers - prior to merge? - * Does the project enable mandatory GitHub 2FA for contributors? - * Does the project have evidence of high test coverage, fuzzing, static analysis (e.g. CodeQL), - etc.? - -* Dependencies for extensions that are tagged as `robust_to_untrusted_downstream` or - `robust_to_untrusted_downstream_and_upstream` should be sensitive to the same set of concerns - as the core data plane. +Any new dependency on the Envoy data or control plane that impacts Envoy core (i.e. is not +specific to a single non-core extension) must be cleared with the Envoy dependency shepherds and +security team, please file an issue and tag both [dependency +shepherds](https://github.com/orgs/envoyproxy/teams/dependency-shepherds) and +the [@envoyproxy/security-team](https://github.com/orgs/envoyproxy/teams/security-team). + +The criteria below are used to evaluate new dependencies on the data, control +and observability plane. They apply to all core dependencies and any extension +that is robust to untrusted downstream or upstream traffic. The criteria are +guidelines, exceptions may be granted with solid rationale. Precedent from +existing extensions does not apply; there are extant extensions in violation of +this policy which we will be addressing over time, they do not provide grounds +to ignore policy criteria below. + +|Criteria|Requirement|Mnemonic|Weight|Rationale| +|--------|-----------|--------|------|---------| +|Cloud Native Computing Foundation (CNCF) [approved license](https://github.com/cncf/foundation/blob/master/allowed-third-party-license-policy.md#approved-licenses-for-allowlist)|MUST|License|High|| +|Dependencies must not substantially increase the binary size unless they are optional (i.e. confined to specific extensions)|MUST|BinarySize|High|Envoy Mobile is sensitive to binary size. We should pick dependencies that are used in core with this criteria in mind.| +|No duplication of existing dependencies|MUST|NoDuplication|High|Avoid maintenance cost of multiple JSON parsers etc| +|Hosted on a git repository and the archive fetch must directly reference this repository. We will NOT support intermediate artifacts built by-hand located on GCS, S3, etc.|MUST|Source|High|Flows based on manual updates are fragile (they are not tested until needed), often suffer from missing documentation and shared exercise, may fail during emergency zero day updates and have no audit trail (i.e. it's unclear how the artifact we depend upon came to be at a later date).| +|CVE history appears reasonable, no pathological CVE arcs|MUST|SoundCVEs|High|Avoid dependencies that are CVE heavy in the same area (e.g. buffer overflow) +|Code review (ideally PRs) before merge|MUST|Code-Review|Normal|Consistent code reviews| +|Security vulnerability process exists, with contact details and reporting/disclosure process|MUST|SecPolicy|High|Lack of a policy implies security bugs are open zero days| +|> 1 contributor responsible for a non-trivial number of commits|MUST|Contributors|Normal|Avoid bus factor of 1| +|Tests run in CI|MUST|CI-Tests|Normal|Changes gated on tests| +|High test coverage (also static/dynamic analysis, fuzzing)|SHOULD|Test-Coverage|Normal|Key dependencies must meet the same quality bar as Envoy| +|Envoy can obtain advanced notification of vulnerabilities or of security releases|SHOULD|SecPolicy-Compat|High|Coordinated security releases possible, but most dependencies do not feature this.| +|Do other significant projects have shared fate by using this dependency?|SHOULD|SharedFate|High|Increased likelihood of security community interest, many eyes.| +|Releases (with release notes)|SHOULD|Releases|Normal|Discrete upgrade points, clear understanding of security implications. We have many counterexamples today (e.g. CEL, re2).| +|Commits/releases in last 90 days|SHOULD|Active|Normal|Avoid unmaintained deps, not compulsory since some code bases are “done”| + +The rationale behind this policy is tracked +[here](https://docs.google.com/document/d/1HbREo7pv7rgeIIjQn6mNpySzQE5rx2Yv9dXm5NqR2N8/edit#). ## Maintaining existing dependencies From ea62aecce42c4332455a9ae537985e87f68d48e5 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 10 Dec 2020 19:36:52 -0500 Subject: [PATCH 05/49] tcp: fixing a tls logging bug with the new TCP pool (#14364) Also adding an integration test of tcp proxy tls-to-upstream (which passes without this PR, I just noticed we didn't have one) Risk Level: n/a (only affects the new TCP pool) Testing: unit test regression test Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- source/common/tcp/conn_pool.cc | 20 +++++++++++-------- test/common/tcp/BUILD | 1 + test/common/tcp/conn_pool_test.cc | 5 +++++ test/integration/autonomous_upstream.h | 6 ++++-- test/integration/base_integration_test.cc | 9 ++++++--- test/integration/base_integration_test.h | 1 + test/integration/fake_upstream.cc | 15 +++++++++++++- test/integration/fake_upstream.h | 5 +++++ .../integration/tcp_proxy_integration_test.cc | 19 ++++++++++++++++++ 9 files changed, 67 insertions(+), 14 deletions(-) diff --git a/source/common/tcp/conn_pool.cc b/source/common/tcp/conn_pool.cc index e905b2337711..6ff3f05e03d4 100644 --- a/source/common/tcp/conn_pool.cc +++ b/source/common/tcp/conn_pool.cc @@ -59,14 +59,18 @@ void ActiveTcpClient::clearCallbacks() { void ActiveTcpClient::onEvent(Network::ConnectionEvent event) { Envoy::ConnectionPool::ActiveClient::onEvent(event); - // Do not pass the Connected event to any session which registered during onEvent above. - // Consumers of connection pool connections assume they are receiving already connected - // connections. - if (callbacks_ && event != Network::ConnectionEvent::Connected) { - callbacks_->onEvent(event); - // After receiving a disconnect event, the owner of callbacks_ will likely self-destruct. - // Clear the pointer to avoid using it again. - callbacks_ = nullptr; + if (callbacks_) { + // Do not pass the Connected event to any session which registered during onEvent above. + // Consumers of connection pool connections assume they are receiving already connected + // connections. + if (event == Network::ConnectionEvent::Connected) { + connection_->streamInfo().setDownstreamSslConnection(connection_->ssl()); + } else { + callbacks_->onEvent(event); + // After receiving a disconnect event, the owner of callbacks_ will likely self-destruct. + // Clear the pointer to avoid using it again. + callbacks_ = nullptr; + } } } diff --git a/test/common/tcp/BUILD b/test/common/tcp/BUILD index 2a6da2345a2e..ed02ade4c464 100644 --- a/test/common/tcp/BUILD +++ b/test/common/tcp/BUILD @@ -21,6 +21,7 @@ envoy_cc_test( "//test/mocks/event:event_mocks", "//test/mocks/network:network_mocks", "//test/mocks/runtime:runtime_mocks", + "//test/mocks/ssl:ssl_mocks", "//test/mocks/tcp:tcp_mocks", "//test/mocks/upstream:cluster_info_mocks", "//test/test_common:simulated_time_system_lib", diff --git a/test/common/tcp/conn_pool_test.cc b/test/common/tcp/conn_pool_test.cc index 5fb2510e304a..1182d9c6d5e4 100644 --- a/test/common/tcp/conn_pool_test.cc +++ b/test/common/tcp/conn_pool_test.cc @@ -12,6 +12,7 @@ #include "test/mocks/event/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" +#include "test/mocks/ssl/mocks.h" #include "test/mocks/tcp/mocks.h" #include "test/mocks/upstream/cluster_info.h" #include "test/test_common/printers.h" @@ -283,6 +284,7 @@ class TcpConnPoolImplDestructorTest : public Event::TestUsingSimulatedTime, conn_pool_ = std::make_unique( dispatcher_, host_, Upstream::ResourcePriority::Default, nullptr, nullptr); } + ssl_ = std::make_shared>(); } ~TcpConnPoolImplDestructorTest() override = default; @@ -298,13 +300,16 @@ class TcpConnPoolImplDestructorTest : public Event::TestUsingSimulatedTime, EXPECT_CALL(*connect_timer_, disableTimer()); EXPECT_CALL(callbacks_->pool_ready_, ready()); + EXPECT_CALL(*connection_, ssl()).WillOnce(Return(ssl_)); connection_->raiseEvent(Network::ConnectionEvent::Connected); + EXPECT_EQ(connection_->streamInfo().downstreamSslConnection(), ssl_); } bool test_new_connection_pool_; Upstream::ClusterConnectivityState state_; Upstream::HostConstSharedPtr host_; NiceMock dispatcher_; + std::shared_ptr> ssl_; std::shared_ptr cluster_{new NiceMock()}; NiceMock* upstream_ready_cb_; NiceMock* connect_timer_; diff --git a/test/integration/autonomous_upstream.h b/test/integration/autonomous_upstream.h index b53ec4193eea..3a5acd2443a5 100644 --- a/test/integration/autonomous_upstream.h +++ b/test/integration/autonomous_upstream.h @@ -58,9 +58,11 @@ using AutonomousHttpConnectionPtr = std::unique_ptr; // An upstream which creates AutonomousHttpConnection for new incoming connections. class AutonomousUpstream : public FakeUpstream { public: - AutonomousUpstream(const Network::Address::InstanceConstSharedPtr& address, + AutonomousUpstream(Network::TransportSocketFactoryPtr&& transport_socket_factory, + const Network::Address::InstanceConstSharedPtr& address, const FakeUpstreamConfig& config, bool allow_incomplete_streams) - : FakeUpstream(address, config), allow_incomplete_streams_(allow_incomplete_streams), + : FakeUpstream(std::move(transport_socket_factory), address, config), + allow_incomplete_streams_(allow_incomplete_streams), response_trailers_(std::make_unique()), response_headers_(std::make_unique( Http::TestResponseHeaderMapImpl({{":status", "200"}}))) {} diff --git a/test/integration/base_integration_test.cc b/test/integration/base_integration_test.cc index 8cbaba5ead73..647faae59874 100644 --- a/test/integration/base_integration_test.cc +++ b/test/integration/base_integration_test.cc @@ -135,12 +135,15 @@ Network::TransportSocketFactoryPtr BaseIntegrationTest::createUpstreamTlsContext void BaseIntegrationTest::createUpstreams() { for (uint32_t i = 0; i < fake_upstreams_count_; ++i) { + Network::TransportSocketFactoryPtr factory = + upstream_tls_ ? createUpstreamTlsContext() : Network::Test::createRawBufferSocketFactory(); auto endpoint = upstream_address_fn_(i); if (autonomous_upstream_) { - fake_upstreams_.emplace_back( - new AutonomousUpstream(endpoint, upstreamConfig(), autonomous_allow_incomplete_streams_)); + fake_upstreams_.emplace_back(new AutonomousUpstream( + std::move(factory), endpoint, upstreamConfig(), autonomous_allow_incomplete_streams_)); } else { - fake_upstreams_.emplace_back(new FakeUpstream(endpoint, upstreamConfig())); + fake_upstreams_.emplace_back( + new FakeUpstream(std::move(factory), endpoint, upstreamConfig())); } } } diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index 632030d997b4..4a474eab0dc8 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -416,6 +416,7 @@ class BaseIntegrationTest : protected Logger::Loggable { bool create_xds_upstream_{false}; bool tls_xds_upstream_{false}; bool use_lds_{true}; // Use the integration framework's LDS set up. + bool upstream_tls_{false}; Network::TransportSocketFactoryPtr createUpstreamTlsContext(); testing::NiceMock factory_context_; diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index e4ff39d79e32..c0aadbbc5f62 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -498,12 +498,25 @@ FakeUpstream::FakeUpstream(uint32_t port, Network::Address::IpVersion version, localAddress()->ip()->port(), Network::Test::addressVersionAsString(version)); } +FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket_factory, + const Network::Address::InstanceConstSharedPtr& address, + const FakeUpstreamConfig& config) + : FakeUpstream(std::move(transport_socket_factory), + config.udp_fake_upstream_ ? makeUdpListenSocket(address) + : makeTcpListenSocket(address), + config) { + ENVOY_LOG(info, "starting fake server on socket {}:{}. Address version is {}. UDP={}", + address->ip()->addressAsString(), address->ip()->port(), + Network::Test::addressVersionAsString(address->ip()->version()), + config.udp_fake_upstream_); +} + FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket_factory, uint32_t port, Network::Address::IpVersion version, const FakeUpstreamConfig& config) : FakeUpstream(std::move(transport_socket_factory), makeTcpListenSocket(port, version), config) { - ENVOY_LOG(info, "starting fake SSL server on port {}. Address version is {}", + ENVOY_LOG(info, "starting fake server on port {}. Address version is {}", localAddress()->ip()->port(), Network::Test::addressVersionAsString(version)); } diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index e2454b03cb26..d648d7117dec 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -546,6 +546,11 @@ class FakeUpstream : Logger::Loggable, // Creates a fake upstream bound to the specified unix domain socket path. FakeUpstream(const std::string& uds_path, const FakeUpstreamConfig& config); + // Creates a fake upstream bound to the specified |address|. + FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket_factory, + const Network::Address::InstanceConstSharedPtr& address, + const FakeUpstreamConfig& config); + // Creates a fake upstream bound to the specified |address|. FakeUpstream(const Network::Address::InstanceConstSharedPtr& address, const FakeUpstreamConfig& config); diff --git a/test/integration/tcp_proxy_integration_test.cc b/test/integration/tcp_proxy_integration_test.cc index 9559df0e1031..d8e98c10daba 100644 --- a/test/integration/tcp_proxy_integration_test.cc +++ b/test/integration/tcp_proxy_integration_test.cc @@ -93,6 +93,25 @@ TEST_P(TcpProxyIntegrationTest, TcpProxyUpstreamWritesFirst) { ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); } +// Test TLS upstream. +TEST_P(TcpProxyIntegrationTest, TcpProxyUpstreamTls) { + upstream_tls_ = true; + config_helper_.configureUpstreamTls(); + initialize(); + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + ASSERT_TRUE(tcp_client->write("hello")); + FakeRawConnectionPtr fake_upstream_connection; + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + ASSERT_TRUE(fake_upstream_connection->waitForData(5)); + ASSERT_TRUE(fake_upstream_connection->write("world")); + ASSERT_TRUE(fake_upstream_connection->close()); + ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); + tcp_client->waitForHalfClose(); + tcp_client->close(); + + EXPECT_EQ("world", tcp_client->data()); +} + // Test proxying data in both directions, and that all data is flushed properly // when there is an upstream disconnect. TEST_P(TcpProxyIntegrationTest, TcpProxyUpstreamDisconnect) { From 33b8ce7e3e10c42b5d9e6186fe1900b25cea284e Mon Sep 17 00:00:00 2001 From: Ken Dombeck Date: Thu, 10 Dec 2020 18:50:38 -0600 Subject: [PATCH 06/49] docs: fix broken link (#14369) Signed-off-by: Ken Dombeck --- docs/root/start/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/root/start/install.rst b/docs/root/start/install.rst index fabf4f9e673a..0b73d5c1fcc5 100644 --- a/docs/root/start/install.rst +++ b/docs/root/start/install.rst @@ -134,7 +134,7 @@ Install Envoy using Docker ~~~~~~~~~~~~~~~~~~~~~~~~~~ You can run Envoy using the official Docker images, or by -using images provided by `Get Envoy `__. +using images provided by `Get Envoy `__. The following commands will pull and show the Envoy version of current images. From b928a18af7f5cc42c7610e8a05a109561f6ea19c Mon Sep 17 00:00:00 2001 From: Jonathan Chang <31893406+cccntu@users.noreply.github.com> Date: Fri, 11 Dec 2020 08:51:06 +0800 Subject: [PATCH 07/49] docs: fix command in grpc_bridge example (#14355) Signed-off-by: Jonathan Chang --- docs/root/start/sandboxes/grpc_bridge.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/root/start/sandboxes/grpc_bridge.rst b/docs/root/start/sandboxes/grpc_bridge.rst index 7d729bcad428..90b98021729b 100644 --- a/docs/root/start/sandboxes/grpc_bridge.rst +++ b/docs/root/start/sandboxes/grpc_bridge.rst @@ -98,7 +98,7 @@ Set a key: .. code-block:: console - $ docker-compose exec python /client/client.py set foo bar + $ docker-compose exec grpc-client python /client/grpc-kv-client.py set foo bar setf foo to bar @@ -106,21 +106,21 @@ Get a key: .. code-block:: console - $ docker-compose exec python /client/client.py get foo + $ docker-compose exec grpc-client python /client/grpc-kv-client.py get foo bar Modify an existing key: .. code-block:: console - $ docker-compose exec python /client/client.py set foo baz + $ docker-compose exec grpc-client python /client/grpc-kv-client.py set foo baz setf foo to baz Get the modified key: .. code-block:: console - $ docker-compose exec python /client/client.py get foo + $ docker-compose exec grpc-client python /client/grpc-kv-client.py get foo baz In the running docker-compose container, you should see the gRPC service printing a record of its activity: From d1c20748714aba7c6236dc092213defe81916910 Mon Sep 17 00:00:00 2001 From: Matt Klein Date: Thu, 10 Dec 2020 17:18:06 -0800 Subject: [PATCH 08/49] tech debt: move connection and async client APIs to thread local cluster (#14332) This is part of several follow ups to #13906. This change moves various functions from the cluster API to the thread local cluster API. This simplifies error handling and also will increase performance slightly as many instances of double map lookups are now a single lookup. Signed-off-by: Matt Klein --- include/envoy/upstream/cluster_manager.h | 46 - include/envoy/upstream/thread_local_cluster.h | 48 + source/common/config/remote_data_fetcher.cc | 14 +- source/common/grpc/async_client_impl.cc | 5 +- source/common/http/rest_api_fetcher.cc | 11 +- source/common/router/shadow_writer_impl.cc | 5 +- source/common/tcp_proxy/tcp_proxy.cc | 2 +- source/common/tcp_proxy/upstream.cc | 19 +- .../common/upstream/cluster_manager_impl.cc | 81 +- source/common/upstream/cluster_manager_impl.h | 21 +- .../common/upstream/cluster_update_tracker.cc | 8 +- .../common/upstream/cluster_update_tracker.h | 6 +- source/extensions/common/wasm/context.cc | 8 +- .../common/ext_authz/ext_authz_http_impl.cc | 5 +- .../filters/http/common/jwks_fetcher.cc | 7 +- .../extensions/filters/http/lua/lua_filter.cc | 6 +- .../filters/http/oauth2/oauth_client.cc | 14 +- .../filters/http/squash/squash_filter.cc | 16 +- .../network/dubbo_proxy/router/router_impl.cc | 4 +- .../rocketmq_proxy/router/router_impl.cc | 4 +- .../thrift_proxy/router/router_impl.cc | 4 +- .../stat_sinks/common/statsd/statsd.cc | 8 +- .../tracers/datadog/datadog_tracer_impl.cc | 10 +- .../lightstep/lightstep_tracer_impl.cc | 12 +- .../tracers/zipkin/zipkin_tracer_impl.cc | 10 +- .../upstreams/http/http/upstream_request.h | 10 +- .../upstreams/http/tcp/upstream_request.h | 9 +- source/server/config_validation/BUILD | 13 - .../server/config_validation/async_client.cc | 19 - .../server/config_validation/async_client.h | 37 - .../config_validation/cluster_manager.cc | 30 +- .../config_validation/cluster_manager.h | 35 +- source/server/config_validation/server.cc | 2 +- test/common/config/datasource_test.cc | 64 +- .../config/http_subscription_impl_test.cc | 13 + .../config/http_subscription_test_harness.h | 9 +- .../config/subscription_factory_impl_test.cc | 15 +- test/common/grpc/async_client_impl_test.cc | 5 +- .../grpc_client_integration_test_harness.h | 29 +- test/common/http/async_client_impl_test.cc | 206 +-- .../network/filter_manager_impl_test.cc | 2 +- test/common/router/router_test.cc | 1161 +++++++++-------- .../common/router/router_upstream_log_test.cc | 52 +- test/common/router/shadow_writer_impl_test.cc | 9 +- test/common/tcp_proxy/tcp_proxy_test.cc | 44 +- .../upstream/cluster_manager_impl_test.cc | 374 +++--- .../upstream/cluster_update_tracker_test.cc | 26 +- test/config_test/config_test.cc | 2 +- test/extensions/common/wasm/wasm_test.cc | 14 +- .../ext_authz/ext_authz_http_impl_test.cc | 5 +- .../filters/http/common/fuzz/uber_filter.cc | 3 +- .../http/common/fuzz/uber_per_filter.cc | 3 +- .../filters/http/common/jwks_fetcher_test.cc | 6 +- test/extensions/filters/http/common/mock.cc | 13 +- .../http/jwt_authn/authenticator_test.cc | 2 +- test/extensions/filters/http/jwt_authn/mock.h | 4 +- .../http/jwt_authn/provider_verifier_test.cc | 3 +- .../filters/http/lua/lua_filter_test.cc | 64 +- .../filters/http/oauth2/filter_test.cc | 2 +- .../filters/http/oauth2/oauth_test.cc | 33 +- .../filters/http/squash/squash_filter_test.cc | 25 +- .../filters/http/wasm/config_test.cc | 69 +- .../filters/http/wasm/wasm_filter_test.cc | 30 +- .../client_ssl_auth/client_ssl_auth_test.cc | 17 +- .../network/client_ssl_auth/config_test.cc | 3 + .../network/dubbo_proxy/router_test.cc | 38 +- .../network/rocketmq_proxy/router_test.cc | 19 +- .../network/thrift_proxy/router_test.cc | 89 +- .../stats_sinks/common/statsd/statsd_test.cc | 6 +- .../datadog/datadog_tracer_impl_test.cc | 42 +- .../lightstep/lightstep_tracer_impl_test.cc | 56 +- .../tracers/zipkin/zipkin_tracer_impl_test.cc | 46 +- .../http/tcp/upstream_request_test.cc | 3 +- .../tcp_conn_pool_integration_test.cc | 5 +- test/mocks/http/conn_pool.h | 2 + test/mocks/upstream/BUILD | 3 + test/mocks/upstream/cluster_manager.cc | 4 - test/mocks/upstream/cluster_manager.h | 18 - test/mocks/upstream/thread_local_cluster.cc | 9 +- test/mocks/upstream/thread_local_cluster.h | 24 +- test/per_file_coverage.sh | 2 +- test/server/config_validation/BUILD | 13 - .../config_validation/async_client_test.cc | 28 - .../config_validation/cluster_manager_test.cc | 12 +- 84 files changed, 1691 insertions(+), 1564 deletions(-) delete mode 100644 source/server/config_validation/async_client.cc delete mode 100644 source/server/config_validation/async_client.h delete mode 100644 test/server/config_validation/async_client_test.cc diff --git a/include/envoy/upstream/cluster_manager.h b/include/envoy/upstream/cluster_manager.h index 8729458cb69d..b467d0ed52cc 100644 --- a/include/envoy/upstream/cluster_manager.h +++ b/include/envoy/upstream/cluster_manager.h @@ -15,7 +15,6 @@ #include "envoy/config/grpc_mux.h" #include "envoy/config/subscription_factory.h" #include "envoy/grpc/async_client_manager.h" -#include "envoy/http/async_client.h" #include "envoy/http/conn_pool.h" #include "envoy/local_info/local_info.h" #include "envoy/runtime/runtime.h" @@ -222,51 +221,6 @@ class ClusterManager { */ virtual ThreadLocalCluster* getThreadLocalCluster(absl::string_view cluster) PURE; - /** - * Allocate a load balanced HTTP connection pool for a cluster. This is *per-thread* so that - * callers do not need to worry about per thread synchronization. The load balancing policy that - * is used is the one defined on the cluster when it was created. - * - * Can return nullptr if there is no host available in the cluster or if the cluster does not - * exist. - * - * To resolve the protocol to use, we provide the downstream protocol (if one exists). - */ - virtual Http::ConnectionPool::Instance* - httpConnPoolForCluster(const std::string& cluster, ResourcePriority priority, - absl::optional downstream_protocol, - LoadBalancerContext* context) PURE; - - /** - * Allocate a load balanced TCP connection pool for a cluster. This is *per-thread* so that - * callers do not need to worry about per thread synchronization. The load balancing policy that - * is used is the one defined on the cluster when it was created. - * - * Can return nullptr if there is no host available in the cluster or if the cluster does not - * exist. - */ - virtual Tcp::ConnectionPool::Instance* tcpConnPoolForCluster(const std::string& cluster, - ResourcePriority priority, - LoadBalancerContext* context) PURE; - - /** - * Allocate a load balanced TCP connection for a cluster. The created connection is already - * bound to the correct *per-thread* dispatcher, so no further synchronization is needed. The - * load balancing policy that is used is the one defined on the cluster when it was created. - * - * Returns both a connection and the host that backs the connection. Both can be nullptr if there - * is no host available in the cluster. - */ - virtual Host::CreateConnectionData tcpConnForCluster(const std::string& cluster, - LoadBalancerContext* context) PURE; - - /** - * Returns a client that can be used to make async HTTP calls against the given cluster. The - * client may be backed by a connection pool or by a multiplexed connection. The cluster manager - * owns the client. - */ - virtual Http::AsyncClient& httpAsyncClientForCluster(const std::string& cluster) PURE; - /** * Remove a cluster via API. Only clusters added via addOrUpdateCluster() can * be removed in this manner. Statically defined clusters present when Envoy starts cannot be diff --git a/include/envoy/upstream/thread_local_cluster.h b/include/envoy/upstream/thread_local_cluster.h index 2a9dafb9af6a..6c08c194103b 100644 --- a/include/envoy/upstream/thread_local_cluster.h +++ b/include/envoy/upstream/thread_local_cluster.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/common/pure.h" +#include "envoy/http/async_client.h" #include "envoy/upstream/load_balancer.h" #include "envoy/upstream/upstream.h" @@ -32,7 +33,54 @@ class ThreadLocalCluster { * @return LoadBalancer& the backing load balancer. */ virtual LoadBalancer& loadBalancer() PURE; + + /** + * Allocate a load balanced HTTP connection pool for a cluster. This is *per-thread* so that + * callers do not need to worry about per thread synchronization. The load balancing policy that + * is used is the one defined on the cluster when it was created. + * + * @param priority the connection pool priority. + * @param downstream_protocol the downstream protocol (if one exists) to use in protocol + * selection. + * @param context the optional load balancer context. + * @return the connection pool or nullptr if there is no host available in the cluster. + */ + virtual Http::ConnectionPool::Instance* + httpConnPool(ResourcePriority priority, absl::optional downstream_protocol, + LoadBalancerContext* context) PURE; + + /** + * Allocate a load balanced TCP connection pool for a cluster. This is *per-thread* so that + * callers do not need to worry about per thread synchronization. The load balancing policy that + * is used is the one defined on the cluster when it was created. + * + * @param priority the connection pool priority. + * @param context the optional load balancer context. + * @return the connection pool or nullptr if there is no host available in the cluster. + */ + virtual Tcp::ConnectionPool::Instance* tcpConnPool(ResourcePriority priority, + LoadBalancerContext* context) PURE; + + /** + * Allocate a load balanced TCP connection for a cluster. The created connection is already + * bound to the correct *per-thread* dispatcher, so no further synchronization is needed. The + * load balancing policy that is used is the one defined on the cluster when it was created. + * + * @param context the optional load balancer context. + * @return both a connection and the host that backs the connection. Both can be nullptr if there + * is no host available in the cluster. + */ + virtual Host::CreateConnectionData tcpConn(LoadBalancerContext* context) PURE; + + /** + * @return a client that can be used to make async HTTP calls against the given cluster. The + * client may be backed by a connection pool or by a multiplexed connection. The cluster manager + * owns the client. + */ + virtual Http::AsyncClient& httpAsyncClient() PURE; }; +using ThreadLocalClusterOptRef = absl::optional>; + } // namespace Upstream } // namespace Envoy diff --git a/source/common/config/remote_data_fetcher.cc b/source/common/config/remote_data_fetcher.cc index 4723402e4955..c6f8382c5c15 100644 --- a/source/common/config/remote_data_fetcher.cc +++ b/source/common/config/remote_data_fetcher.cc @@ -33,10 +33,16 @@ void RemoteDataFetcher::fetch() { Http::RequestMessagePtr message = Http::Utility::prepareHeaders(uri_); message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Get); ENVOY_LOG(debug, "fetch remote data from [uri = {}]: start", uri_.uri()); - request_ = cm_.httpAsyncClientForCluster(uri_.cluster()) - .send(std::move(message), *this, - Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds( - DurationUtil::durationToMilliseconds(uri_.timeout())))); + const auto thread_local_cluster = cm_.getThreadLocalCluster(uri_.cluster()); + if (thread_local_cluster != nullptr) { + request_ = thread_local_cluster->httpAsyncClient().send( + std::move(message), *this, + Http::AsyncClient::RequestOptions().setTimeout( + std::chrono::milliseconds(DurationUtil::durationToMilliseconds(uri_.timeout())))); + } else { + ENVOY_LOG(debug, "fetch remote data [uri = {}]: no cluster {}", uri_.uri(), uri_.cluster()); + callback_.onFailure(FailureReason::Network); + } } void RemoteDataFetcher::onSuccess(const Http::AsyncClient::Request&, diff --git a/source/common/grpc/async_client_impl.cc b/source/common/grpc/async_client_impl.cc index c855dfd86d85..9527d1dad34a 100644 --- a/source/common/grpc/async_client_impl.cc +++ b/source/common/grpc/async_client_impl.cc @@ -67,13 +67,14 @@ AsyncStreamImpl::AsyncStreamImpl(AsyncClientImpl& parent, absl::string_view serv callbacks_(callbacks), options_(options) {} void AsyncStreamImpl::initialize(bool buffer_body_for_retry) { - if (parent_.cm_.getThreadLocalCluster(parent_.remote_cluster_name_) == nullptr) { + const auto thread_local_cluster = parent_.cm_.getThreadLocalCluster(parent_.remote_cluster_name_); + if (thread_local_cluster == nullptr) { callbacks_.onRemoteClose(Status::WellKnownGrpcStatus::Unavailable, "Cluster not available"); http_reset_ = true; return; } - auto& http_async_client = parent_.cm_.httpAsyncClientForCluster(parent_.remote_cluster_name_); + auto& http_async_client = thread_local_cluster->httpAsyncClient(); dispatcher_ = &http_async_client.dispatcher(); stream_ = http_async_client.start(*this, options_.setBufferBodyForRetry(buffer_body_for_retry)); diff --git a/source/common/http/rest_api_fetcher.cc b/source/common/http/rest_api_fetcher.cc index ef4cca9bcd7b..e65b518d4543 100644 --- a/source/common/http/rest_api_fetcher.cc +++ b/source/common/http/rest_api_fetcher.cc @@ -60,9 +60,14 @@ void RestApiFetcher::refresh() { RequestMessagePtr message(new RequestMessageImpl()); createRequest(*message); message->headers().setHost(remote_cluster_name_); - active_request_ = cm_.httpAsyncClientForCluster(remote_cluster_name_) - .send(std::move(message), *this, - AsyncClient::RequestOptions().setTimeout(request_timeout_)); + const auto thread_local_cluster = cm_.getThreadLocalCluster(remote_cluster_name_); + if (thread_local_cluster != nullptr) { + active_request_ = thread_local_cluster->httpAsyncClient().send( + std::move(message), *this, AsyncClient::RequestOptions().setTimeout(request_timeout_)); + } else { + onFetchFailure(Config::ConfigUpdateFailureReason::ConnectionFailure, nullptr); + requestComplete(); + } } void RestApiFetcher::requestComplete() { diff --git a/source/common/router/shadow_writer_impl.cc b/source/common/router/shadow_writer_impl.cc index d7c826e92f8f..89da4c9ef85b 100644 --- a/source/common/router/shadow_writer_impl.cc +++ b/source/common/router/shadow_writer_impl.cc @@ -16,7 +16,8 @@ void ShadowWriterImpl::shadow(const std::string& cluster, Http::RequestMessagePt // It's possible that the cluster specified in the route configuration no longer exists due // to a CDS removal. Check that it still exists before shadowing. // TODO(mattklein123): Optimally we would have a stat but for now just fix the crashing issue. - if (!cm_.getThreadLocalCluster(cluster)) { + const auto thread_local_cluster = cm_.getThreadLocalCluster(cluster); + if (thread_local_cluster == nullptr) { ENVOY_LOG(debug, "shadow cluster '{}' does not exist", cluster); return; } @@ -29,7 +30,7 @@ void ShadowWriterImpl::shadow(const std::string& cluster, Http::RequestMessagePt ? absl::StrJoin(parts, "-shadow:") : absl::StrCat(request->headers().getHostValue(), "-shadow")); // This is basically fire and forget. We don't handle cancelling. - cm_.httpAsyncClientForCluster(cluster).send(std::move(request), *this, options); + thread_local_cluster->httpAsyncClient().send(std::move(request), *this, options); } } // namespace Router diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index 5b25c48bafff..ac8a61a3f9c7 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -435,7 +435,7 @@ Network::FilterStatus Filter::initializeUpstreamConnection() { } if (!maybeTunnel(*thread_local_cluster, cluster_name)) { - // Either cluster is unknown or there are no healthy hosts. tcpConnPoolForCluster() increments + // Either cluster is unknown or there are no healthy hosts. tcpConnPool() increments // cluster->stats().upstream_cx_none_healthy in the latter case. getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::NoHealthyUpstream); onInitFailure(UpstreamFailureReason::NoHealthyUpstream); diff --git a/source/common/tcp_proxy/upstream.cc b/source/common/tcp_proxy/upstream.cc index 3b8dfec07165..c5bbce249cc7 100644 --- a/source/common/tcp_proxy/upstream.cc +++ b/source/common/tcp_proxy/upstream.cc @@ -139,8 +139,13 @@ TcpConnPool::TcpConnPool(const std::string& cluster_name, Upstream::ClusterManag Upstream::LoadBalancerContext* context, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks) : upstream_callbacks_(upstream_callbacks) { - conn_pool_ = cluster_manager.tcpConnPoolForCluster(cluster_name, - Upstream::ResourcePriority::Default, context); + // TODO(mattklein123): Pass thread local cluster into this function, removing an additional + // map lookup and moving the error handling closer to the source (where it is likely already + // done). + const auto thread_local_cluster = cluster_manager.getThreadLocalCluster(cluster_name); + if (thread_local_cluster != nullptr) { + conn_pool_ = thread_local_cluster->tcpConnPool(Upstream::ResourcePriority::Default, context); + } } TcpConnPool::~TcpConnPool() { @@ -186,8 +191,14 @@ HttpConnPool::HttpConnPool(const std::string& cluster_name, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, Http::CodecClient::Type type) : hostname_(config.hostname()), type_(type), upstream_callbacks_(upstream_callbacks) { - conn_pool_ = cluster_manager.httpConnPoolForCluster( - cluster_name, Upstream::ResourcePriority::Default, absl::nullopt, context); + // TODO(mattklein123): Pass thread local cluster into this function, removing an additional + // map lookup and moving the error handling closer to the source (where it is likely already + // done). + const auto thread_local_cluster = cluster_manager.getThreadLocalCluster(cluster_name); + if (thread_local_cluster != nullptr) { + conn_pool_ = thread_local_cluster->httpConnPool(Upstream::ResourcePriority::Default, + absl::nullopt, context); + } } HttpConnPool::~HttpConnPool() { diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index f171f8b388cf..f7b856380e00 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -858,7 +858,7 @@ ThreadLocalCluster* ClusterManagerImpl::getThreadLocalCluster(absl::string_view } void ClusterManagerImpl::maybePrefetch( - ThreadLocalClusterManagerImpl::ClusterEntryPtr& cluster_entry, + ThreadLocalClusterManagerImpl::ClusterEntry& cluster_entry, std::function pick_prefetch_pool) { // TODO(alyssawilk) As currently implemented, this will always just prefetch // one connection ahead of actually needed connections. @@ -874,63 +874,48 @@ void ClusterManagerImpl::maybePrefetch( // per-upstream prefetch. // // Once we do this, this should loop capped number of times while shouldPrefetch is true. - if (cluster_entry->cluster_info_->peekaheadRatio() > 1.0) { + if (cluster_entry.cluster_info_->peekaheadRatio() > 1.0) { ConnectionPool::Instance* prefetch_pool = pick_prefetch_pool(); if (prefetch_pool) { - prefetch_pool->maybePrefetch(cluster_entry->cluster_info_->peekaheadRatio()); + prefetch_pool->maybePrefetch(cluster_entry.cluster_info_->peekaheadRatio()); } } } Http::ConnectionPool::Instance* -ClusterManagerImpl::httpConnPoolForCluster(const std::string& cluster, ResourcePriority priority, - absl::optional protocol, - LoadBalancerContext* context) { - ThreadLocalClusterManagerImpl& cluster_manager = *tls_; - - auto entry = cluster_manager.thread_local_clusters_.find(cluster); - if (entry == cluster_manager.thread_local_clusters_.end()) { - return nullptr; - } - +ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::httpConnPool( + ResourcePriority priority, absl::optional protocol, + LoadBalancerContext* context) { // Select a host and create a connection pool for it if it does not already exist. - auto ret = entry->second->connPool(priority, protocol, context, false); + auto ret = connPool(priority, protocol, context, false); // Now see if another host should be prefetched. - // httpConnPoolForCluster is called immediately before a call for newStream. newStream doesn't + // httpConnPool is called immediately before a call for newStream. newStream doesn't // have the load balancer context needed to make selection decisions so prefetching must be // performed here in anticipation of the new stream. // TODO(alyssawilk) refactor to have one function call and return a pair, so this invariant is // code-enforced. - maybePrefetch(entry->second, [&entry, &priority, &protocol, &context]() { - return entry->second->connPool(priority, protocol, context, true); + maybePrefetch(*this, [this, &priority, &protocol, &context]() { + return connPool(priority, protocol, context, true); }); return ret; } Tcp::ConnectionPool::Instance* -ClusterManagerImpl::tcpConnPoolForCluster(const std::string& cluster, ResourcePriority priority, - LoadBalancerContext* context) { - ThreadLocalClusterManagerImpl& cluster_manager = *tls_; - - auto entry = cluster_manager.thread_local_clusters_.find(cluster); - if (entry == cluster_manager.thread_local_clusters_.end()) { - return nullptr; - } - +ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::tcpConnPool( + ResourcePriority priority, LoadBalancerContext* context) { // Select a host and create a connection pool for it if it does not already exist. - auto ret = entry->second->tcpConnPool(priority, context, false); + auto ret = tcpConnPool(priority, context, false); - // tcpConnPoolForCluster is called immediately before a call for newConnection. newConnection + // tcpConnPool is called immediately before a call for newConnection. newConnection // doesn't have the load balancer context needed to make selection decisions so prefetching must // be performed here in anticipation of the new connection. // TODO(alyssawilk) refactor to have one function call and return a pair, so this invariant is // code-enforced. // Now see if another host should be prefetched. - maybePrefetch(entry->second, [&entry, &priority, &context]() { - return entry->second->tcpConnPool(priority, context, true); - }); + maybePrefetch(*this, + [this, &priority, &context]() { return tcpConnPool(priority, context, true); }); return ret; } @@ -1008,43 +993,31 @@ void ClusterManagerImpl::postThreadLocalHealthFailure(const HostSharedPtr& host) }); } -Host::CreateConnectionData ClusterManagerImpl::tcpConnForCluster(const std::string& cluster, - LoadBalancerContext* context) { - ThreadLocalClusterManagerImpl& cluster_manager = *tls_; - - auto entry = cluster_manager.thread_local_clusters_.find(cluster); - if (entry == cluster_manager.thread_local_clusters_.end()) { - throw EnvoyException(fmt::format("unknown cluster '{}'", cluster)); - } - - HostConstSharedPtr logical_host = entry->second->lb_->chooseHost(context); +Host::CreateConnectionData ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::tcpConn( + LoadBalancerContext* context) { + HostConstSharedPtr logical_host = lb_->chooseHost(context); if (logical_host) { auto conn_info = logical_host->createConnection( - cluster_manager.thread_local_dispatcher_, nullptr, + parent_.thread_local_dispatcher_, nullptr, context == nullptr ? nullptr : context->upstreamTransportSocketOptions()); - if ((entry->second->cluster_info_->features() & + if ((cluster_info_->features() & ClusterInfo::Features::CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE) && conn_info.connection_ != nullptr) { - auto& conn_map = cluster_manager.host_tcp_conn_map_[logical_host]; + auto& conn_map = parent_.host_tcp_conn_map_[logical_host]; conn_map.emplace(conn_info.connection_.get(), std::make_unique( - cluster_manager, logical_host, *conn_info.connection_)); + parent_, logical_host, *conn_info.connection_)); } return conn_info; } else { - entry->second->cluster_info_->stats().upstream_cx_none_healthy_.inc(); + cluster_info_->stats().upstream_cx_none_healthy_.inc(); return {nullptr, nullptr}; } } -Http::AsyncClient& ClusterManagerImpl::httpAsyncClientForCluster(const std::string& cluster) { - ThreadLocalClusterManagerImpl& cluster_manager = *tls_; - auto entry = cluster_manager.thread_local_clusters_.find(cluster); - if (entry != cluster_manager.thread_local_clusters_.end()) { - return entry->second->http_async_client_; - } else { - throw EnvoyException(fmt::format("unknown cluster '{}'", cluster)); - } +Http::AsyncClient& +ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::httpAsyncClient() { + return http_async_client_; } ClusterUpdateCallbacksHandlePtr diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 013547194ab6..ca55b3fbcdb1 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -254,18 +254,6 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable downstream_protocol, - LoadBalancerContext* context) override; - Tcp::ConnectionPool::Instance* tcpConnPoolForCluster(const std::string& cluster, - ResourcePriority priority, - LoadBalancerContext* context) override; - Host::CreateConnectionData tcpConnForCluster(const std::string& cluster, - LoadBalancerContext* context) override; - Http::AsyncClient& httpAsyncClientForCluster(const std::string& cluster) override; bool removeCluster(const std::string& cluster) override; void shutdown() override { if (resume_cds_ != nullptr) { @@ -409,6 +397,13 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable downstream_protocol, + LoadBalancerContext* context) override; + Tcp::ConnectionPool::Instance* tcpConnPool(ResourcePriority priority, + LoadBalancerContext* context) override; + Host::CreateConnectionData tcpConn(LoadBalancerContext* context) override; + Http::AsyncClient& httpAsyncClient() override; ThreadLocalClusterManagerImpl& parent_; PrioritySetImpl priority_set_; @@ -562,7 +557,7 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable prefetch_pool); ClusterManagerFactory& factory_; diff --git a/source/common/upstream/cluster_update_tracker.cc b/source/common/upstream/cluster_update_tracker.cc index 3f5f6eac3025..5a591f177469 100644 --- a/source/common/upstream/cluster_update_tracker.cc +++ b/source/common/upstream/cluster_update_tracker.cc @@ -7,21 +7,23 @@ ClusterUpdateTracker::ClusterUpdateTracker(ClusterManager& cm, const std::string : cluster_name_(cluster_name), cluster_update_callbacks_handle_(cm.addThreadLocalClusterUpdateCallbacks(*this)) { Upstream::ThreadLocalCluster* cluster = cm.getThreadLocalCluster(cluster_name_); - cluster_info_ = cluster ? cluster->info() : nullptr; + if (cluster != nullptr) { + thread_local_cluster_ = *cluster; + } } void ClusterUpdateTracker::onClusterAddOrUpdate(ThreadLocalCluster& cluster) { if (cluster.info()->name() != cluster_name_) { return; } - cluster_info_ = cluster.info(); + thread_local_cluster_ = cluster; } void ClusterUpdateTracker::onClusterRemoval(const std::string& cluster) { if (cluster != cluster_name_) { return; } - cluster_info_.reset(); + thread_local_cluster_.reset(); } } // namespace Upstream diff --git a/source/common/upstream/cluster_update_tracker.h b/source/common/upstream/cluster_update_tracker.h index b55d1b0d5483..4660faf3c6d9 100644 --- a/source/common/upstream/cluster_update_tracker.h +++ b/source/common/upstream/cluster_update_tracker.h @@ -14,9 +14,7 @@ namespace Upstream { class ClusterUpdateTracker : public ClusterUpdateCallbacks { public: ClusterUpdateTracker(ClusterManager& cm, const std::string& cluster_name); - - bool exists() { return cluster_info_ != nullptr; } - ClusterInfoConstSharedPtr info() { return cluster_info_; } + ThreadLocalClusterOptRef threadLocalCluster() { return thread_local_cluster_; }; // ClusterUpdateCallbacks void onClusterAddOrUpdate(ThreadLocalCluster& cluster) override; @@ -26,7 +24,7 @@ class ClusterUpdateTracker : public ClusterUpdateCallbacks { const std::string cluster_name_; const ClusterUpdateCallbacksHandlePtr cluster_update_callbacks_handle_; - ClusterInfoConstSharedPtr cluster_info_; + ThreadLocalClusterOptRef thread_local_cluster_; }; } // namespace Upstream diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc index b6014d513c19..8651b98ef4b3 100644 --- a/source/extensions/common/wasm/context.cc +++ b/source/extensions/common/wasm/context.cc @@ -892,7 +892,8 @@ WasmResult Context::httpCall(absl::string_view cluster, const Pairs& request_hea return WasmResult::BadArgument; } auto cluster_string = std::string(cluster); - if (clusterManager().getThreadLocalCluster(cluster_string) == nullptr) { + const auto thread_local_cluster = clusterManager().getThreadLocalCluster(cluster_string); + if (thread_local_cluster == nullptr) { return WasmResult::BadArgument; } @@ -928,9 +929,8 @@ WasmResult Context::httpCall(absl::string_view cluster, const Pairs& request_hea Protobuf::RepeatedPtrField hash_policy; hash_policy.Add()->mutable_header()->set_header_name(Http::Headers::get().Host.get()); options.setHashPolicy(hash_policy); - auto http_request = clusterManager() - .httpAsyncClientForCluster(cluster_string) - .send(std::move(message), handler, options); + auto http_request = + thread_local_cluster->httpAsyncClient().send(std::move(message), handler, options); if (!http_request) { http_request_.erase(token); return WasmResult::InternalFailure; diff --git a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc index 6a44b7dcddc4..4663563ada68 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc +++ b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc @@ -222,7 +222,8 @@ void RawHttpClientImpl::check(RequestCallbacks& callbacks, // It's possible that the cluster specified in the filter configuration no longer exists due to a // CDS removal. - if (cm_.getThreadLocalCluster(cluster) == nullptr) { + const auto thread_local_cluster = cm_.getThreadLocalCluster(cluster); + if (thread_local_cluster == nullptr) { // TODO(dio): Add stats related to this. ENVOY_LOG(debug, "ext_authz cluster '{}' does not exist", cluster); callbacks_->onComplete(std::make_unique(errorResponse())); @@ -233,7 +234,7 @@ void RawHttpClientImpl::check(RequestCallbacks& callbacks, .setParentSpan(parent_span) .setChildSpanName(config_->tracingName()); - request_ = cm_.httpAsyncClientForCluster(cluster).send(std::move(message), *this, options); + request_ = thread_local_cluster->httpAsyncClient().send(std::move(message), *this, options); } } diff --git a/source/extensions/filters/http/common/jwks_fetcher.cc b/source/extensions/filters/http/common/jwks_fetcher.cc index 1777c1d094f6..5aadb8c94bdf 100644 --- a/source/extensions/filters/http/common/jwks_fetcher.cc +++ b/source/extensions/filters/http/common/jwks_fetcher.cc @@ -40,8 +40,8 @@ class JwksFetcherImpl : public JwksFetcher, uri_ = &uri; // Check if cluster is configured, fail the request if not. - // Otherwise cm_.httpAsyncClientForCluster will throw exception. - if (cm_.getThreadLocalCluster(uri.cluster()) == nullptr) { + const auto thread_local_cluster = cm_.getThreadLocalCluster(uri.cluster()); + if (thread_local_cluster == nullptr) { ENVOY_LOG(error, "{}: fetch pubkey [uri = {}] failed: [cluster = {}] is not configured", __func__, uri.uri(), uri.cluster()); complete_ = true; @@ -58,8 +58,7 @@ class JwksFetcherImpl : public JwksFetcher, DurationUtil::durationToMilliseconds(uri.timeout()))) .setParentSpan(parent_span) .setChildSpanName("JWT Remote PubKey Fetch"); - request_ = - cm_.httpAsyncClientForCluster(uri.cluster()).send(std::move(message), *this, options); + request_ = thread_local_cluster->httpAsyncClient().send(std::move(message), *this, options); } // HTTP async receive methods diff --git a/source/extensions/filters/http/lua/lua_filter.cc b/source/extensions/filters/http/lua/lua_filter.cc index 82eebdd06241..f8e1e7531916 100644 --- a/source/extensions/filters/http/lua/lua_filter.cc +++ b/source/extensions/filters/http/lua/lua_filter.cc @@ -120,7 +120,8 @@ Http::AsyncClient::Request* makeHttpCall(lua_State* state, Filter& filter, luaL_error(state, "http call timeout must be >= 0"); } - if (filter.clusterManager().getThreadLocalCluster(cluster) == nullptr) { + const auto thread_local_cluster = filter.clusterManager().getThreadLocalCluster(cluster); + if (thread_local_cluster == nullptr) { luaL_error(state, "http call cluster invalid. Must be configured"); } @@ -145,8 +146,7 @@ Http::AsyncClient::Request* makeHttpCall(lua_State* state, Filter& filter, } auto options = Http::AsyncClient::RequestOptions().setTimeout(timeout).setParentSpan(parent_span); - return filter.clusterManager().httpAsyncClientForCluster(cluster).send(std::move(message), - callbacks, options); + return thread_local_cluster->httpAsyncClient().send(std::move(message), callbacks, options); } } // namespace diff --git a/source/extensions/filters/http/oauth2/oauth_client.cc b/source/extensions/filters/http/oauth2/oauth_client.cc index 71db1ce74020..170d4a9a2c98 100644 --- a/source/extensions/filters/http/oauth2/oauth_client.cc +++ b/source/extensions/filters/http/oauth2/oauth_client.cc @@ -48,11 +48,15 @@ void OAuth2ClientImpl::asyncGetAccessToken(const std::string& auth_code, } void OAuth2ClientImpl::dispatchRequest(Http::RequestMessagePtr&& msg) { - in_flight_request_ = - cm_.httpAsyncClientForCluster(uri_.cluster()) - .send(std::move(msg), *this, - Http::AsyncClient::RequestOptions().setTimeout( - std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(uri_, timeout)))); + const auto thread_local_cluster = cm_.getThreadLocalCluster(uri_.cluster()); + if (thread_local_cluster != nullptr) { + in_flight_request_ = thread_local_cluster->httpAsyncClient().send( + std::move(msg), *this, + Http::AsyncClient::RequestOptions().setTimeout( + std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(uri_, timeout)))); + } else { + parent_->sendUnauthorizedResponse(); + } } void OAuth2ClientImpl::onSuccess(const Http::AsyncClient::Request&, diff --git a/source/extensions/filters/http/squash/squash_filter.cc b/source/extensions/filters/http/squash/squash_filter.cc index c372e733b090..084ec4308569 100644 --- a/source/extensions/filters/http/squash/squash_filter.cc +++ b/source/extensions/filters/http/squash/squash_filter.cc @@ -152,10 +152,12 @@ Http::FilterHeadersStatus SquashFilter::decodeHeaders(Http::RequestHeaderMap& he request->body().add(config_->attachmentJson()); is_squashing_ = true; - in_flight_request_ = - cm_.httpAsyncClientForCluster(config_->clusterName()) - .send(std::move(request), create_attachment_callback_, - Http::AsyncClient::RequestOptions().setTimeout(config_->requestTimeout())); + const auto thread_local_cluster = cm_.getThreadLocalCluster(config_->clusterName()); + if (thread_local_cluster != nullptr) { + in_flight_request_ = thread_local_cluster->httpAsyncClient().send( + std::move(request), create_attachment_callback_, + Http::AsyncClient::RequestOptions().setTimeout(config_->requestTimeout())); + } if (in_flight_request_ == nullptr) { ENVOY_LOG(debug, "Squash: can't create request for squash server"); @@ -274,8 +276,12 @@ void SquashFilter::pollForAttachment() { request->headers().setReferencePath(debug_attachment_path_); request->headers().setReferenceHost(SERVER_AUTHORITY); + // TODO(mattklein123): The following code should check to see if the cluster has been removed + // by CDS. This is a preexisting bug so was not fixed in my recent refactor because testing is + // non-trivial. in_flight_request_ = - cm_.httpAsyncClientForCluster(config_->clusterName()) + cm_.getThreadLocalCluster(config_->clusterName()) + ->httpAsyncClient() .send(std::move(request), check_attachment_callback_, Http::AsyncClient::RequestOptions().setTimeout(config_->requestTimeout())); // No need to check if in_flight_request_ is null as onFailure will take care of diff --git a/source/extensions/filters/network/dubbo_proxy/router/router_impl.cc b/source/extensions/filters/network/dubbo_proxy/router/router_impl.cc index 8b2be5132ca3..2d9e96a9d8e7 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/router_impl.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/router_impl.cc @@ -64,8 +64,8 @@ FilterStatus Router::onMessageDecoded(MessageMetadataSharedPtr metadata, Context return FilterStatus::StopIteration; } - Tcp::ConnectionPool::Instance* conn_pool = cluster_manager_.tcpConnPoolForCluster( - route_entry_->clusterName(), Upstream::ResourcePriority::Default, this); + Tcp::ConnectionPool::Instance* conn_pool = + cluster->tcpConnPool(Upstream::ResourcePriority::Default, this); if (!conn_pool) { callbacks_->sendLocalReply( AppException( diff --git a/source/extensions/filters/network/rocketmq_proxy/router/router_impl.cc b/source/extensions/filters/network/rocketmq_proxy/router/router_impl.cc index 15daca9ccbb0..531fadc9e0d4 100644 --- a/source/extensions/filters/network/rocketmq_proxy/router/router_impl.cc +++ b/source/extensions/filters/network/rocketmq_proxy/router/router_impl.cc @@ -106,8 +106,8 @@ void RouterImpl::sendRequestToUpstream(ActiveMessage& active_message) { return; } - Tcp::ConnectionPool::Instance* conn_pool = cluster_manager_.tcpConnPoolForCluster( - cluster_name, Upstream::ResourcePriority::Default, this); + Tcp::ConnectionPool::Instance* conn_pool = + cluster->tcpConnPool(Upstream::ResourcePriority::Default, this); if (!conn_pool) { ENVOY_LOG(warn, "No host available for cluster {}. Opaque: {}", cluster_name, opaque); active_message.onError("No host available"); diff --git a/source/extensions/filters/network/thrift_proxy/router/router_impl.cc b/source/extensions/filters/network/thrift_proxy/router/router_impl.cc index 8470cbb3c634..343e28c51100 100644 --- a/source/extensions/filters/network/thrift_proxy/router/router_impl.cc +++ b/source/extensions/filters/network/thrift_proxy/router/router_impl.cc @@ -268,8 +268,8 @@ FilterStatus Router::messageBegin(MessageMetadataSharedPtr metadata) { passthrough_supported_ = true; } - Tcp::ConnectionPool::Instance* conn_pool = cluster_manager_.tcpConnPoolForCluster( - cluster_name, Upstream::ResourcePriority::Default, this); + Tcp::ConnectionPool::Instance* conn_pool = + cluster->tcpConnPool(Upstream::ResourcePriority::Default, this); if (!conn_pool) { stats_.no_healthy_upstream_.inc(); callbacks_->sendLocalReply( diff --git a/source/extensions/stat_sinks/common/statsd/statsd.cc b/source/extensions/stat_sinks/common/statsd/statsd.cc index ce39592a63bc..e1be68c6fd33 100644 --- a/source/extensions/stat_sinks/common/statsd/statsd.cc +++ b/source/extensions/stat_sinks/common/statsd/statsd.cc @@ -283,8 +283,12 @@ void TcpStatsdSink::TlsSink::write(Buffer::Instance& buffer) { } if (!connection_) { - Upstream::Host::CreateConnectionData info = - parent_.cluster_manager_.tcpConnForCluster(parent_.cluster_info_->name(), nullptr); + const auto thread_local_cluster = + parent_.cluster_manager_.getThreadLocalCluster(parent_.cluster_info_->name()); + Upstream::Host::CreateConnectionData info; + if (thread_local_cluster != nullptr) { + info = thread_local_cluster->tcpConn(nullptr); + } if (!info.connection_) { buffer.drain(buffer.length()); return; diff --git a/source/extensions/tracers/datadog/datadog_tracer_impl.cc b/source/extensions/tracers/datadog/datadog_tracer_impl.cc index 8ccb6fbc4595..62abf628d36d 100644 --- a/source/extensions/tracers/datadog/datadog_tracer_impl.cc +++ b/source/extensions/tracers/datadog/datadog_tracer_impl.cc @@ -99,13 +99,11 @@ void TraceReporter::flushTraces() { ENVOY_LOG(debug, "submitting {} trace(s) to {} with payload size {}", pendingTraces, encoder_->path(), encoder_->payload().size()); - if (collector_cluster_.exists()) { + if (collector_cluster_.threadLocalCluster().has_value()) { Http::AsyncClient::Request* request = - driver_.clusterManager() - .httpAsyncClientForCluster(collector_cluster_.info()->name()) - .send( - std::move(message), *this, - Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(1000U))); + collector_cluster_.threadLocalCluster()->get().httpAsyncClient().send( + std::move(message), *this, + Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(1000U))); if (request) { active_requests_.add(*request); } diff --git a/source/extensions/tracers/lightstep/lightstep_tracer_impl.cc b/source/extensions/tracers/lightstep/lightstep_tracer_impl.cc index 1cb535deb6c5..d82ae4c45453 100644 --- a/source/extensions/tracers/lightstep/lightstep_tracer_impl.cc +++ b/source/extensions/tracers/lightstep/lightstep_tracer_impl.cc @@ -128,15 +128,13 @@ void LightStepDriver::LightStepTransporter::Send(std::unique_ptr(timeout)); serializeGrpcMessage(*report, message->body()); - if (collector_cluster_.exists()) { + if (collector_cluster_.threadLocalCluster().has_value()) { active_report_ = std::move(report); active_callback_ = &callback; - active_cluster_ = collector_cluster_.info(); - active_request_ = driver_.clusterManager() - .httpAsyncClientForCluster(collector_cluster_.info()->name()) - .send(std::move(message), *this, - Http::AsyncClient::RequestOptions().setTimeout( - std::chrono::milliseconds(timeout))); + active_cluster_ = collector_cluster_.threadLocalCluster()->get().info(); + active_request_ = collector_cluster_.threadLocalCluster()->get().httpAsyncClient().send( + std::move(message), *this, + Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(timeout))); } else { ENVOY_LOG(debug, "collector cluster '{}' does not exist", driver_.cluster()); driver_.tracerStats().reports_skipped_no_cluster_.inc(); diff --git a/source/extensions/tracers/zipkin/zipkin_tracer_impl.cc b/source/extensions/tracers/zipkin/zipkin_tracer_impl.cc index c7f2a0382f06..badecded9a15 100644 --- a/source/extensions/tracers/zipkin/zipkin_tracer_impl.cc +++ b/source/extensions/tracers/zipkin/zipkin_tracer_impl.cc @@ -193,13 +193,11 @@ void ReporterImpl::flushSpans() { const uint64_t timeout = driver_.runtime().snapshot().getInteger("tracing.zipkin.request_timeout", 5000U); - if (collector_cluster_.exists()) { + if (collector_cluster_.threadLocalCluster().has_value()) { Http::AsyncClient::Request* request = - driver_.clusterManager() - .httpAsyncClientForCluster(collector_cluster_.info()->name()) - .send(std::move(message), *this, - Http::AsyncClient::RequestOptions().setTimeout( - std::chrono::milliseconds(timeout))); + collector_cluster_.threadLocalCluster()->get().httpAsyncClient().send( + std::move(message), *this, + Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(timeout))); if (request) { active_requests_.add(*request); } diff --git a/source/extensions/upstreams/http/http/upstream_request.h b/source/extensions/upstreams/http/http/upstream_request.h index 21de9e79275e..c2c2755f31fc 100644 --- a/source/extensions/upstreams/http/http/upstream_request.h +++ b/source/extensions/upstreams/http/http/upstream_request.h @@ -24,8 +24,14 @@ class HttpConnPool : public Router::GenericConnPool, public Envoy::Http::Connect absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) { ASSERT(!is_connect); - conn_pool_ = cm.httpConnPoolForCluster(route_entry.clusterName(), route_entry.priority(), - downstream_protocol, ctx); + // TODO(mattklein123): Pass thread local cluster into this function, removing an additional + // map lookup and moving the error handling closer to the source (where it is likely already + // done). + const auto thread_local_cluster = cm.getThreadLocalCluster(route_entry.clusterName()); + if (thread_local_cluster != nullptr) { + conn_pool_ = + thread_local_cluster->httpConnPool(route_entry.priority(), downstream_protocol, ctx); + } } ~HttpConnPool() override { ASSERT(conn_pool_stream_handle_ == nullptr, "conn_pool_stream_handle not null"); diff --git a/source/extensions/upstreams/http/tcp/upstream_request.h b/source/extensions/upstreams/http/tcp/upstream_request.h index 4623ec44f5f0..1b9f6a238680 100644 --- a/source/extensions/upstreams/http/tcp/upstream_request.h +++ b/source/extensions/upstreams/http/tcp/upstream_request.h @@ -24,8 +24,13 @@ class TcpConnPool : public Router::GenericConnPool, public Envoy::Tcp::Connectio TcpConnPool(Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, absl::optional, Upstream::LoadBalancerContext* ctx) { ASSERT(is_connect); - conn_pool_ = cm.tcpConnPoolForCluster(route_entry.clusterName(), - Upstream::ResourcePriority::Default, ctx); + // TODO(mattklein123): Pass thread local cluster into this function, removing an additional + // map lookup and moving the error handling closer to the source (where it is likely already + // done). + const auto thread_local_cluster = cm.getThreadLocalCluster(route_entry.clusterName()); + if (thread_local_cluster != nullptr) { + conn_pool_ = thread_local_cluster->tcpConnPool(Upstream::ResourcePriority::Default, ctx); + } } void newStream(Router::GenericConnectionPoolCallbacks* callbacks) override { callbacks_ = callbacks; diff --git a/source/server/config_validation/BUILD b/source/server/config_validation/BUILD index b6c57bbc824a..cabe63058307 100644 --- a/source/server/config_validation/BUILD +++ b/source/server/config_validation/BUILD @@ -27,24 +27,11 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "async_client_lib", - srcs = ["async_client.cc"], - hdrs = ["async_client.h"], - deps = [ - ":dispatcher_lib", - "//include/envoy/http:async_client_interface", - "//include/envoy/http:message_interface", - "//source/common/common:assert_lib", - ], -) - envoy_cc_library( name = "cluster_manager_lib", srcs = ["cluster_manager.cc"], hdrs = ["cluster_manager.h"], deps = [ - ":async_client_lib", "//include/envoy/upstream:cluster_manager_interface", "//source/common/common:utility_lib", "//source/common/http:context_lib", diff --git a/source/server/config_validation/async_client.cc b/source/server/config_validation/async_client.cc deleted file mode 100644 index 098a5fd1f4a9..000000000000 --- a/source/server/config_validation/async_client.cc +++ /dev/null @@ -1,19 +0,0 @@ -#include "server/config_validation/async_client.h" - -namespace Envoy { -namespace Http { - -ValidationAsyncClient::ValidationAsyncClient(Api::Api& api, Event::TimeSystem& time_system) - : dispatcher_("validation_async_client", api, time_system) {} - -AsyncClient::Request* ValidationAsyncClient::send(RequestMessagePtr&&, Callbacks&, - const RequestOptions&) { - return nullptr; -} - -AsyncClient::Stream* ValidationAsyncClient::start(StreamCallbacks&, const StreamOptions&) { - return nullptr; -} - -} // namespace Http -} // namespace Envoy diff --git a/source/server/config_validation/async_client.h b/source/server/config_validation/async_client.h deleted file mode 100644 index c665dc49d1c8..000000000000 --- a/source/server/config_validation/async_client.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -#include "envoy/http/async_client.h" -#include "envoy/http/message.h" - -#include "common/common/assert.h" - -#include "server/config_validation/dispatcher.h" - -namespace Envoy { -namespace Http { - -/** - * Config-validation-only implementation of AsyncClient. Both methods on AsyncClient are allowed to - * return nullptr if the request can't be created, so that's what the ValidationAsyncClient does in - * all cases. - */ -class ValidationAsyncClient : public AsyncClient { -public: - ValidationAsyncClient(Api::Api& api, Event::TimeSystem& time_system); - - // Http::AsyncClient - AsyncClient::Request* send(RequestMessagePtr&& request, Callbacks& callbacks, - const RequestOptions&) override; - - AsyncClient::Stream* start(StreamCallbacks& callbacks, const StreamOptions&) override; - - Event::Dispatcher& dispatcher() override { return dispatcher_; } - -private: - Event::ValidationDispatcher dispatcher_; -}; - -} // namespace Http -} // namespace Envoy diff --git a/source/server/config_validation/cluster_manager.cc b/source/server/config_validation/cluster_manager.cc index 025500e77f18..6295b5a67734 100644 --- a/source/server/config_validation/cluster_manager.cc +++ b/source/server/config_validation/cluster_manager.cc @@ -12,8 +12,7 @@ ClusterManagerPtr ValidationClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { return std::make_unique( bootstrap, *this, stats_, tls_, runtime_, local_info_, log_manager_, main_thread_dispatcher_, - admin_, validation_context_, api_, http_context_, grpc_context_, router_context_, - time_system_); + admin_, validation_context_, api_, http_context_, grpc_context_, router_context_); } CdsApiPtr @@ -25,32 +24,5 @@ ValidationClusterManagerFactory::createCds(const envoy::config::core::v3::Config return nullptr; } -ValidationClusterManager::ValidationClusterManager( - const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, - Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, - const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, - Event::Dispatcher& main_thread_dispatcher, Server::Admin& admin, - ProtobufMessage::ValidationContext& validation_context, Api::Api& api, - Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, - Event::TimeSystem& time_system) - : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, - main_thread_dispatcher, admin, validation_context, api, http_context, - grpc_context, router_context), - async_client_(api, time_system) {} - -Http::ConnectionPool::Instance* ValidationClusterManager::httpConnPoolForCluster( - const std::string&, ResourcePriority, absl::optional, LoadBalancerContext*) { - return nullptr; -} - -Host::CreateConnectionData ValidationClusterManager::tcpConnForCluster(const std::string&, - LoadBalancerContext*) { - return Host::CreateConnectionData{nullptr, nullptr}; -} - -Http::AsyncClient& ValidationClusterManager::httpAsyncClientForCluster(const std::string&) { - return async_client_; -} - } // namespace Upstream } // namespace Envoy diff --git a/source/server/config_validation/cluster_manager.h b/source/server/config_validation/cluster_manager.h index 22919468e3ec..7a0b8047883e 100644 --- a/source/server/config_validation/cluster_manager.h +++ b/source/server/config_validation/cluster_manager.h @@ -8,8 +8,6 @@ #include "common/http/context_impl.h" #include "common/upstream/cluster_manager_impl.h" -#include "server/config_validation/async_client.h" - namespace Envoy { namespace Upstream { @@ -28,13 +26,12 @@ class ValidationClusterManagerFactory : public ProdClusterManagerFactory { const LocalInfo::LocalInfo& local_info, Secret::SecretManager& secret_manager, ProtobufMessage::ValidationContext& validation_context, Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, - AccessLog::AccessLogManager& log_manager, Singleton::Manager& singleton_manager, - Event::TimeSystem& time_system) + AccessLog::AccessLogManager& log_manager, Singleton::Manager& singleton_manager) : ProdClusterManagerFactory(admin, runtime, stats, tls, dns_resolver, ssl_context_manager, main_thread_dispatcher, local_info, secret_manager, validation_context, api, http_context, grpc_context, router_context, log_manager, singleton_manager), - grpc_context_(grpc_context), router_context_(router_context), time_system_(time_system) {} + grpc_context_(grpc_context), router_context_(router_context) {} ClusterManagerPtr clusterManagerFromProto(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) override; @@ -47,7 +44,6 @@ class ValidationClusterManagerFactory : public ProdClusterManagerFactory { private: Grpc::Context& grpc_context_; Router::Context& router_context_; - Event::TimeSystem& time_system_; }; /** @@ -55,27 +51,14 @@ class ValidationClusterManagerFactory : public ProdClusterManagerFactory { */ class ValidationClusterManager : public ClusterManagerImpl { public: - ValidationClusterManager(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - ClusterManagerFactory& factory, Stats::Store& stats, - ThreadLocal::Instance& tls, Runtime::Loader& runtime, - const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Event::Dispatcher& dispatcher, - Server::Admin& admin, - ProtobufMessage::ValidationContext& validation_context, Api::Api& api, - Http::Context& http_context, Grpc::Context& grpc_context, - Router::Context& router_context, Event::TimeSystem& time_system); - - Http::ConnectionPool::Instance* httpConnPoolForCluster(const std::string&, ResourcePriority, - absl::optional, - LoadBalancerContext*) override; - Host::CreateConnectionData tcpConnForCluster(const std::string&, LoadBalancerContext*) override; - Http::AsyncClient& httpAsyncClientForCluster(const std::string&) override; + using ClusterManagerImpl::ClusterManagerImpl; -private: - // ValidationAsyncClient always returns null on send() and start(), so it has - // no internal state -- we might as well just keep one and hand out references - // to it. - Http::ValidationAsyncClient async_client_; + ThreadLocalCluster* getThreadLocalCluster(absl::string_view) override { + // A thread local cluster is never guaranteed to exist (because it is not created until warmed) + // so all calling code must have error handling for this case. Returning nullptr here prevents + // any calling code creating real outbound networking during validation. + return nullptr; + } }; } // namespace Upstream diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index a03ff99684d3..901230bf2747 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -101,7 +101,7 @@ void ValidationInstance::initialize(const Options& options, cluster_manager_factory_ = std::make_unique( admin(), runtime(), stats(), threadLocal(), dnsResolver(), sslContextManager(), dispatcher(), localInfo(), *secret_manager_, messageValidationContext(), *api_, http_context_, - grpc_context_, router_context_, accessLogManager(), singletonManager(), time_system_); + grpc_context_, router_context_, accessLogManager(), singletonManager()); config_.initialize(bootstrap, *this, *cluster_manager_factory_); runtime().initialize(clusterManager()); clusterManager().setInitializedCb([this]() -> void { init_manager_.initialize(init_watcher_); }); diff --git a/test/common/config/datasource_test.cc b/test/common/config/datasource_test.cc index 843f6e6ec1a4..70860a7fefd2 100644 --- a/test/common/config/datasource_test.cc +++ b/test/common/config/datasource_test.cc @@ -33,7 +33,7 @@ class AsyncDataSourceTest : public testing::Test { Event::MockDispatcher dispatcher_; Event::MockTimer* retry_timer_; Event::TimerCb retry_timer_cb_; - NiceMock request_{&cm_.async_client_}; + NiceMock request_{&cm_.thread_local_cluster_.async_client_}; Config::DataSource::LocalAsyncDataProviderPtr local_data_provider_; Config::DataSource::RemoteAsyncDataProviderPtr remote_data_provider_; @@ -53,15 +53,21 @@ class AsyncDataSourceTest : public testing::Test { return retry_timer_; })); - EXPECT_CALL(cm_, httpAsyncClientForCluster("cluster_1")) + EXPECT_CALL(*retry_timer_, disableTimer()); + if (!func) { + return; + } + + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) .Times(AtLeast(1)) - .WillRepeatedly(ReturnRef(cm_.async_client_)); + .WillRepeatedly(ReturnRef(cm_.thread_local_cluster_.async_client_)); - EXPECT_CALL(*retry_timer_, disableTimer()); if (num_retries == 1) { - EXPECT_CALL(cm_.async_client_, send_(_, _, _)).Times(AtLeast(1)).WillRepeatedly(Invoke(func)); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) + .Times(AtLeast(1)) + .WillRepeatedly(Invoke(func)); } else { - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .Times(num_retries) .WillRepeatedly(Invoke(func)); } @@ -99,6 +105,42 @@ TEST_F(AsyncDataSourceTest, LoadLocalDataSource) { EXPECT_EQ(async_data, "xxxxxx"); } +TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceNoCluster) { + AsyncDataSourcePb config; + + std::string yaml = R"EOF( + remote: + http_uri: + uri: https://example.com/data + cluster: cluster_1 + timeout: 1s + sha256: + xxxxxx + )EOF"; + TestUtility::loadFromYamlAndValidate(yaml, config); + EXPECT_TRUE(config.has_remote()); + + initialize(nullptr); + + std::string async_data = "non-empty"; + remote_data_provider_ = std::make_unique( + cm_, init_manager_, config.remote(), dispatcher_, random_, true, + [&](const std::string& data) { + EXPECT_EQ(init_manager_.state(), Init::Manager::State::Initializing); + EXPECT_EQ(data, EMPTY_STRING); + async_data = data; + }); + + EXPECT_CALL(init_manager_, state()).WillOnce(Return(Init::Manager::State::Initializing)); + EXPECT_CALL(init_watcher_, ready()); + EXPECT_CALL(*retry_timer_, enableTimer(_, _)) + .WillOnce(Invoke( + [&](const std::chrono::milliseconds&, const ScopeTrackedObject*) { retry_timer_cb_(); })); + init_target_handle_->initialize(init_watcher_); + + EXPECT_EQ(async_data, EMPTY_STRING); +} + TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceReturnFailure) { AsyncDataSourcePb config; @@ -114,6 +156,7 @@ TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceReturnFailure) { TestUtility::loadFromYamlAndValidate(yaml, config); EXPECT_TRUE(config.has_remote()); + cm_.initializeThreadLocalClusters({"cluster_1"}); initialize([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { callbacks.onFailure(request_, Envoy::Http::AsyncClient::FailureReason::Reset); @@ -154,6 +197,7 @@ TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceSuccessWith503) { TestUtility::loadFromYamlAndValidate(yaml, config); EXPECT_TRUE(config.has_remote()); + cm_.initializeThreadLocalClusters({"cluster_1"}); initialize([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { callbacks.onSuccess( @@ -196,6 +240,7 @@ TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceSuccessWithEmptyBody) { TestUtility::loadFromYamlAndValidate(yaml, config); EXPECT_TRUE(config.has_remote()); + cm_.initializeThreadLocalClusters({"cluster_1"}); initialize([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { callbacks.onSuccess( @@ -240,6 +285,7 @@ TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceSuccessIncorrectSha256) { const std::string body = "hello world"; + cm_.initializeThreadLocalClusters({"cluster_1"}); initialize([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { Http::ResponseMessagePtr response(new Http::ResponseMessageImpl( @@ -284,6 +330,7 @@ TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceSuccess) { TestUtility::loadFromYamlAndValidate(yaml, config); EXPECT_TRUE(config.has_remote()); + cm_.initializeThreadLocalClusters({"cluster_1"}); const std::string body = "hello world"; initialize([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -326,6 +373,7 @@ TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceDoNotAllowEmpty) { TestUtility::loadFromYamlAndValidate(yaml, config); EXPECT_TRUE(config.has_remote()); + cm_.initializeThreadLocalClusters({"cluster_1"}); initialize([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { callbacks.onSuccess( @@ -367,6 +415,7 @@ TEST_F(AsyncDataSourceTest, DatasourceReleasedBeforeFetchingData) { TestUtility::loadFromYamlAndValidate(yaml, config); EXPECT_TRUE(config.has_remote()); + cm_.initializeThreadLocalClusters({"cluster_1"}); initialize([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { Http::ResponseMessagePtr response(new Http::ResponseMessageImpl( @@ -411,6 +460,7 @@ TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceWithRetry) { TestUtility::loadFromYamlAndValidate(yaml, config); EXPECT_TRUE(config.has_remote()); + cm_.initializeThreadLocalClusters({"cluster_1"}); const std::string body = "hello world"; int num_retries = 3; @@ -439,7 +489,7 @@ TEST_F(AsyncDataSourceTest, LoadRemoteDataSourceWithRetry) { EXPECT_CALL(*retry_timer_, enableTimer(_, _)) .WillRepeatedly(Invoke([&](const std::chrono::milliseconds&, const ScopeTrackedObject*) { if (--num_retries == 0) { - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(Invoke( [&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { diff --git a/test/common/config/http_subscription_impl_test.cc b/test/common/config/http_subscription_impl_test.cc index 33e54fb33162..e0fb0a8ece98 100644 --- a/test/common/config/http_subscription_impl_test.cc +++ b/test/common/config/http_subscription_impl_test.cc @@ -10,6 +10,19 @@ namespace { class HttpSubscriptionImplTest : public testing::Test, public HttpSubscriptionTestHarness {}; +// Validate that we start the retry timer when there is no cluster. +TEST_F(HttpSubscriptionImplTest, NoCluster) { + ON_CALL(cm_, getThreadLocalCluster("eds_cluster")).WillByDefault(Return(nullptr)); + EXPECT_CALL(callbacks_, + onConfigUpdateFailed(Envoy::Config::ConfigUpdateFailureReason::ConnectionFailure, _)) + .Times(0); + EXPECT_CALL(random_gen_, random()).WillOnce(Return(0)); + EXPECT_CALL(*timer_, enableTimer(_, _)); + version_ = ""; + cluster_names_ = {"cluster0", "cluster1"}; + subscription_->start({"cluster0", "cluster1"}); +} + // Validate that the client can recover from a remote fetch failure. TEST_F(HttpSubscriptionImplTest, OnRequestReset) { startSubscription({"cluster0", "cluster1"}); diff --git a/test/common/config/http_subscription_test_harness.h b/test/common/config/http_subscription_test_harness.h index 89826a049b4b..91ab6905a8b8 100644 --- a/test/common/config/http_subscription_test_harness.h +++ b/test/common/config/http_subscription_test_harness.h @@ -28,6 +28,7 @@ #include "gtest/gtest.h" using testing::_; +using testing::AtLeast; using testing::Invoke; using testing::Return; @@ -41,13 +42,15 @@ class HttpSubscriptionTestHarness : public SubscriptionTestHarness { HttpSubscriptionTestHarness(std::chrono::milliseconds init_fetch_timeout) : method_descriptor_(Protobuf::DescriptorPool::generated_pool()->FindMethodByName( "envoy.api.v2.EndpointDiscoveryService.FetchEndpoints")), - timer_(new Event::MockTimer()), http_request_(&cm_.async_client_) { + timer_(new Event::MockTimer()), http_request_(&cm_.thread_local_cluster_.async_client_) { node_.set_id("fo0"); EXPECT_CALL(local_info_, node()).WillOnce(testing::ReturnRef(node_)); EXPECT_CALL(dispatcher_, createTimer_(_)).WillOnce(Invoke([this](Event::TimerCb timer_cb) { timer_cb_ = timer_cb; return timer_; })); + cm_.initializeThreadLocalClusters({"eds_cluster"}); + EXPECT_CALL(cm_, getThreadLocalCluster("eds_cluster")).Times(AtLeast(0)); subscription_ = std::make_unique( local_info_, cm_, "eds_cluster", dispatcher_, random_gen_, std::chrono::milliseconds(1), std::chrono::milliseconds(1000), *method_descriptor_, @@ -65,8 +68,8 @@ class HttpSubscriptionTestHarness : public SubscriptionTestHarness { void expectSendMessage(const std::set& cluster_names, const std::string& version, bool expect_node = false) override { UNREFERENCED_PARAMETER(expect_node); - EXPECT_CALL(cm_, httpAsyncClientForCluster("eds_cluster")); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(Invoke([this, cluster_names, version](Http::RequestMessagePtr& request, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) { diff --git a/test/common/config/subscription_factory_impl_test.cc b/test/common/config/subscription_factory_impl_test.cc index 81f68727a7a8..0a92ab644493 100644 --- a/test/common/config/subscription_factory_impl_test.cc +++ b/test/common/config/subscription_factory_impl_test.cc @@ -38,7 +38,8 @@ namespace { class SubscriptionFactoryTest : public testing::Test { public: SubscriptionFactoryTest() - : http_request_(&cm_.async_client_), api_(Api::createApiForTest(stats_store_, random_)), + : http_request_(&cm_.thread_local_cluster_.async_client_), + api_(Api::createApiForTest(stats_store_, random_)), subscription_factory_(local_info_, dispatcher_, cm_, validation_visitor_, *api_, runtime_) { } @@ -250,9 +251,11 @@ TEST_F(SubscriptionFactoryTest, HttpSubscriptionCustomRequestTimeout) { primary_clusters.insert("static_cluster"); EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); EXPECT_CALL(dispatcher_, createTimer_(_)).Times(2); - EXPECT_CALL(cm_, httpAsyncClientForCluster("static_cluster")); + cm_.initializeThreadLocalClusters({"static_cluster"}); + EXPECT_CALL(cm_, getThreadLocalCluster("static_cluster")); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()); EXPECT_CALL( - cm_.async_client_, + cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(5000)))); subscriptionFromConfigSource(config)->start({"static_cluster"}); } @@ -268,8 +271,10 @@ TEST_F(SubscriptionFactoryTest, HttpSubscription) { primary_clusters.insert("static_cluster"); EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); EXPECT_CALL(dispatcher_, createTimer_(_)).Times(2); - EXPECT_CALL(cm_, httpAsyncClientForCluster("static_cluster")); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + cm_.initializeThreadLocalClusters({"static_cluster"}); + EXPECT_CALL(cm_, getThreadLocalCluster("static_cluster")); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(Invoke([this](Http::RequestMessagePtr& request, Http::AsyncClient::Callbacks&, const Http::AsyncClient::RequestOptions&) { EXPECT_EQ("POST", request->headers().getMethodValue()); diff --git a/test/common/grpc/async_client_impl_test.cc b/test/common/grpc/async_client_impl_test.cc index 251ea3144cac..2305c706e5e5 100644 --- a/test/common/grpc/async_client_impl_test.cc +++ b/test/common/grpc/async_client_impl_test.cc @@ -35,7 +35,7 @@ class EnvoyAsyncClientImplTest : public testing::Test { grpc_client_ = std::make_unique(cm_, config, test_time_.timeSystem()); cm_.initializeThreadLocalClusters({"test_cluster"}); - ON_CALL(cm_, httpAsyncClientForCluster("test_cluster")).WillByDefault(ReturnRef(http_client_)); + ON_CALL(cm_.thread_local_cluster_, httpAsyncClient()).WillByDefault(ReturnRef(http_client_)); } const Protobuf::MethodDescriptor* method_descriptor_; @@ -77,8 +77,7 @@ TEST_F(EnvoyAsyncClientImplTest, HostIsOverrideByConfig) { config.mutable_envoy_grpc()->set_authority("demo.com"); grpc_client_ = std::make_unique(cm_, config, test_time_.timeSystem()); - EXPECT_CALL(cm_, httpAsyncClientForCluster("test_cluster")) - .WillRepeatedly(ReturnRef(http_client_)); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).WillRepeatedly(ReturnRef(http_client_)); NiceMock> grpc_callbacks; Http::AsyncClient::StreamCallbacks* http_callbacks; diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index b1fccb6474f5..5c86df4d8b84 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -46,6 +46,7 @@ #include "test/test_common/utility.h" using testing::_; +using testing::AtLeast; using testing::Eq; using testing::Invoke; using testing::InvokeWithoutArgs; @@ -290,30 +291,28 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { client_connection_ = std::make_unique( *dispatcher_, fake_upstream_->localAddress(), nullptr, std::move(async_client_transport_socket_), nullptr); - ON_CALL(*mock_cluster_info_, connectTimeout()) + ON_CALL(*cm_.thread_local_cluster_.cluster_.info_, connectTimeout()) .WillByDefault(Return(std::chrono::milliseconds(10000))); - EXPECT_CALL(*mock_cluster_info_, name()).WillRepeatedly(ReturnRef(fake_cluster_name_)); - EXPECT_CALL(cm_, getThreadLocalCluster(_)).WillRepeatedly(Return(&thread_local_cluster_)); - EXPECT_CALL(thread_local_cluster_, info()).WillRepeatedly(Return(cluster_info_ptr_)); + cm_.initializeThreadLocalClusters({"fake_cluster"}); + EXPECT_CALL(cm_, getThreadLocalCluster("fake_cluster")).Times(AtLeast(1)); Upstream::MockHost::MockCreateConnectionData connection_data{client_connection_.release(), host_description_ptr_}; EXPECT_CALL(*mock_host_, createConnection_(_, _)).WillRepeatedly(Return(connection_data)); - EXPECT_CALL(*mock_host_, cluster()).WillRepeatedly(ReturnRef(*cluster_info_ptr_)); + EXPECT_CALL(*mock_host_, cluster()) + .WillRepeatedly(ReturnRef(*cm_.thread_local_cluster_.cluster_.info_)); EXPECT_CALL(*mock_host_description_, locality()).WillRepeatedly(ReturnRef(host_locality_)); http_conn_pool_ = Http::Http2::allocateConnPool(*dispatcher_, random_, host_ptr_, Upstream::ResourcePriority::Default, nullptr, nullptr, state_); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillRepeatedly(Return(http_conn_pool_.get())); http_async_client_ = std::make_unique( - cluster_info_ptr_, *stats_store_, *dispatcher_, local_info_, cm_, runtime_, random_, - std::move(shadow_writer_ptr_), http_context_, router_context_); - EXPECT_CALL(cm_, httpAsyncClientForCluster(fake_cluster_name_)) + cm_.thread_local_cluster_.cluster_.info_, *stats_store_, *dispatcher_, local_info_, cm_, + runtime_, random_, std::move(shadow_writer_ptr_), http_context_, router_context_); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) .WillRepeatedly(ReturnRef(*http_async_client_)); - EXPECT_CALL(cm_, getThreadLocalCluster(Eq(fake_cluster_name_))) - .WillRepeatedly(Return(&thread_local_cluster_)); envoy::config::core::v3::GrpcService config; - config.mutable_envoy_grpc()->set_cluster_name(fake_cluster_name_); + config.mutable_envoy_grpc()->set_cluster_name("fake_cluster"); fillServiceWideInitialMetadata(config); return std::make_unique(cm_, config, dispatcher_->timeSource()); } @@ -376,7 +375,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { EXPECT_CALL(active_span, spawnChild_(_, "async fake_cluster egress", _)) .WillOnce(Return(request->child_span_)); EXPECT_CALL(*request->child_span_, - setTag(Eq(Tracing::Tags::get().UpstreamCluster), Eq(fake_cluster_name_))); + setTag(Eq(Tracing::Tags::get().UpstreamCluster), Eq("fake_cluster"))); EXPECT_CALL(*request->child_span_, setTag(Eq(Tracing::Tags::get().Component), Eq(Tracing::Tags::get().Proxy))); EXPECT_CALL(*request->child_span_, injectContext(_)); @@ -462,11 +461,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { // Fake/mock infrastructure for Grpc::AsyncClientImpl upstream. Upstream::ClusterConnectivityState state_; Network::TransportSocketPtr async_client_transport_socket_{new Network::RawBufferSocket()}; - const std::string fake_cluster_name_{"fake_cluster"}; Upstream::MockClusterManager cm_; - Upstream::MockClusterInfo* mock_cluster_info_ = new NiceMock(); - Upstream::ClusterInfoConstSharedPtr cluster_info_ptr_{mock_cluster_info_}; - Upstream::MockThreadLocalCluster thread_local_cluster_; NiceMock local_info_; Runtime::MockLoader runtime_; Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{test_time_.timeSystem()}; diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index a739a1836ddb..c9fbc8abf617 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -50,7 +50,7 @@ class AsyncClientImplTest : public testing::Test { message_->headers().setMethod("GET"); message_->headers().setHost("host"); message_->headers().setPath("/"); - ON_CALL(*cm_.conn_pool_.host_, locality()) + ON_CALL(*cm_.thread_local_cluster_.conn_pool_.host_, locality()) .WillByDefault(ReturnRef(envoy::config::core::v3::Locality().default_instance())); cm_.initializeThreadLocalClusters({"fake_cluster"}); } @@ -118,10 +118,11 @@ class AsyncClientImplTracingTest : public AsyncClientImplTest { TEST_F(AsyncClientImplTest, BasicStream) { Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -162,10 +163,11 @@ TEST_F(AsyncClientImplTest, Basic) { message_->body().add("test body"); Buffer::Instance& data = message_->body(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -200,10 +202,11 @@ TEST_F(AsyncClientImplTracingTest, Basic) { message_->body().add("test body"); Buffer::Instance& data = message_->body(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -247,10 +250,11 @@ TEST_F(AsyncClientImplTracingTest, BasicNamedChildSpan) { message_->body().add("test body"); Buffer::Instance& data = message_->body(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -293,20 +297,21 @@ TEST_F(AsyncClientImplTest, BasicHashPolicy) { message_->body().add("test body"); Buffer::Instance& data = message_->body(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, auto, + Invoke([&](Upstream::ResourcePriority, auto, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { // this is the hash of :path header value "/" EXPECT_EQ(16761507700594825962UL, context->computeHashKey().value()); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); TestRequestHeaderMapImpl copy(message_->headers()); @@ -340,10 +345,11 @@ TEST_F(AsyncClientImplTest, Retry) { message_->body().add("test body"); Buffer::Instance& data = message_->body(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -362,10 +368,11 @@ TEST_F(AsyncClientImplTest, Retry) { response_decoder_->decodeHeaders(std::move(response_headers), true); // Retry request. - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -385,10 +392,11 @@ TEST_F(AsyncClientImplTest, RetryWithStream) { .WillByDefault(Return(true)); Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -410,10 +418,11 @@ TEST_F(AsyncClientImplTest, RetryWithStream) { response_decoder_->decodeHeaders(std::move(response_headers), true); // Retry request. - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -433,10 +442,11 @@ TEST_F(AsyncClientImplTest, MultipleStreams) { // Start stream 1 Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -459,10 +469,11 @@ TEST_F(AsyncClientImplTest, MultipleStreams) { ResponseDecoder* response_decoder2{}; MockAsyncClientStreamCallbacks stream_callbacks2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder2, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder2 = &decoder; return nullptr; })); @@ -493,10 +504,11 @@ TEST_F(AsyncClientImplTest, MultipleRequests) { message_->body().add("test body"); Buffer::Instance& data = message_->body(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -513,10 +525,11 @@ TEST_F(AsyncClientImplTest, MultipleRequests) { NiceMock stream_encoder2; ResponseDecoder* response_decoder2{}; MockAsyncClientCallbacks callbacks2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder2, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder2 = &decoder; return nullptr; })); @@ -531,10 +544,11 @@ TEST_F(AsyncClientImplTest, MultipleRequests) { NiceMock stream_encoder3; ResponseDecoder* response_decoder3{}; MockAsyncClientCallbacks callbacks3; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder3, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder3, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder3 = &decoder; return nullptr; })); @@ -581,10 +595,11 @@ TEST_F(AsyncClientImplTest, StreamAndRequest) { message_->body().add("test body"); Buffer::Instance& data = message_->body(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -600,10 +615,11 @@ TEST_F(AsyncClientImplTest, StreamAndRequest) { NiceMock stream_encoder2; ResponseDecoder* response_decoder2{}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder2, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder2 = &decoder; return nullptr; })); @@ -639,10 +655,11 @@ TEST_F(AsyncClientImplTest, StreamWithTrailers) { HttpTestUtility::addDefaultHeaders(headers); TestRequestTrailerMapImpl trailers{{"some", "request_trailer"}}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -673,10 +690,11 @@ TEST_F(AsyncClientImplTest, Trailers) { message_->body().add("test body"); Buffer::Instance& data = message_->body(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -696,10 +714,11 @@ TEST_F(AsyncClientImplTest, Trailers) { } TEST_F(AsyncClientImplTest, ImmediateReset) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -719,10 +738,11 @@ TEST_F(AsyncClientImplTest, ImmediateReset) { TEST_F(AsyncClientImplTest, LocalResetAfterStreamStart) { Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -755,10 +775,11 @@ TEST_F(AsyncClientImplTest, LocalResetAfterStreamStart) { TEST_F(AsyncClientImplTest, SendDataAfterRemoteClosure) { Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -790,10 +811,11 @@ TEST_F(AsyncClientImplTest, SendDataAfterRemoteClosure) { TEST_F(AsyncClientImplTest, SendTrailersRemoteClosure) { Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -830,10 +852,11 @@ TEST_F(AsyncClientImplTest, SendTrailersRemoteClosure) { TEST_F(AsyncClientImplTest, ResetInOnHeaders) { Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -866,10 +889,11 @@ TEST_F(AsyncClientImplTest, ResetInOnHeaders) { TEST_F(AsyncClientImplTest, RemoteResetAfterStreamStart) { Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -900,10 +924,11 @@ TEST_F(AsyncClientImplTest, RemoteResetAfterStreamStart) { } TEST_F(AsyncClientImplTest, ResetAfterResponseStart) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); @@ -928,10 +953,11 @@ TEST_F(AsyncClientImplTest, ResetAfterResponseStart) { } TEST_F(AsyncClientImplTest, ResetStream) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -945,10 +971,11 @@ TEST_F(AsyncClientImplTest, ResetStream) { } TEST_F(AsyncClientImplTest, CancelRequest) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -963,10 +990,11 @@ TEST_F(AsyncClientImplTest, CancelRequest) { TEST_F(AsyncClientImplTracingTest, CancelRequest) { Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1001,10 +1029,11 @@ TEST_F(AsyncClientImplTracingTest, CancelRequest) { } TEST_F(AsyncClientImplTest, DestroyWithActiveStream) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1016,10 +1045,11 @@ TEST_F(AsyncClientImplTest, DestroyWithActiveStream) { } TEST_F(AsyncClientImplTest, DestroyWithActiveRequest) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); EXPECT_CALL(stream_encoder_, encodeHeaders(HeaderMapEqualRef(&message_->headers()), true)); @@ -1041,10 +1071,11 @@ TEST_F(AsyncClientImplTest, DestroyWithActiveRequest) { TEST_F(AsyncClientImplTracingTest, DestroyWithActiveRequest) { Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1081,7 +1112,7 @@ TEST_F(AsyncClientImplTracingTest, DestroyWithActiveRequest) { } TEST_F(AsyncClientImplTest, PoolFailure) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { callbacks.onPoolFailure(ConnectionPool::PoolFailureReason::Overflow, absl::string_view(), @@ -1106,7 +1137,7 @@ TEST_F(AsyncClientImplTest, PoolFailure) { } TEST_F(AsyncClientImplTest, PoolFailureWithBody) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { callbacks.onPoolFailure(ConnectionPool::PoolFailureReason::Overflow, absl::string_view(), @@ -1131,10 +1162,11 @@ TEST_F(AsyncClientImplTest, PoolFailureWithBody) { } TEST_F(AsyncClientImplTest, StreamTimeout) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1157,17 +1189,18 @@ TEST_F(AsyncClientImplTest, StreamTimeout) { EXPECT_EQ(1UL, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_timeout") .value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_EQ( 1UL, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_504").value()); } TEST_F(AsyncClientImplTest, StreamTimeoutHeadReply) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1190,10 +1223,11 @@ TEST_F(AsyncClientImplTest, StreamTimeoutHeadReply) { } TEST_F(AsyncClientImplTest, RequestTimeout) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1213,7 +1247,7 @@ TEST_F(AsyncClientImplTest, RequestTimeout) { EXPECT_EQ(1UL, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_timeout") .value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_EQ( 1UL, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_504").value()); @@ -1221,10 +1255,11 @@ TEST_F(AsyncClientImplTest, RequestTimeout) { TEST_F(AsyncClientImplTracingTest, RequestTimeout) { Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1262,10 +1297,11 @@ TEST_F(AsyncClientImplTracingTest, RequestTimeout) { } TEST_F(AsyncClientImplTest, DisableTimer) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1282,10 +1318,11 @@ TEST_F(AsyncClientImplTest, DisableTimer) { } TEST_F(AsyncClientImplTest, DisableTimerWithStream) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](StreamDecoder&, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); return nullptr; })); @@ -1306,10 +1343,11 @@ TEST_F(AsyncClientImplTest, MultipleDataStream) { Buffer::InstancePtr body{new Buffer::OwnedImpl("test body")}; Buffer::InstancePtr body2{new Buffer::OwnedImpl("test body2")}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks) -> ConnectionPool::Cancellable* { - callbacks.onPoolReady(stream_encoder_, cm_.conn_pool_.host_, stream_info_, {}); + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); response_decoder_ = &decoder; return nullptr; })); diff --git a/test/common/network/filter_manager_impl_test.cc b/test/common/network/filter_manager_impl_test.cc index ba0aae2d6e73..b2e8d4f809a2 100644 --- a/test/common/network/filter_manager_impl_test.cc +++ b/test/common/network/filter_manager_impl_test.cc @@ -412,7 +412,7 @@ stat_prefix: name EXPECT_EQ(manager.initializeReadFilters(), true); - EXPECT_CALL(factory_context.cluster_manager_, tcpConnPoolForCluster("fake_cluster", _, _)) + EXPECT_CALL(factory_context.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(&conn_pool)); request_callbacks->complete(Extensions::Filters::Common::RateLimit::LimitStatus::OK, nullptr, diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 764b5ce9941b..8414d0f307e6 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -105,8 +105,10 @@ class RouterTestBase : public testing::Test { router_.setDecoderFilterCallbacks(callbacks_); upstream_locality_.set_zone("to_az"); cm_.initializeThreadLocalClusters({"fake_cluster"}); - ON_CALL(*cm_.conn_pool_.host_, address()).WillByDefault(Return(host_address_)); - ON_CALL(*cm_.conn_pool_.host_, locality()).WillByDefault(ReturnRef(upstream_locality_)); + ON_CALL(*cm_.thread_local_cluster_.conn_pool_.host_, address()) + .WillByDefault(Return(host_address_)); + ON_CALL(*cm_.thread_local_cluster_.conn_pool_.host_, locality()) + .WillByDefault(ReturnRef(upstream_locality_)); router_.downstream_connection_.local_address_ = host_address_; router_.downstream_connection_.remote_address_ = Network::Utility::parseInternetAddressAndPort("1.2.3.4:80"); @@ -141,15 +143,15 @@ class RouterTestBase : public testing::Test { } AssertionResult verifyHostUpstreamStats(uint64_t success, uint64_t error) { - if (success != cm_.conn_pool_.host_->stats_.rq_success_.value()) { - return AssertionFailure() << fmt::format("rq_success {} does not match expected {}", - cm_.conn_pool_.host_->stats_.rq_success_.value(), - success); + if (success != cm_.thread_local_cluster_.conn_pool_.host_->stats_.rq_success_.value()) { + return AssertionFailure() << fmt::format( + "rq_success {} does not match expected {}", + cm_.thread_local_cluster_.conn_pool_.host_->stats_.rq_success_.value(), success); } - if (error != cm_.conn_pool_.host_->stats_.rq_error_.value()) { - return AssertionFailure() << fmt::format("rq_error {} does not match expected {}", - cm_.conn_pool_.host_->stats_.rq_error_.value(), - error); + if (error != cm_.thread_local_cluster_.conn_pool_.host_->stats_.rq_error_.value()) { + return AssertionFailure() << fmt::format( + "rq_error {} does not match expected {}", + cm_.thread_local_cluster_.conn_pool_.host_->stats_.rq_error_.value(), error); } return AssertionSuccess(); } @@ -182,10 +184,10 @@ class RouterTestBase : public testing::Test { .WillByDefault(Return(nullptr)); } - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) - .WillOnce(Invoke( - [&](const std::string&, Upstream::ResourcePriority, absl::optional, - Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) + .WillOnce( + Invoke([&](Upstream::ResourcePriority, absl::optional, + Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { auto match = context->metadataMatchCriteria()->metadataMatchCriteria(); EXPECT_EQ(match.size(), 2); auto it = match.begin(); @@ -206,9 +208,10 @@ class RouterTestBase : public testing::Test { // be cached. EXPECT_EQ(context->metadataMatchCriteria(), context->metadataMatchCriteria()); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -224,7 +227,8 @@ class RouterTestBase : public testing::Test { absl::optional preset_count, int expected_count) { setIncludeAttemptCountInRequest(set_include_attempt_count_in_request); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -252,13 +256,13 @@ class RouterTestBase : public testing::Test { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -273,7 +277,8 @@ class RouterTestBase : public testing::Test { response_headers->setEnvoyAttemptCount(preset_count.value()); } - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(callbacks_, encodeHeaders_(_, true)) .WillOnce(Invoke([expected_count](Http::ResponseHeaderMap& headers, bool) { EXPECT_EQ(expected_count, atoi(std::string(headers.getEnvoyAttemptCountValue()).c_str())); @@ -289,14 +294,14 @@ class RouterTestBase : public testing::Test { if (end_stream) { EXPECT_CALL(callbacks_.dispatcher_, createTimer_(_)); } - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder_ = &decoder; EXPECT_CALL(callbacks_.dispatcher_, setTrackedObject(_)).Times(testing::AtLeast(2)); - callbacks.onPoolReady(original_encoder_, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(original_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); HttpTestUtility::addDefaultHeaders(default_request_headers_); @@ -337,7 +342,7 @@ class RouterTestBase : public testing::Test { void setUpstreamMaxStreamDuration(uint32_t seconds) { common_http_protocol_options_.mutable_max_stream_duration()->MergeFrom( ProtobufUtil::TimeUtil::MillisecondsToDuration(seconds)); - ON_CALL(cm_.conn_pool_.host_->cluster_, commonHttpProtocolOptions()) + ON_CALL(cm_.thread_local_cluster_.conn_pool_.host_->cluster_, commonHttpProtocolOptions()) .WillByDefault(ReturnRef(common_http_protocol_options_)); } @@ -409,7 +414,8 @@ TEST_F(RouterTest, UpdateServerNameFilterState) { .WillByDefault(ReturnRef(dummy_option)); ON_CALL(callbacks_.stream_info_, filterState()) .WillByDefault(ReturnRef(stream_info.filterState())); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); stream_info.filterState()->setData(Network::UpstreamServerName::key(), std::make_unique("dummy"), StreamInfo::FilterState::StateType::Mutable); @@ -440,7 +446,8 @@ TEST_F(RouterTest, UpdateSubjectAltNamesFilterState) { .WillByDefault(ReturnRef(dummy_option)); ON_CALL(callbacks_.stream_info_, filterState()) .WillByDefault(ReturnRef(stream_info.filterState())); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -476,13 +483,13 @@ TEST_F(RouterTest, RouteNotFound) { TEST_F(RouterTest, MissingRequiredHeaders) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -522,12 +529,13 @@ TEST_F(RouterTest, ClusterNotFound) { TEST_F(RouterTest, PoolFailureWithPriority) { ON_CALL(callbacks_.route_->route_entry_, priority()) .WillByDefault(Return(Upstream::ResourcePriority::High)); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, Upstream::ResourcePriority::High, _, &router_)); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_, + httpConnPool(Upstream::ResourcePriority::High, _, &router_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { callbacks.onPoolFailure(ConnectionPool::PoolFailureReason::RemoteConnectionFailure, - "tls version mismatch", cm_.conn_pool_.host_); + "tls version mismatch", cm_.thread_local_cluster_.conn_pool_.host_); return nullptr; })); @@ -554,8 +562,9 @@ TEST_F(RouterTest, PoolFailureWithPriority) { } TEST_F(RouterTest, Http1Upstream) { - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, absl::optional(), _)); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, absl::optional(), _)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -577,8 +586,9 @@ TEST_F(RouterTest, Http1Upstream) { // x-envoy-original-path in the basic upstream test when Envoy header // suppression is configured. TEST_F(RouterTestSuppressEnvoyHeaders, Http1Upstream) { - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, absl::optional(), _)); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, absl::optional(), _)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -596,8 +606,9 @@ TEST_F(RouterTestSuppressEnvoyHeaders, Http1Upstream) { } TEST_F(RouterTest, Http2Upstream) { - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, absl::optional(), _)); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, absl::optional(), _)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -618,14 +629,15 @@ TEST_F(RouterTest, HashPolicy) { .WillByDefault(Return(&callbacks_.route_->route_entry_.hash_policy_)); EXPECT_CALL(callbacks_.route_->route_entry_.hash_policy_, generateHash(_, _, _, _)) .WillOnce(Return(absl::optional(10))); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, absl::optional, + Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { EXPECT_EQ(10UL, context->computeHashKey().value()); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -645,14 +657,15 @@ TEST_F(RouterTest, HashPolicyNoHash) { .WillByDefault(Return(&callbacks_.route_->route_entry_.hash_policy_)); EXPECT_CALL(callbacks_.route_->route_entry_.hash_policy_, generateHash(_, _, _, _)) .WillOnce(Return(absl::optional())); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, &router_)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, &router_)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, absl::optional, + Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { EXPECT_FALSE(context->computeHashKey()); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -678,22 +691,22 @@ TEST_F(RouterTest, AddCookie) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, absl::optional, + Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { EXPECT_EQ(10UL, context->computeHashKey().value()); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); std::string cookie_value; @@ -732,22 +745,22 @@ TEST_F(RouterTest, AddCookieNoDuplicate) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, absl::optional, + Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { EXPECT_EQ(10UL, context->computeHashKey().value()); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); EXPECT_CALL(callbacks_.route_->route_entry_.hash_policy_, generateHash(_, _, _, _)) @@ -785,22 +798,22 @@ TEST_F(RouterTest, AddMultipleCookies) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, absl::optional, + Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { EXPECT_EQ(10UL, context->computeHashKey().value()); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); std::string choco_c, foo_c; @@ -844,15 +857,16 @@ TEST_F(RouterTest, MetadataNoOp) { EXPECT_EQ(nullptr, router_.metadataMatchCrite TEST_F(RouterTest, MetadataMatchCriteria) { ON_CALL(callbacks_.route_->route_entry_, metadataMatchCriteria()) .WillByDefault(Return(&callbacks_.route_->route_entry_.metadata_matches_criteria_)); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, absl::optional, + Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { EXPECT_EQ(context->metadataMatchCriteria(), &callbacks_.route_->route_entry_.metadata_matches_criteria_); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -874,14 +888,15 @@ TEST_F(RouterTest, MetadataMatchCriteriaFromRequestNoRouteEntryMatch) { TEST_F(RouterTest, NoMetadataMatchCriteria) { ON_CALL(callbacks_.route_->route_entry_, metadataMatchCriteria()).WillByDefault(Return(nullptr)); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, absl::optional, + Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { EXPECT_EQ(context->metadataMatchCriteria(), nullptr); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -894,7 +909,8 @@ TEST_F(RouterTest, NoMetadataMatchCriteria) { } TEST_F(RouterTest, CancelBeforeBoundToPool) { - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; @@ -910,7 +926,7 @@ TEST_F(RouterTest, CancelBeforeBoundToPool) { } TEST_F(RouterTest, NoHost) { - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)).WillOnce(Return(nullptr)); + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(nullptr)); Http::TestResponseHeaderMapImpl response_headers{ {":status", "503"}, {"content-length", "19"}, {"content-type", "text/plain"}}; @@ -977,13 +993,13 @@ TEST_F(RouterTestSuppressEnvoyHeaders, MaintenanceMode) { TEST_F(RouterTest, ResponseCodeDetailsSetByUpstream) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -1004,13 +1020,13 @@ TEST_F(RouterTest, ResponseCodeDetailsSetByUpstream) { TEST_F(RouterTest, EnvoyUpstreamServiceTime) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -1021,7 +1037,8 @@ TEST_F(RouterTest, EnvoyUpstreamServiceTime) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(callbacks_, encodeHeaders_(_, true)) .WillOnce(Invoke([](Http::HeaderMap& headers, bool) { EXPECT_FALSE(headers.get(Http::Headers::get().EnvoyUpstreamServiceTime).empty()); @@ -1062,13 +1079,13 @@ TEST_F(RouterTest, EnvoyAttemptCountInRequestUpdatedInRetries) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -1086,7 +1103,8 @@ TEST_F(RouterTest, EnvoyAttemptCountInRequestUpdatedInRetries) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); response_decoder->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -1094,13 +1112,13 @@ TEST_F(RouterTest, EnvoyAttemptCountInRequestUpdatedInRetries) { // We expect the 5xx response to kick off a new request. EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); router_.retry_state_->callback_(); @@ -1112,10 +1130,11 @@ TEST_F(RouterTest, EnvoyAttemptCountInRequestUpdatedInRetries) { // Normal response. EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); - EXPECT_CALL(cm_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); response_decoder->decodeHeaders(std::move(response_headers2), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); @@ -1169,11 +1188,11 @@ TEST_F(RouterTestSuppressEnvoyHeaders, EnvoyAttemptCountInResponseNotPresent) { TEST_F(RouterTest, EnvoyAttemptCountInResponsePresentWithLocalReply) { setIncludeAttemptCountInResponse(true); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { callbacks.onPoolFailure(ConnectionPool::PoolFailureReason::RemoteConnectionFailure, - absl::string_view(), cm_.conn_pool_.host_); + absl::string_view(), cm_.thread_local_cluster_.conn_pool_.host_); return nullptr; })); @@ -1207,13 +1226,13 @@ TEST_F(RouterTest, EnvoyAttemptCountInResponseWithRetries) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -1229,20 +1248,21 @@ TEST_F(RouterTest, EnvoyAttemptCountInResponseWithRetries) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); response_decoder->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // We expect the 5xx response to kick off a new request. EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); router_.retry_state_->callback_(); @@ -1251,10 +1271,11 @@ TEST_F(RouterTest, EnvoyAttemptCountInResponseWithRetries) { // Normal response. EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); - EXPECT_CALL(cm_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(callbacks_, encodeHeaders_(_, true)) .WillOnce(Invoke([](Http::ResponseHeaderMap& headers, bool) { // Because a retry happened the number of attempts in the response headers should be 2. @@ -1281,13 +1302,13 @@ void RouterTestBase::testAppendCluster(absl::optional clu NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -1300,7 +1321,8 @@ void RouterTestBase::testAppendCluster(absl::optional clu Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(callbacks_, encodeHeaders_(_, true)) .WillOnce(Invoke([&cluster_header_name](Http::HeaderMap& headers, bool) { const auto cluster_header = @@ -1336,17 +1358,17 @@ void RouterTestBase::testAppendUpstreamHost( callbacks_.streamInfo().filterState()->setData(DebugConfig::key(), std::move(debug_config), StreamInfo::FilterState::StateType::ReadOnly, StreamInfo::FilterState::LifeSpan::FilterChain); - cm_.conn_pool_.host_->hostname_ = "scooby.doo"; + cm_.thread_local_cluster_.conn_pool_.host_->hostname_ = "scooby.doo"; NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -1359,7 +1381,8 @@ void RouterTestBase::testAppendUpstreamHost( Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(callbacks_, encodeHeaders_(_, true)) .WillOnce(Invoke([&hostname_header_name, &host_address_header_name](Http::HeaderMap& headers, bool) { @@ -1449,7 +1472,7 @@ TEST_F(RouterTest, AllDebugConfig) { callbacks_.streamInfo().filterState()->setData(DebugConfig::key(), std::move(debug_config), StreamInfo::FilterState::StateType::ReadOnly, StreamInfo::FilterState::LifeSpan::FilterChain); - cm_.conn_pool_.host_->hostname_ = "scooby.doo"; + cm_.thread_local_cluster_.conn_pool_.host_->hostname_ = "scooby.doo"; Http::TestResponseHeaderMapImpl response_headers{ {":status", "204"}, @@ -1474,13 +1497,13 @@ TEST_F(RouterTest, AllDebugConfig) { TEST_F(RouterTestSuppressEnvoyHeaders, EnvoyUpstreamServiceTime) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -1494,7 +1517,8 @@ TEST_F(RouterTestSuppressEnvoyHeaders, EnvoyUpstreamServiceTime) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); Http::TestResponseHeaderMapImpl downstream_response_headers{ {":status", "200"}, {"x-envoy-upstream-service-time", "0"}}; EXPECT_CALL(callbacks_, encodeHeaders_(_, true)) @@ -1508,13 +1532,13 @@ TEST_F(RouterTestSuppressEnvoyHeaders, EnvoyUpstreamServiceTime) { TEST_F(RouterTest, NoRetriesOverflow) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -1529,7 +1553,8 @@ TEST_F(RouterTest, NoRetriesOverflow) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); response_decoder->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -1537,13 +1562,13 @@ TEST_F(RouterTest, NoRetriesOverflow) { // We expect the 5xx response to kick off a new request. EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); router_.retry_state_->callback_(); @@ -1554,10 +1579,11 @@ TEST_F(RouterTest, NoRetriesOverflow) { EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamOverflow)); EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)) .WillOnce(Return(RetryStatus::NoOverflow)); - EXPECT_CALL(cm_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); response_decoder->decodeHeaders(std::move(response_headers2), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 2)); @@ -1566,13 +1592,13 @@ TEST_F(RouterTest, NoRetriesOverflow) { TEST_F(RouterTest, ResetDuringEncodeHeaders) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -1587,10 +1613,10 @@ TEST_F(RouterTest, ResetDuringEncodeHeaders) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); // First connection is successful and reset happens later on. - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); router_.decodeHeaders(headers, true); @@ -1602,13 +1628,13 @@ TEST_F(RouterTest, ResetDuringEncodeHeaders) { TEST_F(RouterTest, UpstreamTimeout) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) @@ -1634,7 +1660,7 @@ TEST_F(RouterTest, UpstreamTimeout) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)).Times(0); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); response_timeout_->invokeCallback(); @@ -1644,7 +1670,7 @@ TEST_F(RouterTest, UpstreamTimeout) { .value()); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_timeout_.value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); } @@ -1653,13 +1679,13 @@ TEST_F(RouterTest, UpstreamTimeout) { TEST_F(RouterTest, TimeoutBudgetHistogramStat) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -1698,13 +1724,13 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStat) { TEST_F(RouterTest, TimeoutBudgetHistogramStatFailure) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -1742,13 +1768,13 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatFailure) { TEST_F(RouterTest, TimeoutBudgetHistogramStatOnlyGlobal) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -1784,13 +1810,13 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatOnlyGlobal) { TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -1822,7 +1848,8 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "504"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(504)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(504)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)).Times(2); response_decoder1->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -1831,13 +1858,13 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -1866,14 +1893,14 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); per_try_timeout_->invokeCallback(); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_rq_per_try_timeout") .value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 2)); } @@ -1882,13 +1909,13 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -1920,7 +1947,8 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); test_time_.advanceTimeWait(std::chrono::milliseconds(160)); response_decoder1->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -1929,13 +1957,13 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -1965,14 +1993,14 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)).Times(0); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); response_timeout_->invokeCallback(); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_timeout") .value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 2)); } @@ -1980,13 +2008,13 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { TEST_F(RouterTest, GrpcOkTrailersOnly) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2001,7 +2029,8 @@ TEST_F(RouterTest, GrpcOkTrailersOnly) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "0"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); } @@ -2010,13 +2039,13 @@ TEST_F(RouterTest, GrpcOkTrailersOnly) { TEST_F(RouterTest, GrpcAlreadyExistsTrailersOnly) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2031,7 +2060,8 @@ TEST_F(RouterTest, GrpcAlreadyExistsTrailersOnly) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "6"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(409)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(409)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); } @@ -2040,13 +2070,13 @@ TEST_F(RouterTest, GrpcAlreadyExistsTrailersOnly) { TEST_F(RouterTest, GrpcOutlierDetectionUnavailableStatusCode) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2062,7 +2092,8 @@ TEST_F(RouterTest, GrpcOutlierDetectionUnavailableStatusCode) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "14"}}); // Outlier detector will use the gRPC response status code. - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); } @@ -2071,13 +2102,13 @@ TEST_F(RouterTest, GrpcOutlierDetectionUnavailableStatusCode) { TEST_F(RouterTest, GrpcInternalTrailersOnly) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2091,7 +2122,8 @@ TEST_F(RouterTest, GrpcInternalTrailersOnly) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "13"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(500)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(500)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -2102,13 +2134,13 @@ TEST_F(RouterTest, GrpcInternalTrailersOnly) { TEST_F(RouterTest, GrpcDataEndStream) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2123,7 +2155,8 @@ TEST_F(RouterTest, GrpcDataEndStream) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), false); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); Buffer::OwnedImpl data; @@ -2136,13 +2169,13 @@ TEST_F(RouterTest, GrpcDataEndStream) { TEST_F(RouterTest, GrpcReset) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2157,10 +2190,11 @@ TEST_F(RouterTest, GrpcReset) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), false); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -2171,13 +2205,13 @@ TEST_F(RouterTest, GrpcReset) { TEST_F(RouterTest, GrpcOk) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2193,7 +2227,8 @@ TEST_F(RouterTest, GrpcOk) { EXPECT_CALL(callbacks_.dispatcher_, setTrackedObject(_)).Times(2); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), false); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); @@ -2208,13 +2243,13 @@ TEST_F(RouterTest, GrpcOk) { TEST_F(RouterTest, GrpcInternal) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2229,7 +2264,8 @@ TEST_F(RouterTest, GrpcInternal) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), false); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); Http::ResponseTrailerMapPtr response_trailers( @@ -2241,13 +2277,13 @@ TEST_F(RouterTest, GrpcInternal) { TEST_F(RouterTest, UpstreamTimeoutWithAltResponse) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) @@ -2273,7 +2309,7 @@ TEST_F(RouterTest, UpstreamTimeoutWithAltResponse) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)).Times(0); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(204))); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); response_timeout_->invokeCallback(); @@ -2281,7 +2317,7 @@ TEST_F(RouterTest, UpstreamTimeoutWithAltResponse) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_timeout") .value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); } @@ -2289,13 +2325,13 @@ TEST_F(RouterTest, UpstreamTimeoutWithAltResponse) { TEST_F(RouterTest, UpstreamPerTryTimeout) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) @@ -2326,7 +2362,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeout) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); per_try_timeout_->invokeCallback(); @@ -2334,7 +2370,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeout) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_rq_per_try_timeout") .value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); } @@ -2344,7 +2380,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutDelayedPoolReady) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; Http::ConnectionPool::Callbacks* pool_callbacks; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { @@ -2372,8 +2408,8 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutDelayedPoolReady) { EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - pool_callbacks->onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + pool_callbacks->onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2384,7 +2420,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutDelayedPoolReady) { {":status", "504"}, {"content-length", "24"}, {"content-type", "text/plain"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); per_try_timeout_->invokeCallback(); @@ -2392,7 +2428,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutDelayedPoolReady) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_rq_per_try_timeout") .value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); } @@ -2403,7 +2439,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutExcludesNewStream) { Http::ResponseDecoder* response_decoder = nullptr; Http::ConnectionPool::Callbacks* pool_callbacks; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { @@ -2432,14 +2468,14 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutExcludesNewStream) { EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // The per try timeout timer should not be started yet. - pool_callbacks->onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + pool_callbacks->onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_CALL(encoder.stream_, resetStream(Http::StreamResetReason::LocalReset)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); EXPECT_CALL(*per_try_timeout_, disableTimer()); EXPECT_CALL(callbacks_.stream_info_, @@ -2454,7 +2490,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutExcludesNewStream) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_rq_per_try_timeout") .value()); - EXPECT_EQ(1UL, cm_.conn_pool_.host_->stats().rq_timeout_.value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.conn_pool_.host_->stats().rq_timeout_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); } @@ -2466,17 +2502,17 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))) .Times(2); @@ -2490,7 +2526,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; @@ -2498,14 +2534,14 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { router_.retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -2521,7 +2557,8 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { // incremented properly. Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(encoder2.stream_, resetStream(_)); @@ -2549,17 +2586,17 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))) .Times(2); @@ -2574,7 +2611,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; @@ -2582,14 +2619,14 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { router_.retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -2605,7 +2642,8 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { // should be reset immediately. Http::ResponseHeaderMapPtr bad_response_headers( new Http::TestResponseHeaderMapImpl{{":status", "500"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(500)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(500)); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(encoder2.stream_, resetStream(_)); EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)) @@ -2617,7 +2655,8 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { // incremented properly. Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)) @@ -2638,14 +2677,14 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2663,11 +2702,12 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "500"}}); // Local origin connect success happens for first and third try. - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))) .Times(2); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(500)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(500)); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); router_.retry_state_->expectHeadersRetry(); @@ -2675,14 +2715,14 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -2695,18 +2735,18 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { // Now trigger a per try timeout on the 2nd request, expect a 3rd router_.retry_state_->expectHedgedPerTryTimeoutRetry(); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); NiceMock encoder3; Http::ResponseDecoder* response_decoder3 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder3 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder3, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder3, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -2722,7 +2762,8 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { // incremented properly. Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(encoder2.stream_, resetStream(_)); EXPECT_CALL(encoder3.stream_, resetStream(_)).Times(0); @@ -2746,17 +2787,17 @@ TEST_F(RouterTest, RetryOnlyOnceForSameUpstreamRequest) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))) .Times(2); @@ -2770,7 +2811,7 @@ TEST_F(RouterTest, RetryOnlyOnceForSameUpstreamRequest) { EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); router_.retry_state_->expectHedgedPerTryTimeoutRetry(); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)).Times(2); @@ -2778,14 +2819,14 @@ TEST_F(RouterTest, RetryOnlyOnceForSameUpstreamRequest) { NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -2795,13 +2836,14 @@ TEST_F(RouterTest, RetryOnlyOnceForSameUpstreamRequest) { // Now send a 5xx back and make sure we don't ask whether we should retry it. Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "500"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(500)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(500)); EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).Times(0); EXPECT_CALL(*router_.retry_state_, wouldRetryFromHeaders(_)).WillOnce(Return(true)); response_decoder1->decodeHeaders(std::move(response_headers1), true); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); response_timeout_->invokeCallback(); @@ -2816,17 +2858,17 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))) .Times(2); @@ -2841,7 +2883,7 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); router_.retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); @@ -2852,7 +2894,8 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { // and also that we don't respond downstream with it. Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "500"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(500)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(500)); EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).Times(0); EXPECT_CALL(*router_.retry_state_, wouldRetryFromHeaders(_)).WillOnce(Return(true)); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); @@ -2861,14 +2904,14 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { // Now trigger the retry for the per try timeout earlier. NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); router_.retry_state_->callback_(); @@ -2881,7 +2924,8 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { EXPECT_EQ(headers.Status()->value(), "200"); EXPECT_TRUE(end_stream); })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder2->decodeHeaders(std::move(response_headers2), true); } @@ -2890,13 +2934,13 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { TEST_F(RouterTest, RetryRequestBeforeBody) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2911,13 +2955,13 @@ TEST_F(RouterTest, RetryRequestBeforeBody) { encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); EXPECT_CALL(encoder2, encodeHeaders(HeaderHasValueRef("myheader", "present"), false)); @@ -2953,13 +2997,13 @@ TEST_F(RouterTest, RetryRequestDuringBody) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -2978,13 +3022,13 @@ TEST_F(RouterTest, RetryRequestDuringBody) { encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3024,13 +3068,13 @@ TEST_F(RouterTest, RetryRequestDuringBodyDataBetweenAttemptsNotEndStream) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -3053,13 +3097,13 @@ TEST_F(RouterTest, RetryRequestDuringBodyDataBetweenAttemptsNotEndStream) { router_.decodeData(buf2, false); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3099,13 +3143,13 @@ TEST_F(RouterTest, RetryRequestDuringBodyCompleteBetweenAttempts) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3129,13 +3173,13 @@ TEST_F(RouterTest, RetryRequestDuringBodyCompleteBetweenAttempts) { router_.decodeData(buf2, true); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3168,13 +3212,13 @@ TEST_F(RouterTest, RetryRequestDuringBodyTrailerBetweenAttempts) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3197,13 +3241,13 @@ TEST_F(RouterTest, RetryRequestDuringBodyTrailerBetweenAttempts) { router_.decodeTrailers(trailers); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3238,13 +3282,13 @@ TEST_F(RouterTest, RetryRequestDuringBodyBufferLimitExceeded) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3280,17 +3324,17 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))) .Times(2); @@ -3304,7 +3348,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); @@ -3313,14 +3357,14 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -3334,7 +3378,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { EXPECT_CALL(encoder1.stream_, resetStream(_)); EXPECT_CALL(encoder2.stream_, resetStream(_)); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)) @@ -3344,7 +3388,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)).Times(2); response_timeout_->invokeCallback(); EXPECT_TRUE(verifyHostUpstreamStats(0, 2)); - EXPECT_EQ(2, cm_.conn_pool_.host_->stats_.rq_timeout_.value()); + EXPECT_EQ(2, cm_.thread_local_cluster_.conn_pool_.host_->stats_.rq_timeout_.value()); // TODO: Verify hedge stats here once they are implemented. } @@ -3355,17 +3399,17 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))); expectPerTryTimerCreate(); @@ -3379,7 +3423,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); @@ -3388,17 +3432,17 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))); expectPerTryTimerCreate(); @@ -3411,7 +3455,8 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { // Now trigger a 503 in response to the second request. Http::ResponseHeaderMapPtr bad_response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)) .WillOnce(Return(RetryStatus::NoRetryLimitExceeded)); @@ -3422,7 +3467,8 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { // Now trigger a 502 in response to the first request. Http::ResponseHeaderMapPtr bad_response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "502"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(502)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(502)); // We should not call shouldRetryHeaders() because you never retry the same // request twice. @@ -3444,20 +3490,20 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); // First is reset - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))) .Times(2); @@ -3472,7 +3518,7 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); @@ -3481,14 +3527,14 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -3517,7 +3563,8 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { .WillOnce(Invoke([&](Http::ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ(headers.Status()->value(), "200"); })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder2->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); @@ -3532,17 +3579,17 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))); @@ -3560,21 +3607,21 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, true)); EXPECT_CALL( - cm_.conn_pool_.host_->outlier_detector_, + cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); per_try_timeout_->invokeCallback(); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); callbacks.onPoolFailure(ConnectionPool::PoolFailureReason::RemoteConnectionFailure, - absl::string_view(), cm_.conn_pool_.host_); + absl::string_view(), cm_.thread_local_cluster_.conn_pool_.host_); return nullptr; })); EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)) @@ -3595,7 +3642,8 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { .WillOnce(Invoke([&](Http::ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ(headers.Status()->value(), "200"); })); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); @@ -3607,13 +3655,13 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { TEST_F(RouterTest, RetryNoneHealthy) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3628,12 +3676,12 @@ TEST_F(RouterTest, RetryNoneHealthy) { router_.decodeHeaders(headers, true); router_.retry_state_->expectResetRetry(); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); encoder1.stream_.resetStream(Http::StreamResetReason::LocalReset); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)).WillOnce(Return(nullptr)); + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(nullptr)); Http::TestResponseHeaderMapImpl response_headers{ {":status", "503"}, {"content-length", "19"}, {"content-type", "text/plain"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); @@ -3650,13 +3698,13 @@ TEST_F(RouterTest, RetryNoneHealthy) { TEST_F(RouterTest, RetryUpstreamReset) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -3672,23 +3720,23 @@ TEST_F(RouterTest, RetryUpstreamReset) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); router_.retry_state_->expectResetRetry(); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)).Times(2); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); // We expect this reset to kick off a new request. NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); router_.retry_state_->callback_(); @@ -3700,7 +3748,8 @@ TEST_F(RouterTest, RetryUpstreamReset) { EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); } @@ -3708,13 +3757,13 @@ TEST_F(RouterTest, RetryUpstreamReset) { TEST_F(RouterTest, NoRetryWithBodyLimit) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -3743,14 +3792,14 @@ TEST_F(RouterTest, NoRetryWithBodyLimit) { TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -3766,7 +3815,7 @@ TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); router_.retry_state_->expectResetRetry(); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); per_try_timeout_->invokeCallback(); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -3774,16 +3823,16 @@ TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { // We expect this reset to kick off a new request. NiceMock encoder2; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -3795,7 +3844,8 @@ TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); } @@ -3804,7 +3854,7 @@ TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { // a way that no host is present. TEST_F(RouterTest, RetryUpstreamConnectionFailure) { Http::ConnectionPool::Callbacks* conn_pool_callbacks; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { conn_pool_callbacks = &callbacks; @@ -3830,14 +3880,14 @@ TEST_F(RouterTest, RetryUpstreamConnectionFailure) { Http::ResponseDecoder* response_decoder = nullptr; // We expect this reset to kick off a new request. NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); router_.retry_state_->callback_(); @@ -3848,7 +3898,8 @@ TEST_F(RouterTest, RetryUpstreamConnectionFailure) { EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); } @@ -3856,13 +3907,13 @@ TEST_F(RouterTest, RetryUpstreamConnectionFailure) { TEST_F(RouterTest, DontResetStartedResponseOnUpstreamPerTryTimeout) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -3882,7 +3933,8 @@ TEST_F(RouterTest, DontResetStartedResponseOnUpstreamPerTryTimeout) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); Buffer::OwnedImpl body("test body"); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), false); per_try_timeout_->invokeCallback(); EXPECT_CALL(callbacks_, encodeData(_, true)); @@ -3898,13 +3950,13 @@ TEST_F(RouterTest, DontResetStartedResponseOnUpstreamPerTryTimeout) { TEST_F(RouterTest, RetryUpstreamResetResponseStarted) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -3920,9 +3972,10 @@ TEST_F(RouterTest, RetryUpstreamResetResponseStarted) { EXPECT_CALL(callbacks_, encodeHeaders_(_, false)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), false); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); // Normally, sendLocalReply will actually send the reply, but in this case the // HCM will detect the headers have already been sent and not route through @@ -3943,13 +3996,13 @@ TEST_F(RouterTest, Coalesce100ContinueHeaders) { // Setup. NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -3985,7 +4038,7 @@ TEST_F(RouterTest, Coalesce100ContinueHeaders) { cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_100").value()); // Reset stream and cleanup. - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); @@ -3996,13 +4049,13 @@ TEST_F(RouterTest, Coalesce100ContinueHeaders) { TEST_F(RouterTest, RetryUpstreamReset100ContinueResponseStarted) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4024,7 +4077,7 @@ TEST_F(RouterTest, RetryUpstreamReset100ContinueResponseStarted) { EXPECT_EQ( 1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_100").value()); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); @@ -4035,13 +4088,13 @@ TEST_F(RouterTest, RetryUpstreamReset100ContinueResponseStarted) { TEST_F(RouterTest, RetryUpstream5xx) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4057,20 +4110,21 @@ TEST_F(RouterTest, RetryUpstream5xx) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); response_decoder->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // We expect the 5xx response to kick off a new request. EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); router_.retry_state_->callback_(); @@ -4079,10 +4133,11 @@ TEST_F(RouterTest, RetryUpstream5xx) { // Normal response. EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); - EXPECT_CALL(cm_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers2), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); } @@ -4090,13 +4145,13 @@ TEST_F(RouterTest, RetryUpstream5xx) { TEST_F(RouterTest, RetryTimeoutDuringRetryDelay) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4112,7 +4167,8 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelay) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); response_decoder->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -4120,7 +4176,8 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelay) { EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamRequestTimeout)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResponseTime(_)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResponseTime(_)) + .Times(0); Http::TestResponseHeaderMapImpl response_headers{ {":status", "504"}, {"content-length", "24"}, {"content-type", "text/plain"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); @@ -4133,13 +4190,13 @@ TEST_F(RouterTest, MaxStreamDurationValidlyConfiguredWithoutRetryPolicy) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; setUpstreamMaxStreamDuration(500); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectMaxStreamDurationTimerCreate(); @@ -4158,13 +4215,13 @@ TEST_F(RouterTest, MaxStreamDurationDisabledIfSetToZero) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; setUpstreamMaxStreamDuration(0); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -4184,13 +4241,13 @@ TEST_F(RouterTest, MaxStreamDurationCallbackNotCalled) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; setUpstreamMaxStreamDuration(5000); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectMaxStreamDurationTimerCreate(); @@ -4208,13 +4265,13 @@ TEST_F(RouterTest, MaxStreamDurationWhenDownstreamAlreadyStartedWithoutRetryPoli NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; setUpstreamMaxStreamDuration(500); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectMaxStreamDurationTimerCreate(); @@ -4237,13 +4294,13 @@ TEST_F(RouterTest, MaxStreamDurationWithRetryPolicy) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; setUpstreamMaxStreamDuration(500); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectMaxStreamDurationTimerCreate(); @@ -4261,13 +4318,13 @@ TEST_F(RouterTest, MaxStreamDurationWithRetryPolicy) { // Second upstream request NiceMock encoder2; setUpstreamMaxStreamDuration(500); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectMaxStreamDurationTimerCreate(); @@ -4283,13 +4340,13 @@ TEST_F(RouterTest, MaxStreamDurationWithRetryPolicy) { TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHost) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4305,12 +4362,13 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHost) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); response_decoder->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); Envoy::ConnectionPool::MockCancellable cancellable; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks&) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; @@ -4323,7 +4381,8 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHost) { EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamRequestTimeout)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResponseTime(_)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResponseTime(_)) + .Times(0); Http::TestResponseHeaderMapImpl response_headers{ {":status", "504"}, {"content-length", "24"}, {"content-type", "text/plain"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); @@ -4339,13 +4398,13 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHost) { TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHostAltResponseCode) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4363,12 +4422,13 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHostAltRespo router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); response_decoder->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); Envoy::ConnectionPool::MockCancellable cancellable; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks&) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; @@ -4381,7 +4441,8 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHostAltRespo EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamRequestTimeout)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResponseTime(_)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResponseTime(_)) + .Times(0); Http::TestResponseHeaderMapImpl response_headers{{":status", "204"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); response_timeout_->invokeCallback(); @@ -4394,13 +4455,13 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHostAltRespo TEST_F(RouterTest, RetryUpstream5xxNotComplete) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4425,19 +4486,20 @@ TEST_F(RouterTest, RetryUpstream5xxNotComplete) { Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(encoder1.stream_, resetStream(Http::StreamResetReason::LocalReset)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); response_decoder->decodeHeaders(std::move(response_headers1), false); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // We expect the 5xx response to kick off a new request. NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); ON_CALL(callbacks_, decodingBuffer()).WillByDefault(Return(body_data.get())); @@ -4450,9 +4512,10 @@ TEST_F(RouterTest, RetryUpstream5xxNotComplete) { // Normal response. EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResponseTime(_)); - EXPECT_CALL(cm_.conn_pool_.host_->health_checker_, setUnhealthy()); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResponseTime(_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy()); Http::ResponseHeaderMapPtr response_headers2(new Http::TestResponseHeaderMapImpl{ {":status", "200"}, {"x-envoy-immediate-health-check-fail", "true"}}); response_decoder->decodeHeaders(std::move(response_headers2), true); @@ -4476,13 +4539,13 @@ TEST_F(RouterTest, RetryUpstream5xxNotComplete) { TEST_F(RouterTest, RetryUpstreamGrpcCancelled) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4501,20 +4564,21 @@ TEST_F(RouterTest, RetryUpstreamGrpcCancelled) { router_.retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "1"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(499)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(499)); response_decoder->decodeHeaders(std::move(response_headers1), true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // We expect the grpc-status to result in a retried request. EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); router_.retry_state_->callback_(); @@ -4525,7 +4589,8 @@ TEST_F(RouterTest, RetryUpstreamGrpcCancelled) { EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "0"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); } @@ -4537,13 +4602,13 @@ TEST_F(RouterTest, RetryRespsectsMaxHostSelectionCount) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4571,20 +4636,21 @@ TEST_F(RouterTest, RetryRespsectsMaxHostSelectionCount) { Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(encoder1.stream_, resetStream(Http::StreamResetReason::LocalReset)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)).Times(2); response_decoder->decodeHeaders(std::move(response_headers1), false); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // We expect the 5xx response to kick off a new request. NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); ON_CALL(callbacks_, decodingBuffer()).WillByDefault(Return(body_data.get())); @@ -4600,10 +4666,11 @@ TEST_F(RouterTest, RetryRespsectsMaxHostSelectionCount) { // Normal response. EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); - EXPECT_CALL(cm_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers2), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); } @@ -4615,13 +4682,13 @@ TEST_F(RouterTest, RetryRespectsRetryHostPredicate) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4649,20 +4716,21 @@ TEST_F(RouterTest, RetryRespectsRetryHostPredicate) { Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(encoder1.stream_, resetStream(Http::StreamResetReason::LocalReset)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)).Times(2); response_decoder->decodeHeaders(std::move(response_headers1), false); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // We expect the 5xx response to kick off a new request. NiceMock encoder2; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); ON_CALL(callbacks_, decodingBuffer()).WillByDefault(Return(body_data.get())); @@ -4678,10 +4746,11 @@ TEST_F(RouterTest, RetryRespectsRetryHostPredicate) { // Normal response. EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); - EXPECT_CALL(cm_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy()).Times(0); Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers2), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); } @@ -4924,13 +4993,13 @@ TEST_F(RouterTest, Shadow) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -4985,13 +5054,13 @@ TEST_F(RouterTest, AltStatName) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -5003,8 +5072,9 @@ TEST_F(RouterTest, AltStatName) { EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, putResponseTime(_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResponseTime(_)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}, @@ -5200,13 +5270,13 @@ TEST_F(RouterTest, PropagatesUpstreamFilterState) { "upstream data", std::make_unique(123), StreamInfo::FilterState::StateType::ReadOnly, StreamInfo::FilterState::LifeSpan::Connection); expectResponseTimerCreate(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -5233,13 +5303,13 @@ TEST_F(RouterTest, UpstreamSSLConnection) { upstream_stream_info_.setDownstreamSslConnection(connection_info); expectResponseTimerCreate(); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -5264,13 +5334,13 @@ TEST_F(RouterTest, UpstreamSSLConnection) { TEST_F(RouterTest, UpstreamTimingSingleRequest) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -5327,13 +5397,13 @@ TEST_F(RouterTest, UpstreamTimingSingleRequest) { TEST_F(RouterTest, UpstreamTimingRetry) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -5357,13 +5427,13 @@ TEST_F(RouterTest, UpstreamTimingRetry) { test_time_.advanceTimeWait(std::chrono::milliseconds(43)); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -5416,13 +5486,13 @@ TEST_F(RouterTest, UpstreamTimingRetry) { TEST_F(RouterTest, UpstreamTimingTimeout) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -5982,13 +6052,13 @@ TEST_F(RouterTest, CanaryStatusTrue) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -6003,7 +6073,7 @@ TEST_F(RouterTest, CanaryStatusTrue) { new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"x-envoy-upstream-canary", "false"}, {"x-envoy-virtual-cluster", "hello"}}); - ON_CALL(*cm_.conn_pool_.host_, canary()).WillByDefault(Return(true)); + ON_CALL(*cm_.thread_local_cluster_.conn_pool_.host_, canary()).WillByDefault(Return(true)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); @@ -6020,13 +6090,13 @@ TEST_F(RouterTest, CanaryStatusFalse) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -6058,19 +6128,19 @@ TEST_F(RouterTest, AutoHostRewriteEnabled) { HttpTestUtility::addDefaultHeaders(incoming_headers); incoming_headers.setHost(req_host); - cm_.conn_pool_.host_->hostname_ = "scooby.doo"; + cm_.thread_local_cluster_.conn_pool_.host_->hostname_ = "scooby.doo"; Http::TestRequestHeaderMapImpl outgoing_headers; HttpTestUtility::addDefaultHeaders(outgoing_headers); - outgoing_headers.setHost(cm_.conn_pool_.host_->hostname_); + outgoing_headers.setHost(cm_.thread_local_cluster_.conn_pool_.host_->hostname_); EXPECT_CALL(callbacks_.route_->route_entry_, timeout()) .WillOnce(Return(std::chrono::milliseconds(0))); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -6101,16 +6171,16 @@ TEST_F(RouterTest, AutoHostRewriteDisabled) { HttpTestUtility::addDefaultHeaders(incoming_headers); incoming_headers.setHost(req_host); - cm_.conn_pool_.host_->hostname_ = "scooby.doo"; + cm_.thread_local_cluster_.conn_pool_.host_->hostname_ = "scooby.doo"; EXPECT_CALL(callbacks_.route_->route_entry_, timeout()) .WillOnce(Return(std::chrono::milliseconds(0))); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -6158,9 +6228,9 @@ TEST_F(RouterTest, ApplicationProtocols) { std::make_unique(std::vector{"foo", "bar"}), StreamInfo::FilterState::StateType::ReadOnly, StreamInfo::FilterState::LifeSpan::FilterChain); - EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, _)) .WillOnce( - Invoke([&](const std::string&, Upstream::ResourcePriority, absl::optional, + Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { Network::TransportSocketOptionsSharedPtr transport_socket_options = context->upstreamTransportSocketOptions(); @@ -6169,9 +6239,10 @@ TEST_F(RouterTest, ApplicationProtocols) { EXPECT_EQ(transport_socket_options->applicationProtocolListOverride().size(), 2); EXPECT_EQ(transport_socket_options->applicationProtocolListOverride()[0], "foo"); EXPECT_EQ(transport_socket_options->applicationProtocolListOverride()[1], "bar"); - return &cm_.conn_pool_; + return &cm_.thread_local_cluster_.conn_pool_; })); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Return(&cancellable_)); expectResponseTimerCreate(); @@ -6193,13 +6264,13 @@ TEST_F(RouterTest, ApplicationProtocols) { TEST_F(RouterTest, ConnectPauseAndResume) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -6235,13 +6306,13 @@ TEST_F(RouterTest, ConnectPauseNoResume) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -6277,7 +6348,7 @@ TEST_F(RouterTest, ConnectExplicitTcpUpstream) { absl::make_optional(); // Make sure newConnection is called on the TCP pool, not newStream on the HTTP pool. - EXPECT_CALL(cm_.tcp_conn_pool_, newConnection(_)); + EXPECT_CALL(cm_.thread_local_cluster_.tcp_conn_pool_, newConnection(_)); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("CONNECT"); @@ -6298,15 +6369,15 @@ class WatermarkTest : public RouterTest { .WillOnce( Invoke([&](Http::StreamCallbacks& callbacks) { stream_callbacks_ = &callbacks; })); EXPECT_CALL(encoder_, getStream()).WillRepeatedly(ReturnRef(stream_)); - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder_ = &decoder; pool_callbacks_ = &callbacks; if (pool_ready) { - callbacks.onPoolReady(encoder_, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); } return nullptr; })); @@ -6404,8 +6475,8 @@ TEST_F(WatermarkTest, FilterWatermarks) { .value()); EXPECT_CALL(encoder_, encodeData(_, true)) .WillOnce(Invoke([&](Buffer::Instance& data, bool) -> void { data.drain(data.length()); })); - pool_callbacks_->onPoolReady(encoder_, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + pool_callbacks_->onPoolReady(encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_flow_control_drained_total") .value()); @@ -6442,13 +6513,13 @@ TEST_F(WatermarkTest, RetryRequestNotComplete) { router_.setDecoderFilterCallbacks(callbacks_); NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillRepeatedly(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); EXPECT_CALL(callbacks_.stream_info_, @@ -6471,7 +6542,7 @@ TEST_F(WatermarkTest, RetryRequestNotComplete) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // This should not trigger a retry as the retry state has been deleted. - EXPECT_CALL(cm_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_(_)); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); @@ -6493,14 +6564,14 @@ TEST_F(RouterTestChildSpan, BasicFlow) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(*child_span, injectContext(_)); - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -6538,14 +6609,14 @@ TEST_F(RouterTestChildSpan, ResetFlow) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(*child_span, injectContext(_)); - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -6589,12 +6660,12 @@ TEST_F(RouterTestChildSpan, CancelFlow) { NiceMock encoder; Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { EXPECT_CALL(*child_span, injectContext(_)); - callbacks.onPoolReady(encoder, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); @@ -6631,14 +6702,14 @@ TEST_F(RouterTestChildSpan, ResetRetryFlow) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; Tracing::MockSpan* child_span_1{new Tracing::MockSpan()}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(*child_span_1, injectContext(_)); - callbacks.onPoolReady(encoder1, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -6673,15 +6744,15 @@ TEST_F(RouterTestChildSpan, ResetRetryFlow) { // We expect this reset to kick off a new request. NiceMock encoder2; Tracing::MockSpan* child_span_2{new Tracing::MockSpan()}; - EXPECT_CALL(cm_.conn_pool_, newStream(_, _)) + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(*child_span_2, injectContext(_)); EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); - callbacks.onPoolReady(encoder2, cm_.conn_pool_.host_, upstream_stream_info_, - Http::Protocol::Http10); + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); diff --git a/test/common/router/router_upstream_log_test.cc b/test/common/router/router_upstream_log_test.cc index 6117949e5d47..184cc54ef5a7 100644 --- a/test/common/router/router_upstream_log_test.cc +++ b/test/common/router/router_upstream_log_test.cc @@ -102,9 +102,9 @@ class RouterUpstreamLogTest : public testing::Test { upstream_locality_.set_zone("to_az"); context_.cluster_manager_.initializeThreadLocalClusters({"fake_cluster"}); - ON_CALL(*context_.cluster_manager_.conn_pool_.host_, address()) + ON_CALL(*context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_, address()) .WillByDefault(Return(host_address_)); - ON_CALL(*context_.cluster_manager_.conn_pool_.host_, locality()) + ON_CALL(*context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_, locality()) .WillByDefault(ReturnRef(upstream_locality_)); router_->downstream_connection_.local_address_ = host_address_; router_->downstream_connection_.remote_address_ = @@ -131,15 +131,16 @@ class RouterUpstreamLogTest : public testing::Test { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(context_.cluster_manager_.conn_pool_, newStream(_, _)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(encoder.stream_, connectionLocalAddress()) .WillRepeatedly(ReturnRef(upstream_local_address1_)); - callbacks.onPoolReady(encoder, context_.cluster_manager_.conn_pool_.host_, - stream_info_, Http::Protocol::Http10); + callbacks.onPoolReady( + encoder, context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_, + stream_info_, Http::Protocol::Http10); return nullptr; })); expectResponseTimerCreate(); @@ -155,7 +156,7 @@ class RouterUpstreamLogTest : public testing::Test { new Http::TestResponseHeaderMapImpl(response_headers_init)); response_headers->setStatus(response_code); - EXPECT_CALL(context_.cluster_manager_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(response_code)); response_decoder->decodeHeaders(std::move(response_headers), false); @@ -170,15 +171,16 @@ class RouterUpstreamLogTest : public testing::Test { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - EXPECT_CALL(context_.cluster_manager_.conn_pool_, newStream(_, _)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.conn_pool_, newStream(_, _)) .WillOnce(Invoke( [&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(encoder1.stream_, connectionLocalAddress()) .WillRepeatedly(ReturnRef(upstream_local_address1_)); - callbacks.onPoolReady(encoder1, context_.cluster_manager_.conn_pool_.host_, - stream_info_, Http::Protocol::Http10); + callbacks.onPoolReady( + encoder1, context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_, + stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); @@ -192,25 +194,27 @@ class RouterUpstreamLogTest : public testing::Test { router_->decodeHeaders(headers, true); router_->retry_state_->expectResetRetry(); - EXPECT_CALL(context_.cluster_manager_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); per_try_timeout_->invokeCallback(); // We expect this reset to kick off a new request. NiceMock encoder2; - EXPECT_CALL(context_.cluster_manager_.conn_pool_, newStream(_, _)) - .WillOnce(Invoke( - [&](Http::ResponseDecoder& decoder, - Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { - response_decoder = &decoder; - EXPECT_CALL(context_.cluster_manager_.conn_pool_.host_->outlier_detector_, - putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, _)); - EXPECT_CALL(encoder2.stream_, connectionLocalAddress()) - .WillRepeatedly(ReturnRef(upstream_local_address2_)); - callbacks.onPoolReady(encoder2, context_.cluster_manager_.conn_pool_.host_, - stream_info_, Http::Protocol::Http10); - return nullptr; - })); + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Invoke([&](Http::ResponseDecoder& decoder, + Http::ConnectionPool::Callbacks& callbacks) + -> Http::ConnectionPool::Cancellable* { + response_decoder = &decoder; + EXPECT_CALL( + context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, _)); + EXPECT_CALL(encoder2.stream_, connectionLocalAddress()) + .WillRepeatedly(ReturnRef(upstream_local_address2_)); + callbacks.onPoolReady(encoder2, + context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_, + stream_info_, Http::Protocol::Http10); + return nullptr; + })); expectPerTryTimerCreate(); router_->retry_state_->callback_(); @@ -218,7 +222,7 @@ class RouterUpstreamLogTest : public testing::Test { EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(context_.cluster_manager_.conn_pool_.host_->outlier_detector_, + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); response_decoder->decodeHeaders(std::move(response_headers), true); } diff --git a/test/common/router/shadow_writer_impl_test.cc b/test/common/router/shadow_writer_impl_test.cc index f6cbfb8f9599..7a5f000ebc5a 100644 --- a/test/common/router/shadow_writer_impl_test.cc +++ b/test/common/router/shadow_writer_impl_test.cc @@ -27,9 +27,10 @@ class ShadowWriterImplTest : public testing::Test { message->headers().setHost(host); cm_.initializeThreadLocalClusters({"foo"}); EXPECT_CALL(cm_, getThreadLocalCluster(Eq("foo"))); - EXPECT_CALL(cm_, httpAsyncClientForCluster("foo")).WillOnce(ReturnRef(cm_.async_client_)); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); auto options = Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(5)); - EXPECT_CALL(cm_.async_client_, send_(_, _, options)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, options)) .WillOnce(Invoke( [&](Http::RequestMessagePtr& inner_message, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -43,7 +44,7 @@ class ShadowWriterImplTest : public testing::Test { Upstream::MockClusterManager cm_; ShadowWriterImpl writer_{cm_}; - Http::MockAsyncClientRequest request_{&cm_.async_client_}; + Http::MockAsyncClientRequest request_{&cm_.thread_local_cluster_.async_client_}; Http::AsyncClient::Callbacks* callback_{}; }; @@ -67,7 +68,7 @@ TEST_F(ShadowWriterImplTest, NoCluster) { Http::RequestMessagePtr message(new Http::RequestMessageImpl()); EXPECT_CALL(cm_, getThreadLocalCluster(Eq("foo"))).WillOnce(Return(nullptr)); - EXPECT_CALL(cm_, httpAsyncClientForCluster("foo")).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).Times(0); auto options = Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(5)); writer_.shadow("foo", std::move(message), options); } diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 77fbad57cb3c..749c294314d7 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -910,7 +910,7 @@ class TcpProxyTest : public testing::Test { { testing::InSequence sequence; for (uint32_t i = 0; i < connections; i++) { - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("fake_cluster", _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(&conn_pool_)) .RetiresOnSaturation(); EXPECT_CALL(conn_pool_, newConnection(_)) @@ -922,7 +922,7 @@ class TcpProxyTest : public testing::Test { })) .RetiresOnSaturation(); } - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("fake_cluster", _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillRepeatedly(Return(nullptr)); } @@ -1405,8 +1405,8 @@ TEST_F(TcpProxyTest, WeightedClusterWithMetadataMatch) { Upstream::LoadBalancerContext* context; EXPECT_CALL(factory_context_.api_.random_, random()).WillOnce(Return(0)); - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("cluster1", _, _)) - .WillOnce(DoAll(SaveArg<2>(&context), Return(nullptr))); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) + .WillOnce(DoAll(SaveArg<1>(&context), Return(nullptr))); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); EXPECT_NE(nullptr, context); @@ -1429,8 +1429,8 @@ TEST_F(TcpProxyTest, WeightedClusterWithMetadataMatch) { Upstream::LoadBalancerContext* context; EXPECT_CALL(factory_context_.api_.random_, random()).WillOnce(Return(2)); - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("cluster2", _, _)) - .WillOnce(DoAll(SaveArg<2>(&context), Return(nullptr))); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) + .WillOnce(DoAll(SaveArg<1>(&context), Return(nullptr))); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); EXPECT_NE(nullptr, context); @@ -1468,8 +1468,8 @@ TEST_F(TcpProxyTest, StreamInfoDynamicMetadata) { Upstream::LoadBalancerContext* context; - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster(_, _, _)) - .WillOnce(DoAll(SaveArg<2>(&context), Return(nullptr))); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) + .WillOnce(DoAll(SaveArg<1>(&context), Return(nullptr))); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); EXPECT_NE(nullptr, context); @@ -1522,8 +1522,8 @@ TEST_F(TcpProxyTest, StreamInfoDynamicMetadataAndConfigMerged) { Upstream::LoadBalancerContext* context; - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster(_, _, _)) - .WillOnce(DoAll(SaveArg<2>(&context), Return(nullptr))); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) + .WillOnce(DoAll(SaveArg<1>(&context), Return(nullptr))); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); EXPECT_NE(nullptr, context); @@ -1973,7 +1973,7 @@ TEST_F(TcpProxyRoutingTest, DEPRECATED_FEATURE_TEST(NonRoutableConnection)) { connection_.local_address_ = std::make_shared("1.2.3.4", 10000); // Expect filter to try to open a connection to the fallback cluster. - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("fallback_cluster", _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(nullptr)); filter_->onNewConnection(); @@ -1995,7 +1995,7 @@ TEST_F(TcpProxyRoutingTest, DEPRECATED_FEATURE_TEST(RoutableConnection)) { connection_.local_address_ = std::make_shared("1.2.3.4", 9999); // Expect filter to try to open a connection to specified cluster. - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("fake_cluster", _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(nullptr)); filter_->onNewConnection(); @@ -2016,8 +2016,7 @@ TEST_F(TcpProxyRoutingTest, DEPRECATED_FEATURE_TEST(UseClusterFromPerConnectionC StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection); // Expect filter to try to open a connection to specified cluster. - EXPECT_CALL(factory_context_.cluster_manager_, - tcpConnPoolForCluster("filter_state_cluster", _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(nullptr)); filter_->onNewConnection(); @@ -2035,11 +2034,10 @@ TEST_F(TcpProxyRoutingTest, DEPRECATED_FEATURE_TEST(UpstreamServerName)) { // Expect filter to try to open a connection to a cluster with the transport socket options with // override-server-name - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster(_, _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce( - Invoke([](const std::string& cluster, Upstream::ResourcePriority, + Invoke([](Upstream::ResourcePriority, Upstream::LoadBalancerContext* context) -> Tcp::ConnectionPool::Instance* { - EXPECT_EQ(cluster, "fake_cluster"); Network::TransportSocketOptionsSharedPtr transport_socket_options = context->upstreamTransportSocketOptions(); EXPECT_NE(transport_socket_options, nullptr); @@ -2067,11 +2065,10 @@ TEST_F(TcpProxyRoutingTest, DEPRECATED_FEATURE_TEST(ApplicationProtocols)) { // Expect filter to try to open a connection to a cluster with the transport socket options with // override-application-protocol - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster(_, _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce( - Invoke([](const std::string& cluster, Upstream::ResourcePriority, + Invoke([](Upstream::ResourcePriority, Upstream::LoadBalancerContext* context) -> Tcp::ConnectionPool::Instance* { - EXPECT_EQ(cluster, "fake_cluster"); Network::TransportSocketOptionsSharedPtr transport_socket_options = context->upstreamTransportSocketOptions(); EXPECT_NE(transport_socket_options, nullptr); @@ -2112,7 +2109,7 @@ TEST_F(TcpProxyNonDeprecatedConfigRoutingTest, ClusterNameSet) { connection_.local_address_ = std::make_shared("1.2.3.4", 9999); // Expect filter to try to open a connection to specified cluster. - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster("fake_cluster", _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(nullptr)); absl::optional cluster_info; EXPECT_CALL(connection_.stream_info_, setUpstreamClusterInfo(_)) @@ -2164,11 +2161,10 @@ class TcpProxyHashingTest : public testing::Test { TEST_F(TcpProxyHashingTest, HashWithSourceIp) { setup(); initializeFilter(); - EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster(_, _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce( - Invoke([](const std::string& cluster, Upstream::ResourcePriority, + Invoke([](Upstream::ResourcePriority, Upstream::LoadBalancerContext* context) -> Tcp::ConnectionPool::Instance* { - EXPECT_EQ(cluster, "fake_cluster"); EXPECT_TRUE(context->computeHashKey().has_value()); return nullptr; })); diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 41aabe78ba66..d8f745f68fb5 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -855,20 +855,6 @@ TEST_F(ClusterManagerImplTest, UnknownCluster) { create(parseBootstrapFromV3Json(json)); EXPECT_EQ(nullptr, cluster_manager_->getThreadLocalCluster("hello")); - EXPECT_EQ(nullptr, cluster_manager_->httpConnPoolForCluster("hello", ResourcePriority::Default, - Http::Protocol::Http2, nullptr)); - EXPECT_EQ(nullptr, - cluster_manager_->tcpConnPoolForCluster("hello", ResourcePriority::Default, nullptr)); - EXPECT_THROW(cluster_manager_->tcpConnForCluster("hello", nullptr), EnvoyException); - - NiceMock example_com_context; - ON_CALL(example_com_context, upstreamTransportSocketOptions()) - .WillByDefault(Return(std::make_shared("example.com"))); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnPoolForCluster("hello", ResourcePriority::Default, - &example_com_context)); - EXPECT_THROW(cluster_manager_->tcpConnForCluster("hello", &example_com_context), EnvoyException); - - EXPECT_THROW(cluster_manager_->httpAsyncClientForCluster("hello"), EnvoyException); factory_.tls_.shutdownThread(); } @@ -900,7 +886,7 @@ TEST_F(ClusterManagerImplTest, VerifyBufferLimits) { EXPECT_CALL(*connection, setBufferLimits(8192)); EXPECT_CALL(factory_.tls_.dispatcher_, createClientConnection_(_, _, _, _)) .WillOnce(Return(connection)); - auto conn_data = cluster_manager_->tcpConnForCluster("cluster_1", nullptr); + auto conn_data = cluster_manager_->getThreadLocalCluster("cluster_1")->tcpConn(nullptr); EXPECT_EQ(connection, conn_data.connection_.get()); factory_.tls_.shutdownThread(); } @@ -1505,13 +1491,13 @@ TEST_F(ClusterManagerImplTest, DynamicAddRemove) { EXPECT_EQ(1UL, cluster_manager_->clusters().active_clusters_.size()); Http::ConnectionPool::MockInstance* cp = new Http::ConnectionPool::MockInstance(); EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)).WillOnce(Return(cp)); - EXPECT_EQ(cp, cluster_manager_->httpConnPoolForCluster("fake_cluster", ResourcePriority::Default, - Http::Protocol::Http11, nullptr)); + EXPECT_EQ(cp, cluster_manager_->getThreadLocalCluster("fake_cluster") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); Tcp::ConnectionPool::MockInstance* cp2 = new Tcp::ConnectionPool::MockInstance(); EXPECT_CALL(factory_, allocateTcpConnPool_(_)).WillOnce(Return(cp2)); - EXPECT_EQ(cp2, cluster_manager_->tcpConnPoolForCluster("fake_cluster", ResourcePriority::Default, - nullptr)); + EXPECT_EQ(cp2, cluster_manager_->getThreadLocalCluster("fake_cluster") + ->tcpConnPool(ResourcePriority::Default, nullptr)); Network::MockClientConnection* connection = new Network::MockClientConnection(); ON_CALL(*cluster2->info_, features()) @@ -1520,7 +1506,7 @@ TEST_F(ClusterManagerImplTest, DynamicAddRemove) { .WillOnce(Return(connection)); EXPECT_CALL(*connection, setBufferLimits(_)); EXPECT_CALL(*connection, addConnectionCallbacks(_)); - auto conn_info = cluster_manager_->tcpConnForCluster("fake_cluster", nullptr); + auto conn_info = cluster_manager_->getThreadLocalCluster("fake_cluster")->tcpConn(nullptr); EXPECT_EQ(conn_info.connection_.get(), connection); // Now remove the cluster. This should drain the connection pools, but not affect @@ -1662,8 +1648,8 @@ TEST_F(ClusterManagerImplTest, CloseHttpConnectionsOnHealthFailure) { create(parseBootstrapFromV3Json(json)); EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)).WillOnce(Return(cp1)); - cluster_manager_->httpConnPoolForCluster("some_cluster", ResourcePriority::Default, - Http::Protocol::Http11, nullptr); + cluster_manager_->getThreadLocalCluster("some_cluster") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr); outlier_detector.runCallbacks(test_host); health_checker.runCallbacks(test_host, HealthTransition::Unchanged); @@ -1673,8 +1659,8 @@ TEST_F(ClusterManagerImplTest, CloseHttpConnectionsOnHealthFailure) { outlier_detector.runCallbacks(test_host); EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)).WillOnce(Return(cp2)); - cluster_manager_->httpConnPoolForCluster("some_cluster", ResourcePriority::High, - Http::Protocol::Http11, nullptr); + cluster_manager_->getThreadLocalCluster("some_cluster") + ->httpConnPool(ResourcePriority::High, Http::Protocol::Http11, nullptr); } // Order of these calls is implementation dependent, so can't sequence them! @@ -1725,7 +1711,8 @@ TEST_F(ClusterManagerImplTest, CloseTcpConnectionPoolsOnHealthFailure) { create(parseBootstrapFromV3Json(json)); EXPECT_CALL(factory_, allocateTcpConnPool_(_)).WillOnce(Return(cp1)); - cluster_manager_->tcpConnPoolForCluster("some_cluster", ResourcePriority::Default, nullptr); + cluster_manager_->getThreadLocalCluster("some_cluster") + ->tcpConnPool(ResourcePriority::Default, nullptr); outlier_detector.runCallbacks(test_host); health_checker.runCallbacks(test_host, HealthTransition::Unchanged); @@ -1735,7 +1722,8 @@ TEST_F(ClusterManagerImplTest, CloseTcpConnectionPoolsOnHealthFailure) { outlier_detector.runCallbacks(test_host); EXPECT_CALL(factory_, allocateTcpConnPool_(_)).WillOnce(Return(cp2)); - cluster_manager_->tcpConnPoolForCluster("some_cluster", ResourcePriority::High, nullptr); + cluster_manager_->getThreadLocalCluster("some_cluster") + ->tcpConnPool(ResourcePriority::High, nullptr); } // Order of these calls is implementation dependent, so can't sequence them! @@ -1797,7 +1785,7 @@ TEST_F(ClusterManagerImplTest, CloseTcpConnectionsOnHealthFailure) { EXPECT_CALL(factory_.tls_.dispatcher_, createClientConnection_(_, _, _, _)) .WillOnce(Return(connection1)); - conn_info1 = cluster_manager_->tcpConnForCluster("some_cluster", nullptr); + conn_info1 = cluster_manager_->getThreadLocalCluster("some_cluster")->tcpConn(nullptr); outlier_detector.runCallbacks(test_host); health_checker.runCallbacks(test_host, HealthTransition::Unchanged); @@ -1809,11 +1797,11 @@ TEST_F(ClusterManagerImplTest, CloseTcpConnectionsOnHealthFailure) { connection1 = new NiceMock(); EXPECT_CALL(factory_.tls_.dispatcher_, createClientConnection_(_, _, _, _)) .WillOnce(Return(connection1)); - conn_info1 = cluster_manager_->tcpConnForCluster("some_cluster", nullptr); + conn_info1 = cluster_manager_->getThreadLocalCluster("some_cluster")->tcpConn(nullptr); EXPECT_CALL(factory_.tls_.dispatcher_, createClientConnection_(_, _, _, _)) .WillOnce(Return(connection2)); - conn_info2 = cluster_manager_->tcpConnForCluster("some_cluster", nullptr); + conn_info2 = cluster_manager_->getThreadLocalCluster("some_cluster")->tcpConn(nullptr); } // Order of these calls is implementation dependent, so can't sequence them! @@ -1870,7 +1858,7 @@ TEST_F(ClusterManagerImplTest, DoNotCloseTcpConnectionsOnHealthFailure) { EXPECT_CALL(factory_.tls_.dispatcher_, createClientConnection_(_, _, _, _)) .WillOnce(Return(connection1)); - conn_info1 = cluster_manager_->tcpConnForCluster("some_cluster", nullptr); + conn_info1 = cluster_manager_->getThreadLocalCluster("some_cluster")->tcpConn(nullptr); outlier_detector.runCallbacks(test_host); health_checker.runCallbacks(test_host, HealthTransition::Unchanged); @@ -1917,11 +1905,11 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemove) { EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); // Test for no hosts returning the correct values before we have hosts. - EXPECT_EQ(nullptr, cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, - nullptr)); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnForCluster("cluster_1", nullptr).connection_); + const auto thread_local_cluster = cluster_manager_->getThreadLocalCluster("cluster_1"); + EXPECT_EQ(nullptr, thread_local_cluster->httpConnPool(ResourcePriority::Default, + Http::Protocol::Http11, nullptr)); + EXPECT_EQ(nullptr, thread_local_cluster->tcpConnPool(ResourcePriority::Default, nullptr)); + EXPECT_EQ(nullptr, thread_local_cluster->tcpConn(nullptr).connection_); EXPECT_EQ(3UL, factory_.stats_.counter("cluster.cluster_1.upstream_cx_none_healthy").value()); // Set up for an initialize callback. @@ -1942,18 +1930,18 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemove) { .WillRepeatedly(ReturnNew()); // This should provide us a CP for each of the above hosts. - Http::ConnectionPool::MockInstance* cp1 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - Http::ConnectionPool::MockInstance* cp2 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - Http::ConnectionPool::MockInstance* cp1_high = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::High, Http::Protocol::Http11, nullptr)); - Http::ConnectionPool::MockInstance* cp2_high = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::High, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp1 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp2 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp1_high = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::High, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp2_high = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::High, Http::Protocol::Http11, nullptr)); EXPECT_NE(cp1, cp2); EXPECT_NE(cp1_high, cp2_high); @@ -1970,13 +1958,17 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemove) { // This should provide us a CP for each of the above hosts. Tcp::ConnectionPool::MockInstance* tcp1 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); Tcp::ConnectionPool::MockInstance* tcp2 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); Tcp::ConnectionPool::MockInstance* tcp1_high = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::High, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::High, nullptr)); Tcp::ConnectionPool::MockInstance* tcp2_high = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::High, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::High, nullptr)); EXPECT_NE(tcp1, tcp2); EXPECT_NE(tcp1_high, tcp2_high); @@ -2002,19 +1994,21 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemove) { tcp_drained_cb_high = nullptr; // Make sure we get back the same connection pool for the 2nd host as we did before the change. - Http::ConnectionPool::MockInstance* cp3 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - Http::ConnectionPool::MockInstance* cp3_high = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::High, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp3 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp3_high = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::High, Http::Protocol::Http11, nullptr)); EXPECT_EQ(cp2, cp3); EXPECT_EQ(cp2_high, cp3_high); Tcp::ConnectionPool::MockInstance* tcp3 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); Tcp::ConnectionPool::MockInstance* tcp3_high = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::High, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::High, nullptr)); EXPECT_EQ(tcp2, tcp3); EXPECT_EQ(tcp2_high, tcp3_high); @@ -2079,21 +2073,19 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveWithTls) { .WillByDefault(Return(std::make_shared("ibm.com"))); // Test for no hosts returning the correct values before we have hosts. - EXPECT_EQ(nullptr, cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, - nullptr)); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnForCluster("cluster_1", nullptr).connection_); - - EXPECT_EQ(nullptr, cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, - &example_com_context)); + const auto thread_local_cluster = cluster_manager_->getThreadLocalCluster("cluster_1"); + EXPECT_EQ(nullptr, thread_local_cluster->httpConnPool(ResourcePriority::Default, + Http::Protocol::Http11, nullptr)); + EXPECT_EQ(nullptr, thread_local_cluster->tcpConnPool(ResourcePriority::Default, nullptr)); + EXPECT_EQ(nullptr, thread_local_cluster->tcpConn(nullptr).connection_); + EXPECT_EQ(nullptr, - cluster_manager_->tcpConnForCluster("cluster_1", &ibm_com_context).connection_); + thread_local_cluster->tcpConnPool(ResourcePriority::Default, &example_com_context)); + EXPECT_EQ(nullptr, thread_local_cluster->tcpConn(&ibm_com_context).connection_); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, - &ibm_com_context)); EXPECT_EQ(nullptr, - cluster_manager_->tcpConnForCluster("cluster_1", &ibm_com_context).connection_); + thread_local_cluster->tcpConnPool(ResourcePriority::Default, &ibm_com_context)); + EXPECT_EQ(nullptr, thread_local_cluster->tcpConn(&ibm_com_context).connection_); EXPECT_EQ(7UL, factory_.stats_.counter("cluster.cluster_1.upstream_cx_none_healthy").value()); @@ -2115,18 +2107,18 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveWithTls) { .WillRepeatedly(ReturnNew()); // This should provide us a CP for each of the above hosts. - Http::ConnectionPool::MockInstance* cp1 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - Http::ConnectionPool::MockInstance* cp2 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - Http::ConnectionPool::MockInstance* cp1_high = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::High, Http::Protocol::Http11, nullptr)); - Http::ConnectionPool::MockInstance* cp2_high = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::High, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp1 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp2 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp1_high = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::High, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp2_high = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::High, Http::Protocol::Http11, nullptr)); EXPECT_NE(cp1, cp2); EXPECT_NE(cp1_high, cp2_high); @@ -2143,27 +2135,35 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveWithTls) { // This should provide us a CP for each of the above hosts, and for different SNIs Tcp::ConnectionPool::MockInstance* tcp1 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); Tcp::ConnectionPool::MockInstance* tcp2 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); Tcp::ConnectionPool::MockInstance* tcp1_high = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::High, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::High, nullptr)); Tcp::ConnectionPool::MockInstance* tcp2_high = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::High, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::High, nullptr)); Tcp::ConnectionPool::MockInstance* tcp1_example_com = - dynamic_cast(cluster_manager_->tcpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, &example_com_context)); + dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, &example_com_context)); Tcp::ConnectionPool::MockInstance* tcp2_example_com = - dynamic_cast(cluster_manager_->tcpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, &example_com_context)); + dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, &example_com_context)); Tcp::ConnectionPool::MockInstance* tcp1_ibm_com = - dynamic_cast(cluster_manager_->tcpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, &ibm_com_context)); + dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, &ibm_com_context)); Tcp::ConnectionPool::MockInstance* tcp2_ibm_com = - dynamic_cast(cluster_manager_->tcpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, &ibm_com_context)); + dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, &ibm_com_context)); EXPECT_NE(tcp1, tcp2); EXPECT_NE(tcp1_high, tcp2_high); @@ -2226,32 +2226,38 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveWithTls) { tcp_drained_cb_ibm_com = nullptr; // Make sure we get back the same connection pool for the 2nd host as we did before the change. - Http::ConnectionPool::MockInstance* cp3 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - Http::ConnectionPool::MockInstance* cp3_high = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::High, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp3 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp3_high = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::High, Http::Protocol::Http11, nullptr)); EXPECT_EQ(cp2, cp3); EXPECT_EQ(cp2_high, cp3_high); Tcp::ConnectionPool::MockInstance* tcp3 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); Tcp::ConnectionPool::MockInstance* tcp3_high = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::High, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::High, nullptr)); Tcp::ConnectionPool::MockInstance* tcp3_example_com = - dynamic_cast(cluster_manager_->tcpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, &example_com_context)); + dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, &example_com_context)); Tcp::ConnectionPool::MockInstance* tcp3_example_com_with_san = - dynamic_cast(cluster_manager_->tcpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, &example_com_context_with_san)); + dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, &example_com_context_with_san)); Tcp::ConnectionPool::MockInstance* tcp3_example_com_with_san2 = - dynamic_cast(cluster_manager_->tcpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, &example_com_context_with_san2)); + dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, &example_com_context_with_san2)); Tcp::ConnectionPool::MockInstance* tcp3_ibm_com = - dynamic_cast(cluster_manager_->tcpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, &ibm_com_context)); + dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, &ibm_com_context)); EXPECT_EQ(tcp2, tcp3); EXPECT_EQ(tcp2_high, tcp3_high); @@ -2398,12 +2404,13 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveDefaultPriority) { EXPECT_CALL(factory_, allocateTcpConnPool_(_)) .WillOnce(ReturnNew()); - Http::ConnectionPool::MockInstance* cp = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); Tcp::ConnectionPool::MockInstance* tcp = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); // Immediate drain, since this can happen with the HTTP codecs. EXPECT_CALL(*cp, addDrainedCallback(_)) @@ -2479,12 +2486,13 @@ TEST_F(ClusterManagerImplTest, ConnPoolDestroyWithDraining) { MockTcpConnPoolWithDestroy* mock_tcp = new MockTcpConnPoolWithDestroy(); EXPECT_CALL(factory_, allocateTcpConnPool_(_)).WillOnce(Return(mock_tcp)); - Http::ConnectionPool::MockInstance* cp = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); Tcp::ConnectionPool::MockInstance* tcp = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); // Remove the first host, this should lead to the cp being drained. Http::ConnectionPool::Instance::DrainedCb drained_cb; @@ -2527,11 +2535,13 @@ TEST_F(ClusterManagerImplTest, OriginalDstInitialization) { EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); // Test for no hosts returning the correct values before we have hosts. - EXPECT_EQ(nullptr, cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, - nullptr)); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnForCluster("cluster_1", nullptr).connection_); + EXPECT_EQ(nullptr, + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + EXPECT_EQ(nullptr, cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); + EXPECT_EQ(nullptr, + cluster_manager_->getThreadLocalCluster("cluster_1")->tcpConn(nullptr).connection_); EXPECT_EQ(3UL, factory_.stats_.counter("cluster.cluster_1.upstream_cx_none_healthy").value()); factory_.tls_.shutdownThread(); @@ -2925,8 +2935,9 @@ TEST_F(ClusterManagerImplTest, UpstreamSocketOptionsPassedToConnPool) { EXPECT_CALL(context, upstreamSocketOptions()).WillOnce(Return(options_to_return)); EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)).WillOnce(Return(to_create)); - Http::ConnectionPool::Instance* cp = cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context); + Http::ConnectionPool::Instance* cp = + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, &context); EXPECT_NE(nullptr, cp); } @@ -2947,17 +2958,21 @@ TEST_F(ClusterManagerImplTest, UpstreamSocketOptionsUsedInConnPoolHash) { EXPECT_CALL(context2, upstreamSocketOptions()).WillRepeatedly(Return(options2)); EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)).WillOnce(Return(to_create1)); - Http::ConnectionPool::Instance* cp1 = cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context1); + Http::ConnectionPool::Instance* cp1 = + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, &context1); EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)).WillOnce(Return(to_create2)); - Http::ConnectionPool::Instance* cp2 = cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context2); + Http::ConnectionPool::Instance* cp2 = + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, &context2); - Http::ConnectionPool::Instance* should_be_cp1 = cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context1); - Http::ConnectionPool::Instance* should_be_cp2 = cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context2); + Http::ConnectionPool::Instance* should_be_cp1 = + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, &context1); + Http::ConnectionPool::Instance* should_be_cp2 = + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, &context2); // The different upstream options should lead to different hashKeys, thus different pools. EXPECT_NE(cp1, cp2); @@ -2977,8 +2992,9 @@ TEST_F(ClusterManagerImplTest, UpstreamSocketOptionsNullIsOkay) { EXPECT_CALL(context, upstreamSocketOptions()).WillOnce(Return(options_to_return)); EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)).WillOnce(Return(to_create)); - Http::ConnectionPool::Instance* cp = cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context); + Http::ConnectionPool::Instance* cp = + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, &context); EXPECT_NE(nullptr, cp); } @@ -3040,7 +3056,7 @@ TEST_F(ClusterManagerImplTest, AddUpstreamFilters) { EXPECT_CALL(*connection, addFilter(_)).Times(0); EXPECT_CALL(factory_.tls_.dispatcher_, createClientConnection_(_, _, _, _)) .WillOnce(Return(connection)); - auto conn_data = cluster_manager_->tcpConnForCluster("cluster_1", nullptr); + auto conn_data = cluster_manager_->getThreadLocalCluster("cluster_1")->tcpConn(nullptr); EXPECT_EQ(connection, conn_data.connection_.get()); factory_.tls_.shutdownThread(); } @@ -3359,7 +3375,7 @@ class SockoptsTest : public ClusterManagerImplTest { } return connection_; })); - cluster_manager_->tcpConnForCluster("SockoptsCluster", nullptr); + cluster_manager_->getThreadLocalCluster("SockoptsCluster")->tcpConn(nullptr); } void expectSetsockoptFreebind() { @@ -3385,7 +3401,7 @@ class SockoptsTest : public ClusterManagerImplTest { EXPECT_EQ(nullptr, options.get()); return connection_; })); - auto conn_data = cluster_manager_->tcpConnForCluster("SockoptsCluster", nullptr); + auto conn_data = cluster_manager_->getThreadLocalCluster("SockoptsCluster")->tcpConn(nullptr); EXPECT_EQ(connection_, conn_data.connection_.get()); } @@ -3620,7 +3636,7 @@ class TcpKeepaliveTest : public ClusterManagerImplTest { options, socket, envoy::config::core::v3::SocketOption::STATE_PREBIND))); return connection_; })); - cluster_manager_->tcpConnForCluster("TcpKeepaliveCluster", nullptr); + cluster_manager_->getThreadLocalCluster("TcpKeepaliveCluster")->tcpConn(nullptr); return; } NiceMock os_sys_calls; @@ -3678,7 +3694,8 @@ class TcpKeepaliveTest : public ClusterManagerImplTest { return {0, 0}; })); } - auto conn_data = cluster_manager_->tcpConnForCluster("TcpKeepaliveCluster", nullptr); + auto conn_data = + cluster_manager_->getThreadLocalCluster("TcpKeepaliveCluster")->tcpConn(nullptr); EXPECT_EQ(connection_, conn_data.connection_.get()); } @@ -3701,7 +3718,8 @@ class TcpKeepaliveTest : public ClusterManagerImplTest { EXPECT_EQ(1, *static_cast(optval)); return {0, 0}; })); - auto conn_data = cluster_manager_->tcpConnForCluster("TcpKeepaliveCluster", nullptr); + auto conn_data = + cluster_manager_->getThreadLocalCluster("TcpKeepaliveCluster")->tcpConn(nullptr); EXPECT_EQ(connection_, conn_data.connection_.get()); } @@ -3715,7 +3733,8 @@ class TcpKeepaliveTest : public ClusterManagerImplTest { EXPECT_EQ(nullptr, options.get()); return connection_; })); - auto conn_data = cluster_manager_->tcpConnForCluster("TcpKeepaliveCluster", nullptr); + auto conn_data = + cluster_manager_->getThreadLocalCluster("TcpKeepaliveCluster")->tcpConn(nullptr); EXPECT_EQ(connection_, conn_data.connection_.get()); } @@ -3851,11 +3870,13 @@ TEST_F(ClusterManagerImplTest, ConnPoolsDrainedOnHostSetChange) { EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); // Verify that we get no hosts when the HostSet is empty. - EXPECT_EQ(nullptr, cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, - nullptr)); - EXPECT_EQ(nullptr, cluster_manager_->tcpConnForCluster("cluster_1", nullptr).connection_); + EXPECT_EQ(nullptr, + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + EXPECT_EQ(nullptr, cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); + EXPECT_EQ(nullptr, + cluster_manager_->getThreadLocalCluster("cluster_1")->tcpConn(nullptr).connection_); Cluster& cluster = cluster_manager_->activeClusters().begin()->second; @@ -3884,19 +3905,21 @@ TEST_F(ClusterManagerImplTest, ConnPoolsDrainedOnHostSetChange) { .WillRepeatedly(ReturnNew()); // This should provide us a CP for each of the above hosts. - Http::ConnectionPool::MockInstance* cp1 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp1 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); // Create persistent connection for host2. - Http::ConnectionPool::MockInstance* cp2 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http2, nullptr)); + Http::ConnectionPool::MockInstance* cp2 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http2, nullptr)); Tcp::ConnectionPool::MockInstance* tcp1 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); Tcp::ConnectionPool::MockInstance* tcp2 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); EXPECT_NE(cp1, cp2); EXPECT_NE(tcp1, tcp2); @@ -3922,11 +3945,13 @@ TEST_F(ClusterManagerImplTest, ConnPoolsDrainedOnHostSetChange) { hosts_removed, 100); // Recreate connection pool for host1. - cp1 = dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + cp1 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); tcp1 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); HostSharedPtr host3 = makeTestHost(cluster.info(), "tcp://127.0.0.1:82", time_system_); @@ -3988,12 +4013,13 @@ TEST_F(ClusterManagerImplTest, ConnPoolsNotDrainedOnHostSetChange) { .WillRepeatedly(ReturnNew()); // This should provide us a CP for each of the above hosts. - Http::ConnectionPool::MockInstance* cp1 = - dynamic_cast(cluster_manager_->httpConnPoolForCluster( - "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); + Http::ConnectionPool::MockInstance* cp1 = dynamic_cast( + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr)); Tcp::ConnectionPool::MockInstance* tcp1 = dynamic_cast( - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr)); HostSharedPtr host2 = makeTestHost(cluster.info(), "tcp://127.0.0.1:82", time_system_); HostVector hosts_added; @@ -4120,8 +4146,8 @@ TEST_F(ClusterManagerImplTest, ConnectionPoolPerDownstreamConnection) { EXPECT_CALL(downstream_connection, hashKey) .WillOnce(Invoke([i](std::vector& hash_key) { hash_key.push_back(i); })); EXPECT_EQ(conn_pool_vector.back(), - cluster_manager_->httpConnPoolForCluster("cluster_1", ResourcePriority::Default, - Http::Protocol::Http11, &lb_context)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, &lb_context)); } // Check that the first entry is still in the pool map @@ -4129,8 +4155,8 @@ TEST_F(ClusterManagerImplTest, ConnectionPoolPerDownstreamConnection) { hash_key.push_back(0); })); EXPECT_EQ(conn_pool_vector.front(), - cluster_manager_->httpConnPoolForCluster("cluster_1", ResourcePriority::Default, - Http::Protocol::Http11, &lb_context)); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, &lb_context)); } class PrefetchTest : public ClusterManagerImplTest { @@ -4192,13 +4218,14 @@ TEST_F(PrefetchTest, PrefetchOff) { EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)) .Times(1) .WillRepeatedly(ReturnNew()); - cluster_manager_->httpConnPoolForCluster("cluster_1", ResourcePriority::Default, - Http::Protocol::Http11, nullptr); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr); EXPECT_CALL(factory_, allocateTcpConnPool_(_)) .Times(1) .WillRepeatedly(ReturnNew()); - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr); } TEST_F(PrefetchTest, PrefetchOn) { @@ -4209,13 +4236,14 @@ TEST_F(PrefetchTest, PrefetchOn) { EXPECT_CALL(factory_, allocateConnPool_(_, _, _, _)) .Times(2) .WillRepeatedly(ReturnNew>()); - cluster_manager_->httpConnPoolForCluster("cluster_1", ResourcePriority::Default, - Http::Protocol::Http11, nullptr); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->httpConnPool(ResourcePriority::Default, Http::Protocol::Http11, nullptr); EXPECT_CALL(factory_, allocateTcpConnPool_(_)) .Times(2) .WillRepeatedly(ReturnNew>()); - cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, nullptr); + cluster_manager_->getThreadLocalCluster("cluster_1") + ->tcpConnPool(ResourcePriority::Default, nullptr); } } // namespace diff --git a/test/common/upstream/cluster_update_tracker_test.cc b/test/common/upstream/cluster_update_tracker_test.cc index 7d80ef78e453..7081841e0ae7 100644 --- a/test/common/upstream/cluster_update_tracker_test.cc +++ b/test/common/upstream/cluster_update_tracker_test.cc @@ -31,8 +31,7 @@ TEST_F(ClusterUpdateTrackerTest, ClusterDoesNotExistAtConstructionTime) { ClusterUpdateTracker cluster_tracker(cm_, cluster_name_); - EXPECT_FALSE(cluster_tracker.exists()); - EXPECT_EQ(cluster_tracker.info(), nullptr); + EXPECT_FALSE(cluster_tracker.threadLocalCluster().has_value()); } TEST_F(ClusterUpdateTrackerTest, ClusterDoesExistAtConstructionTime) { @@ -40,8 +39,8 @@ TEST_F(ClusterUpdateTrackerTest, ClusterDoesExistAtConstructionTime) { ClusterUpdateTracker cluster_tracker(cm_, cluster_name_); - EXPECT_TRUE(cluster_tracker.exists()); - EXPECT_EQ(cluster_tracker.info(), expected_.cluster_.info_); + EXPECT_TRUE(cluster_tracker.threadLocalCluster().has_value()); + EXPECT_EQ(cluster_tracker.threadLocalCluster()->get().info(), expected_.cluster_.info_); } TEST_F(ClusterUpdateTrackerTest, ShouldProperlyHandleUpdateCallbacks) { @@ -49,41 +48,36 @@ TEST_F(ClusterUpdateTrackerTest, ShouldProperlyHandleUpdateCallbacks) { ClusterUpdateTracker cluster_tracker(cm_, cluster_name_); - { - EXPECT_FALSE(cluster_tracker.exists()); - EXPECT_EQ(cluster_tracker.info(), nullptr); - } + { EXPECT_FALSE(cluster_tracker.threadLocalCluster().has_value()); } { // Simulate addition of an irrelevant cluster. cluster_tracker.onClusterAddOrUpdate(irrelevant_); - EXPECT_FALSE(cluster_tracker.exists()); - EXPECT_EQ(cluster_tracker.info(), nullptr); + EXPECT_FALSE(cluster_tracker.threadLocalCluster().has_value()); } { // Simulate addition of the relevant cluster. cluster_tracker.onClusterAddOrUpdate(expected_); - EXPECT_TRUE(cluster_tracker.exists()); - EXPECT_EQ(cluster_tracker.info(), expected_.cluster_.info_); + ASSERT_TRUE(cluster_tracker.threadLocalCluster().has_value()); + EXPECT_EQ(cluster_tracker.threadLocalCluster()->get().info(), expected_.cluster_.info_); } { // Simulate removal of an irrelevant cluster. cluster_tracker.onClusterRemoval(irrelevant_.cluster_.info_->name_); - EXPECT_TRUE(cluster_tracker.exists()); - EXPECT_EQ(cluster_tracker.info(), expected_.cluster_.info_); + ASSERT_TRUE(cluster_tracker.threadLocalCluster().has_value()); + EXPECT_EQ(cluster_tracker.threadLocalCluster()->get().info(), expected_.cluster_.info_); } { // Simulate removal of the relevant cluster. cluster_tracker.onClusterRemoval(cluster_name_); - EXPECT_FALSE(cluster_tracker.exists()); - EXPECT_EQ(cluster_tracker.info(), nullptr); + EXPECT_FALSE(cluster_tracker.threadLocalCluster().has_value()); } } diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index c66bd6b583b1..474b1291e63e 100644 --- a/test/config_test/config_test.cc +++ b/test/config_test/config_test.cc @@ -94,7 +94,7 @@ class ConfigTest { server_.dnsResolver(), ssl_context_manager_, server_.dispatcher(), server_.localInfo(), server_.secretManager(), server_.messageValidationContext(), *api_, server_.httpContext(), server_.grpcContext(), server_.routerContext(), server_.accessLogManager(), - server_.singletonManager(), time_system_); + server_.singletonManager()); ON_CALL(server_, clusterManager()).WillByDefault(Invoke([&]() -> Upstream::ClusterManager& { return *main_config.clusterManager(); diff --git a/test/extensions/common/wasm/wasm_test.cc b/test/extensions/common/wasm/wasm_test.cc index 8cc954a74034..19e5b016ae72 100644 --- a/test/extensions/common/wasm/wasm_test.cc +++ b/test/extensions/common/wasm/wasm_test.cc @@ -758,9 +758,10 @@ TEST_P(WasmCommonTest, RemoteCode) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager, httpAsyncClientForCluster("example_com")) - .WillOnce(ReturnRef(cluster_manager.async_client_)); - EXPECT_CALL(cluster_manager.async_client_, send_(_, _, _)) + cluster_manager.initializeThreadLocalClusters({"example_com"}); + EXPECT_CALL(cluster_manager.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cluster_manager.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -866,9 +867,10 @@ TEST_P(WasmCommonTest, RemoteCodeMultipleRetry) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager, httpAsyncClientForCluster("example_com")) - .WillRepeatedly(ReturnRef(cluster_manager.async_client_)); - EXPECT_CALL(cluster_manager.async_client_, send_(_, _, _)) + cluster_manager.initializeThreadLocalClusters({"example_com"}); + EXPECT_CALL(cluster_manager.thread_local_cluster_, httpAsyncClient()) + .WillRepeatedly(ReturnRef(cluster_manager.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager.thread_local_cluster_.async_client_, send_(_, _, _)) .WillRepeatedly(Invoke([&, retry = num_retries]( Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) mutable diff --git a/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc b/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc index 9594323fe34e..5798af81725a 100644 --- a/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc +++ b/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc @@ -40,8 +40,7 @@ class ExtAuthzHttpClientTest : public testing::Test { void initialize(const std::string& yaml) { config_ = createConfig(yaml); client_ = std::make_unique(cm_, config_); - ON_CALL(cm_, httpAsyncClientForCluster(config_->cluster())) - .WillByDefault(ReturnRef(async_client_)); + ON_CALL(cm_.thread_local_cluster_, httpAsyncClient()).WillByDefault(ReturnRef(async_client_)); } ClientConfigSharedPtr createConfig(const std::string& yaml = EMPTY_STRING, uint32_t timeout = 250, @@ -512,7 +511,7 @@ TEST_F(ExtAuthzHttpClientTest, NoCluster) { InSequence s; EXPECT_CALL(cm_, getThreadLocalCluster(Eq("ext_authz"))).WillOnce(Return(nullptr)); - EXPECT_CALL(cm_, httpAsyncClientForCluster("ext_authz")).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).Times(0); EXPECT_CALL(request_callbacks_, onComplete_(WhenDynamicCastTo(AuthzErrorResponse(CheckStatus::Error)))); client_->check(request_callbacks_, envoy::service::auth::v3::CheckRequest{}, parent_span_, diff --git a/test/extensions/filters/http/common/fuzz/uber_filter.cc b/test/extensions/filters/http/common/fuzz/uber_filter.cc index 0fcab5125e71..d43c2f403c77 100644 --- a/test/extensions/filters/http/common/fuzz/uber_filter.cc +++ b/test/extensions/filters/http/common/fuzz/uber_filter.cc @@ -13,7 +13,8 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { -UberFilterFuzzer::UberFilterFuzzer() : async_request_{&cluster_manager_.async_client_} { +UberFilterFuzzer::UberFilterFuzzer() + : async_request_{&cluster_manager_.thread_local_cluster_.async_client_} { // This is a decoder filter. ON_CALL(filter_callback_, addStreamDecoderFilter(_)) .WillByDefault(Invoke([&](Http::StreamDecoderFilterSharedPtr filter) -> void { diff --git a/test/extensions/filters/http/common/fuzz/uber_per_filter.cc b/test/extensions/filters/http/common/fuzz/uber_per_filter.cc index 515f4c632a26..9ca63f3e236b 100644 --- a/test/extensions/filters/http/common/fuzz/uber_per_filter.cc +++ b/test/extensions/filters/http/common/fuzz/uber_per_filter.cc @@ -127,7 +127,8 @@ void UberFilterFuzzer::perFilterSetup() { ON_CALL(connection_, remoteAddress()).WillByDefault(testing::ReturnRef(addr_)); ON_CALL(connection_, localAddress()).WillByDefault(testing::ReturnRef(addr_)); ON_CALL(factory_context_, clusterManager()).WillByDefault(testing::ReturnRef(cluster_manager_)); - ON_CALL(cluster_manager_.async_client_, send_(_, _, _)).WillByDefault(Return(&async_request_)); + ON_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) + .WillByDefault(Return(&async_request_)); ON_CALL(decoder_callbacks_, connection()).WillByDefault(testing::Return(&connection_)); ON_CALL(decoder_callbacks_, activeSpan()) diff --git a/test/extensions/filters/http/common/jwks_fetcher_test.cc b/test/extensions/filters/http/common/jwks_fetcher_test.cc index 90a9cc48f40d..cd3604aa40bf 100644 --- a/test/extensions/filters/http/common/jwks_fetcher_test.cc +++ b/test/extensions/filters/http/common/jwks_fetcher_test.cc @@ -132,7 +132,8 @@ TEST_F(JwksFetcherTest, TestHttpFailure) { TEST_F(JwksFetcherTest, TestCancel) { // Setup - Http::MockAsyncClientRequest request(&(mock_factory_ctx_.cluster_manager_.async_client_)); + Http::MockAsyncClientRequest request( + &(mock_factory_ctx_.cluster_manager_.thread_local_cluster_.async_client_)); MockUpstream mock_pubkey(mock_factory_ctx_.cluster_manager_, &request); MockJwksReceiver receiver; std::unique_ptr fetcher(JwksFetcher::create(mock_factory_ctx_.cluster_manager_)); @@ -156,7 +157,8 @@ TEST_F(JwksFetcherTest, TestSpanPassedDown) { std::unique_ptr fetcher(JwksFetcher::create(mock_factory_ctx_.cluster_manager_)); // Expectations for span - EXPECT_CALL(mock_factory_ctx_.cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(mock_factory_ctx_.cluster_manager_.thread_local_cluster_.async_client_, + send_(_, _, _)) .WillOnce(Invoke( [this](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks&, const Http::AsyncClient::RequestOptions& options) -> Http::AsyncClient::Request* { diff --git a/test/extensions/filters/http/common/mock.cc b/test/extensions/filters/http/common/mock.cc index 89de081bebd6..dc1a72a4dfd5 100644 --- a/test/extensions/filters/http/common/mock.cc +++ b/test/extensions/filters/http/common/mock.cc @@ -8,8 +8,9 @@ namespace HttpFilters { namespace Common { MockUpstream::MockUpstream(Upstream::MockClusterManager& mock_cm, const std::string& status, const std::string& response_body) - : request_(&mock_cm.async_client_), status_(status), response_body_(response_body) { - ON_CALL(mock_cm.async_client_, send_(testing::_, testing::_, testing::_)) + : request_(&mock_cm.thread_local_cluster_.async_client_), status_(status), + response_body_(response_body) { + ON_CALL(mock_cm.thread_local_cluster_.async_client_, send_(testing::_, testing::_, testing::_)) .WillByDefault(testing::Invoke( [this](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -28,8 +29,8 @@ MockUpstream::MockUpstream(Upstream::MockClusterManager& mock_cm, const std::str MockUpstream::MockUpstream(Upstream::MockClusterManager& mock_cm, Http::AsyncClient::FailureReason reason) - : request_(&mock_cm.async_client_) { - ON_CALL(mock_cm.async_client_, send_(testing::_, testing::_, testing::_)) + : request_(&mock_cm.thread_local_cluster_.async_client_) { + ON_CALL(mock_cm.thread_local_cluster_.async_client_, send_(testing::_, testing::_, testing::_)) .WillByDefault(testing::Invoke( [this, reason](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -40,8 +41,8 @@ MockUpstream::MockUpstream(Upstream::MockClusterManager& mock_cm, MockUpstream::MockUpstream(Upstream::MockClusterManager& mock_cm, Http::MockAsyncClientRequest* request) - : request_(&mock_cm.async_client_) { - ON_CALL(mock_cm.async_client_, send_(testing::_, testing::_, testing::_)) + : request_(&mock_cm.thread_local_cluster_.async_client_) { + ON_CALL(mock_cm.thread_local_cluster_.async_client_, send_(testing::_, testing::_, testing::_)) .WillByDefault(testing::Return(request)); } } // namespace Common diff --git a/test/extensions/filters/http/jwt_authn/authenticator_test.cc b/test/extensions/filters/http/jwt_authn/authenticator_test.cc index 4cc683db8caa..3ec5713647f4 100644 --- a/test/extensions/filters/http/jwt_authn/authenticator_test.cc +++ b/test/extensions/filters/http/jwt_authn/authenticator_test.cc @@ -288,7 +288,7 @@ TEST_F(AuthenticatorTest, TestInvalidPrefix) { // This test verifies when a JWT is non-expiring without audience specified, JwtAudienceNotAllowed // is returned. TEST_F(AuthenticatorTest, TestNonExpiringJWT) { - EXPECT_CALL(mock_factory_ctx_.cluster_manager_, httpAsyncClientForCluster(_)).Times(0); + EXPECT_CALL(mock_factory_ctx_.cluster_manager_.thread_local_cluster_, httpAsyncClient()).Times(0); Http::TestRequestHeaderMapImpl headers{ {"Authorization", "Bearer " + std::string(NonExpiringToken)}}; diff --git a/test/extensions/filters/http/jwt_authn/mock.h b/test/extensions/filters/http/jwt_authn/mock.h index 990353cf2971..bf06cd0b2f19 100644 --- a/test/extensions/filters/http/jwt_authn/mock.h +++ b/test/extensions/filters/http/jwt_authn/mock.h @@ -64,8 +64,8 @@ class MockExtractor : public Extractor { class MockUpstream { public: MockUpstream(Upstream::MockClusterManager& mock_cm, const std::string& response_body) - : request_(&mock_cm.async_client_), response_body_(response_body) { - ON_CALL(mock_cm.async_client_, send_(_, _, _)) + : request_(&mock_cm.thread_local_cluster_.async_client_), response_body_(response_body) { + ON_CALL(mock_cm.thread_local_cluster_.async_client_, send_(_, _, _)) .WillByDefault( Invoke([this](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { diff --git a/test/extensions/filters/http/jwt_authn/provider_verifier_test.cc b/test/extensions/filters/http/jwt_authn/provider_verifier_test.cc index 8c7d2e1cf837..2598fd40a19a 100644 --- a/test/extensions/filters/http/jwt_authn/provider_verifier_test.cc +++ b/test/extensions/filters/http/jwt_authn/provider_verifier_test.cc @@ -90,7 +90,8 @@ TEST_F(ProviderVerifierTest, TestSpanPassedDown) { .setTimeout(std::chrono::milliseconds(5 * 1000)) .setParentSpan(parent_span_) .setChildSpanName("JWT Remote PubKey Fetch"); - EXPECT_CALL(mock_factory_ctx_.cluster_manager_.async_client_, send_(_, _, Eq(options))); + EXPECT_CALL(mock_factory_ctx_.cluster_manager_.thread_local_cluster_.async_client_, + send_(_, _, Eq(options))); auto headers = Http::TestRequestHeaderMapImpl{ {"Authorization", "Bearer " + std::string(GoodToken)}, diff --git a/test/extensions/filters/http/lua/lua_filter_test.cc b/test/extensions/filters/http/lua/lua_filter_test.cc index de03c5204d27..074f00afd84e 100644 --- a/test/extensions/filters/http/lua/lua_filter_test.cc +++ b/test/extensions/filters/http/lua/lua_filter_test.cc @@ -803,11 +803,11 @@ TEST_F(LuaHttpFilterTest, HttpCall) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -872,11 +872,11 @@ TEST_F(LuaHttpFilterTest, HttpCallAsyncFalse) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -932,11 +932,11 @@ TEST_F(LuaHttpFilterTest, HttpCallAsynchronous) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -1002,11 +1002,11 @@ TEST_F(LuaHttpFilterTest, DoubleHttpCall) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -1028,8 +1028,8 @@ TEST_F(LuaHttpFilterTest, DoubleHttpCall) { EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq(":status 200"))); EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("response"))); EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster2"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster2")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -1082,11 +1082,11 @@ TEST_F(LuaHttpFilterTest, HttpCallNoBody) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -1140,11 +1140,11 @@ TEST_F(LuaHttpFilterTest, HttpCallImmediateResponse) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -1190,11 +1190,11 @@ TEST_F(LuaHttpFilterTest, HttpCallErrorAfterResumeSuccess) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -1240,11 +1240,11 @@ TEST_F(LuaHttpFilterTest, HttpCallFailure) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -1282,11 +1282,11 @@ TEST_F(LuaHttpFilterTest, HttpCallReset) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks; EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -1326,10 +1326,10 @@ TEST_F(LuaHttpFilterTest, HttpCallImmediateFailure) { setup(SCRIPT); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); EXPECT_CALL(cluster_manager_, getThreadLocalCluster(Eq("cluster"))); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index e52e2f83d13d..fae1d1676efb 100644 --- a/test/extensions/filters/http/oauth2/filter_test.cc +++ b/test/extensions/filters/http/oauth2/filter_test.cc @@ -73,7 +73,7 @@ class MockOAuth2Client : public OAuth2Client { class OAuth2Test : public testing::Test { public: - OAuth2Test() : request_(&cm_.async_client_) { + OAuth2Test() : request_(&cm_.thread_local_cluster_.async_client_) { factory_context_.cluster_manager_.initializeClusters({"auth.example.com"}, {}); init(); } diff --git a/test/extensions/filters/http/oauth2/oauth_test.cc b/test/extensions/filters/http/oauth2/oauth_test.cc index 5e5c4b78e6c3..c1de35521ab8 100644 --- a/test/extensions/filters/http/oauth2/oauth_test.cc +++ b/test/extensions/filters/http/oauth2/oauth_test.cc @@ -24,6 +24,7 @@ namespace Oauth2 { using testing::_; using testing::Invoke; using testing::NiceMock; +using testing::Return; class MockCallbacks : public FilterCallbacks { public: @@ -34,11 +35,13 @@ class MockCallbacks : public FilterCallbacks { class OAuth2ClientTest : public testing::Test { public: OAuth2ClientTest() - : mock_callbacks_(std::make_shared()), request_(&cm_.async_client_) { + : mock_callbacks_(std::make_shared()), + request_(&cm_.thread_local_cluster_.async_client_) { envoy::config::core::v3::HttpUri uri; uri.set_cluster("auth"); uri.set_uri("auth.com/oauth/token"); uri.mutable_timeout()->set_seconds(1); + cm_.initializeThreadLocalClusters({"auth"}); client_ = std::make_shared(cm_, uri); } @@ -75,7 +78,7 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenSuccess) { new Http::ResponseMessageImpl(std::move(mock_response_headers))); mock_response->body().add(json); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillRepeatedly( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -87,7 +90,7 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenSuccess) { client_->asyncGetAccessToken("a", "b", "c", "d"); EXPECT_EQ(1, callbacks_.size()); EXPECT_CALL(*mock_callbacks_, onGetAccessTokenSuccess(_, _)); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); ASSERT_TRUE(popPendingCallback( [&](auto* callback) { callback->onSuccess(request, std::move(mock_response)); })); } @@ -106,7 +109,7 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenIncompleteResponse) { new Http::ResponseMessageImpl(std::move(mock_response_headers))); mock_response->body().add(json); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillRepeatedly( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -118,7 +121,7 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenIncompleteResponse) { client_->asyncGetAccessToken("a", "b", "c", "d"); EXPECT_EQ(1, callbacks_.size()); EXPECT_CALL(*mock_callbacks_, sendUnauthorizedResponse()); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); ASSERT_TRUE(popPendingCallback( [&](auto* callback) { callback->onSuccess(request, std::move(mock_response)); })); } @@ -131,7 +134,7 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenErrorResponse) { Http::ResponseMessagePtr mock_response( new Http::ResponseMessageImpl(std::move(mock_response_headers))); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillRepeatedly( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -143,7 +146,7 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenErrorResponse) { client_->asyncGetAccessToken("a", "b", "c", "d"); EXPECT_EQ(1, callbacks_.size()); EXPECT_CALL(*mock_callbacks_, sendUnauthorizedResponse()); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); ASSERT_TRUE(popPendingCallback( [&](auto* callback) { callback->onSuccess(request, std::move(mock_response)); })); } @@ -162,7 +165,7 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenInvalidResponse) { new Http::ResponseMessageImpl(std::move(mock_response_headers))); mock_response->body().add(json); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillRepeatedly( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -174,13 +177,13 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenInvalidResponse) { client_->asyncGetAccessToken("a", "b", "c", "d"); EXPECT_EQ(1, callbacks_.size()); EXPECT_CALL(*mock_callbacks_, sendUnauthorizedResponse()); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); ASSERT_TRUE(popPendingCallback( [&](auto* callback) { callback->onSuccess(request, std::move(mock_response)); })); } TEST_F(OAuth2ClientTest, NetworkError) { - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillRepeatedly( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -193,12 +196,20 @@ TEST_F(OAuth2ClientTest, NetworkError) { EXPECT_EQ(1, callbacks_.size()); EXPECT_CALL(*mock_callbacks_, sendUnauthorizedResponse()); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); ASSERT_TRUE(popPendingCallback([&](auto* callback) { callback->onFailure(request, Http::AsyncClient::FailureReason::Reset); })); } +TEST_F(OAuth2ClientTest, NoCluster) { + ON_CALL(cm_, getThreadLocalCluster("auth")).WillByDefault(Return(nullptr)); + client_->setCallbacks(*mock_callbacks_); + EXPECT_CALL(*mock_callbacks_, sendUnauthorizedResponse()); + client_->asyncGetAccessToken("a", "b", "c", "d"); + EXPECT_EQ(0, callbacks_.size()); +} + } // namespace Oauth2 } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/http/squash/squash_filter_test.cc b/test/extensions/filters/http/squash/squash_filter_test.cc index 3291665812c1..6bfade15ad44 100644 --- a/test/extensions/filters/http/squash/squash_filter_test.cc +++ b/test/extensions/filters/http/squash/squash_filter_test.cc @@ -116,6 +116,7 @@ TEST(SquashFilterConfigTest, ParsesAndEscapesEnvironment) { const auto config = constructSquashFilterConfigFromYaml(yaml, factory_context); EXPECT_JSON_EQ(expected_json, config.attachmentJson()); } + TEST(SquashFilterConfigTest, TwoEnvironmentVariables) { TestEnvironment::setEnvVar("ENV1", "1", 1); TestEnvironment::setEnvVar("ENV2", "2", 1); @@ -155,7 +156,8 @@ TEST(SquashFilterConfigTest, ParsesEnvironmentInComplexTemplate) { class SquashFilterTest : public testing::Test { public: - SquashFilterTest() : request_(&cm_.async_client_) {} + SquashFilterTest() + : request_(&factory_context_.cluster_manager_.thread_local_cluster_.async_client_) {} protected: void SetUp() override {} @@ -164,9 +166,10 @@ class SquashFilterTest : public testing::Test { envoy::extensions::filters::http::squash::v3::Squash p; p.set_cluster("squash"); factory_context_.cluster_manager_.initializeClusters({"squash"}, {}); + factory_context_.cluster_manager_.initializeThreadLocalClusters({"squash"}); config_ = std::make_shared(p, factory_context_.cluster_manager_); - filter_ = std::make_shared(config_, cm_); + filter_ = std::make_shared(config_, factory_context_.cluster_manager_); filter_->setDecoderFilterCallbacks(filter_callbacks_); } @@ -180,8 +183,9 @@ class SquashFilterTest : public testing::Test { attachmentTimeout_timer_ = new NiceMock(&filter_callbacks_.dispatcher_); - EXPECT_CALL(cm_, httpAsyncClientForCluster("squash")) - .WillRepeatedly(ReturnRef(cm_.async_client_)); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillRepeatedly( + ReturnRef(factory_context_.cluster_manager_.thread_local_cluster_.async_client_)); expectAsyncClientSend(); @@ -209,7 +213,8 @@ class SquashFilterTest : public testing::Test { } void expectAsyncClientSend() { - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_.async_client_, + send_(_, _, _)) .WillOnce(Invoke( [&](Envoy::Http::RequestMessagePtr&, Envoy::Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Envoy::Http::AsyncClient::Request* { @@ -248,7 +253,6 @@ class SquashFilterTest : public testing::Test { NiceMock filter_callbacks_; NiceMock factory_context_; NiceMock* attachmentTimeout_timer_{}; - NiceMock cm_; Envoy::Http::MockAsyncClientRequest request_; SquashFilterConfigSharedPtr config_; std::shared_ptr filter_; @@ -258,9 +262,10 @@ class SquashFilterTest : public testing::Test { TEST_F(SquashFilterTest, DecodeHeaderContinuesOnClientFail) { initFilter(); - EXPECT_CALL(cm_, httpAsyncClientForCluster("squash")).WillOnce(ReturnRef(cm_.async_client_)); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(factory_context_.cluster_manager_.thread_local_cluster_.async_client_)); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(Invoke( [&](Envoy::Http::RequestMessagePtr&, Envoy::Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Envoy::Http::AsyncClient::Request* { @@ -297,7 +302,7 @@ TEST_F(SquashFilterTest, DecodeContinuesOnCreateAttachmentFail) { TEST_F(SquashFilterTest, DoesNothingWithNoHeader) { initFilter(); - EXPECT_CALL(cm_, httpAsyncClientForCluster(_)).Times(0); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, httpAsyncClient()).Times(0); Http::TestRequestHeaderMapImpl headers{{":method", "GET"}, {":authority", "www.solo.io"}, @@ -446,7 +451,7 @@ TEST_F(SquashFilterTest, TimerExpiresInline) { attachmentTimeout_timer_->invokeCallback(); })); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(Invoke([&](Envoy::Http::RequestMessagePtr&, Envoy::Http::AsyncClient::Callbacks&, const Http::AsyncClient::RequestOptions&) -> Envoy::Http::AsyncClient::Request* { return &request_; })); diff --git a/test/extensions/filters/http/wasm/config_test.cc b/test/extensions/filters/http/wasm/config_test.cc index 5d5d92afb6b5..e0d1bae01984 100644 --- a/test/extensions/filters/http/wasm/config_test.cc +++ b/test/extensions/filters/http/wasm/config_test.cc @@ -165,7 +165,7 @@ TEST_P(WasmFilterConfigTest, YamlLoadInlineWasm) { vm_config: runtime: "envoy.wasm.runtime.)EOF", GetParam(), R"EOF(" - code: + code: local: { inline_bytes: ")EOF", Base64::encode(code.data(), code.size()), R"EOF(" } )EOF"); @@ -224,9 +224,10 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteWasm) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillOnce(ReturnRef(cluster_manager_.async_client_)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -273,9 +274,10 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteWasmFailOnUncachedThenSucceed) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillOnce(ReturnRef(cluster_manager_.async_client_)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -340,11 +342,12 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteWasmFailCachedThenSucceed) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillRepeatedly(ReturnRef(cluster_manager_.async_client_)); + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillRepeatedly(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); Http::AsyncClient::Callbacks* async_callbacks = nullptr; - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillRepeatedly( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -390,7 +393,7 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteWasmFailCachedThenSucceed) { // Wait for negative cache to timeout. ::Envoy::Extensions::Common::Wasm::setTimeOffsetForCodeCacheForTesting(std::chrono::seconds(10)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillRepeatedly( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -530,9 +533,10 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteConnectionReset) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillOnce(ReturnRef(cluster_manager_.async_client_)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -571,9 +575,10 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteSuccessWith503) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillOnce(ReturnRef(cluster_manager_.async_client_)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -614,9 +619,10 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteSuccessIncorrectSha256) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillOnce(ReturnRef(cluster_manager_.async_client_)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -660,9 +666,10 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteMultipleRetries) { NiceMock client; NiceMock request(&client); int num_retries = 3; - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillRepeatedly(ReturnRef(cluster_manager_.async_client_)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillRepeatedly(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .Times(num_retries) .WillRepeatedly( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, @@ -678,7 +685,7 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteMultipleRetries) { EXPECT_CALL(*retry_timer_, enableTimer(_, _)) .WillRepeatedly(Invoke([&](const std::chrono::milliseconds&, const ScopeTrackedObject*) { if (--num_retries == 0) { - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(Invoke( [&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -728,9 +735,10 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteSuccessBadcode) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillOnce(ReturnRef(cluster_manager_.async_client_)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -793,9 +801,10 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteSuccessBadcodeFailOpen) { NiceMock client; NiceMock request(&client); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster_1")) - .WillOnce(ReturnRef(cluster_manager_.async_client_)); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + cluster_manager_.initializeThreadLocalClusters({"cluster_1"}); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cluster_manager_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { diff --git a/test/extensions/filters/http/wasm/wasm_filter_test.cc b/test/extensions/filters/http/wasm/wasm_filter_test.cc index 8e71c7f6f847..fe6932f1c75c 100644 --- a/test/extensions/filters/http/wasm/wasm_filter_test.cc +++ b/test/extensions/filters/http/wasm/wasm_filter_test.cc @@ -564,11 +564,11 @@ TEST_P(WasmHttpFilterTest, AsyncCall) { setupFilter(); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks = nullptr; cluster_manager_.initializeThreadLocalClusters({"cluster"}); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -608,11 +608,11 @@ TEST_P(WasmHttpFilterTest, StopAndResumeViaAsyncCall) { InSequence s; Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks = nullptr; cluster_manager_.initializeThreadLocalClusters({"cluster"}); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -659,11 +659,11 @@ TEST_P(WasmHttpFilterTest, AsyncCallBadCall) { setupFilter(); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); cluster_manager_.initializeThreadLocalClusters({"cluster"}); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); // Just fail the send. - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks&, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -678,11 +678,11 @@ TEST_P(WasmHttpFilterTest, AsyncCallFailure) { setupFilter(); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks = nullptr; cluster_manager_.initializeThreadLocalClusters({"cluster"}); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -718,11 +718,11 @@ TEST_P(WasmHttpFilterTest, AsyncCallAfterDestroyed) { setupFilter(); Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}}; - Http::MockAsyncClientRequest request(&cluster_manager_.async_client_); + Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callbacks = nullptr; cluster_manager_.initializeThreadLocalClusters({"cluster"}); - EXPECT_CALL(cluster_manager_, httpAsyncClientForCluster("cluster")); - EXPECT_CALL(cluster_manager_.async_client_, send_(_, _, _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, httpAsyncClient()); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& cb, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { diff --git a/test/extensions/filters/network/client_ssl_auth/client_ssl_auth_test.cc b/test/extensions/filters/network/client_ssl_auth/client_ssl_auth_test.cc index b4f3b6a31387..e589381be4fa 100644 --- a/test/extensions/filters/network/client_ssl_auth/client_ssl_auth_test.cc +++ b/test/extensions/filters/network/client_ssl_auth/client_ssl_auth_test.cc @@ -57,7 +57,8 @@ test: a class ClientSslAuthFilterTest : public testing::Test { protected: ClientSslAuthFilterTest() - : request_(&cm_.async_client_), interval_timer_(new Event::MockTimer(&dispatcher_)), + : request_(&cm_.thread_local_cluster_.async_client_), + interval_timer_(new Event::MockTimer(&dispatcher_)), api_(Api::createApiForTest(stats_store_)), ssl_(std::make_shared()) {} ~ClientSslAuthFilterTest() override { tls_.shutdownThread(); } @@ -76,7 +77,7 @@ stat_prefix: vpn envoy::extensions::filters::network::client_ssl_auth::v3::ClientSSLAuth proto_config{}; TestUtility::loadFromYaml(yaml, proto_config); cm_.initializeClusters({"vpn"}, {}); - EXPECT_CALL(cm_, clusters()); + cm_.initializeThreadLocalClusters({"vpn"}); setupRequest(); config_ = ClientSslAuthConfig::create(proto_config, tls_, cm_, dispatcher_, stats_store_, random_); @@ -95,8 +96,9 @@ stat_prefix: vpn } void setupRequest() { - EXPECT_CALL(cm_, httpAsyncClientForCluster("vpn")).WillOnce(ReturnRef(cm_.async_client_)); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([this](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { @@ -106,7 +108,7 @@ stat_prefix: vpn } NiceMock tls_; - Upstream::MockClusterManager cm_; + NiceMock cm_; Event::MockDispatcher dispatcher_; Http::MockAsyncClientRequest request_; ClientSslAuthConfigSharedPtr config_; @@ -250,8 +252,9 @@ TEST_F(ClientSslAuthFilterTest, Ssl) { callbacks_->onFailure(request_, Http::AsyncClient::FailureReason::Reset); // Interval timer fires, cannot obtain async client. - EXPECT_CALL(cm_, httpAsyncClientForCluster("vpn")).WillOnce(ReturnRef(cm_.async_client_)); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce( Invoke([&](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { diff --git a/test/extensions/filters/network/client_ssl_auth/config_test.cc b/test/extensions/filters/network/client_ssl_auth/config_test.cc index 146f136341b5..b7c3d9fa5cc7 100644 --- a/test/extensions/filters/network/client_ssl_auth/config_test.cc +++ b/test/extensions/filters/network/client_ssl_auth/config_test.cc @@ -45,6 +45,7 @@ auth_api_cluster: fake_cluster TestUtility::loadFromYamlAndValidate(yaml, proto_config); NiceMock context; context.cluster_manager_.initializeClusters({"fake_cluster"}, {}); + context.cluster_manager_.initializeThreadLocalClusters({"fake_cluster"}); ClientSslAuthConfigFactory factory; Network::FilterFactoryCb cb = factory.createFilterFactoryFromProto(proto_config, context); Network::MockConnection connection; @@ -63,6 +64,7 @@ auth_api_cluster: fake_cluster TestUtility::loadFromYamlAndValidate(yaml, proto_config); NiceMock context; context.cluster_manager_.initializeClusters({"fake_cluster"}, {}); + context.cluster_manager_.initializeThreadLocalClusters({"fake_cluster"}); ClientSslAuthConfigFactory factory; Network::FilterFactoryCb cb = factory.createFilterFactoryFromProto(proto_config, context); Network::MockConnection connection; @@ -79,6 +81,7 @@ auth_api_cluster: fake_cluster NiceMock context; context.cluster_manager_.initializeClusters({"fake_cluster"}, {}); + context.cluster_manager_.initializeThreadLocalClusters({"fake_cluster"}); ClientSslAuthConfigFactory factory; envoy::extensions::filters::network::client_ssl_auth::v3::ClientSSLAuth proto_config = *dynamic_cast( diff --git a/test/extensions/filters/network/dubbo_proxy/router_test.cc b/test/extensions/filters/network/dubbo_proxy/router_test.cc index 29edf4df53c4..7b580f9f609b 100644 --- a/test/extensions/filters/network/dubbo_proxy/router_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/router_test.cc @@ -130,18 +130,20 @@ class DubboRouterTestBase { } void connectUpstream() { - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, addUpstreamCallbacks(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + addUpstreamCallbacks(_)) .WillOnce(Invoke([&](Tcp::ConnectionPool::UpstreamCallbacks& cb) -> void { upstream_callbacks_ = &cb; })); conn_state_.reset(); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, connectionState()) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + connectionState()) .WillRepeatedly( Invoke([&]() -> Tcp::ConnectionPool::ConnectionState* { return conn_state_.get(); })); EXPECT_CALL(callbacks_, continueDecoding()); - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady(upstream_connection_); EXPECT_NE(nullptr, upstream_callbacks_); } @@ -153,7 +155,8 @@ class DubboRouterTestBase { initializeMetadata(msg_type); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, addUpstreamCallbacks(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + addUpstreamCallbacks(_)) .WillOnce(Invoke([&](Tcp::ConnectionPool::UpstreamCallbacks& cb) -> void { upstream_callbacks_ = &cb; })); @@ -170,11 +173,12 @@ class DubboRouterTestBase { EXPECT_CALL(callbacks_, protocolType()).WillOnce(Return(ProtocolType::Dubbo)); EXPECT_CALL(callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, newConnection(_)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, newConnection(_)) .WillOnce( Invoke([&](Tcp::ConnectionPool::Callbacks& cb) -> Tcp::ConnectionPool::Cancellable* { - context_.cluster_manager_.tcp_conn_pool_.newConnectionImpl(cb); - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.newConnectionImpl(cb); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady( + upstream_connection_); return nullptr; })); } @@ -194,7 +198,8 @@ class DubboRouterTestBase { EXPECT_CALL(callbacks_, upstreamData(Ref(buffer))) .WillOnce(Return(DubboFilters::UpstreamResponseStatus::Complete)); - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, released(Ref(upstream_connection_))); + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, + released(Ref(upstream_connection_))); upstream_callbacks_->onUpstreamData(buffer, false); } @@ -248,7 +253,7 @@ TEST_F(DubboRouterTest, PoolRemoteConnectionFailure) { })); startRequest(MessageType::Request); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( ConnectionPool::PoolFailureReason::RemoteConnectionFailure); } @@ -264,7 +269,8 @@ TEST_F(DubboRouterTest, PoolTimeout) { })); startRequest(MessageType::Request); - context_.cluster_manager_.tcp_conn_pool_.poolFailure(ConnectionPool::PoolFailureReason::Timeout); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( + ConnectionPool::PoolFailureReason::Timeout); } TEST_F(DubboRouterTest, PoolOverflowFailure) { @@ -279,7 +285,8 @@ TEST_F(DubboRouterTest, PoolOverflowFailure) { })); startRequest(MessageType::Request); - context_.cluster_manager_.tcp_conn_pool_.poolFailure(ConnectionPool::PoolFailureReason::Overflow); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( + ConnectionPool::PoolFailureReason::Overflow); } TEST_F(DubboRouterTest, ClusterMaintenanceMode) { @@ -309,7 +316,7 @@ TEST_F(DubboRouterTest, NoHealthyHosts) { EXPECT_CALL(callbacks_, route()).WillOnce(Return(route_ptr_)); EXPECT_CALL(*route_, routeEntry()).WillOnce(Return(&route_entry_)); EXPECT_CALL(route_entry_, clusterName()).WillRepeatedly(ReturnRef(cluster_name_)); - EXPECT_CALL(context_.cluster_manager_, tcpConnPoolForCluster(cluster_name_, _, _)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(nullptr)); EXPECT_CALL(callbacks_, sendLocalReply(_, _)) @@ -334,7 +341,7 @@ TEST_F(DubboRouterTest, PoolConnectionFailureWithOnewayMessage) { EXPECT_CALL(callbacks_, resetStream()); EXPECT_EQ(FilterStatus::StopIteration, router_->onMessageDecoded(metadata_, message_context_)); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( ConnectionPool::PoolFailureReason::RemoteConnectionFailure); destroyRouter(); @@ -419,7 +426,8 @@ TEST_F(DubboRouterTest, OneWay) { initializeRouter(); initializeMetadata(MessageType::Oneway); - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, released(Ref(upstream_connection_))); + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, + released(Ref(upstream_connection_))); startRequest(MessageType::Oneway); connectUpstream(); @@ -489,7 +497,7 @@ TEST_F(DubboRouterTest, DestroyWhileConnecting) { initializeMetadata(MessageType::Request); NiceMock conn_pool_handle; - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, newConnection(_)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, newConnection(_)) .WillOnce(Invoke([&](Tcp::ConnectionPool::Callbacks&) -> Tcp::ConnectionPool::Cancellable* { return &conn_pool_handle; })); diff --git a/test/extensions/filters/network/rocketmq_proxy/router_test.cc b/test/extensions/filters/network/rocketmq_proxy/router_test.cc index 59c5f8b8bf4c..a5665d5a8580 100644 --- a/test/extensions/filters/network/rocketmq_proxy/router_test.cc +++ b/test/extensions/filters/network/rocketmq_proxy/router_test.cc @@ -100,15 +100,16 @@ class RocketmqRouterTestBase { void startRequest() { router_->sendRequestToUpstream(*active_message_); } void connectUpstream() { - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady(upstream_connection_); } void startRequestWithExistingConnection() { - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, newConnection(_)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, newConnection(_)) .WillOnce( Invoke([&](Tcp::ConnectionPool::Callbacks& cb) -> Tcp::ConnectionPool::Cancellable* { - context_.cluster_manager_.tcp_conn_pool_.newConnectionImpl(cb); - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.newConnectionImpl(cb); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady( + upstream_connection_); return nullptr; })); router_->sendRequestToUpstream(*active_message_); @@ -167,7 +168,7 @@ TEST_F(RocketmqRouterTest, PoolRemoteConnectionFailure) { })); startRequest(); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( Tcp::ConnectionPool::PoolFailureReason::RemoteConnectionFailure); } @@ -183,7 +184,7 @@ TEST_F(RocketmqRouterTest, PoolTimeout) { EXPECT_CALL(*active_message_, onReset()); startRequest(); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( Tcp::ConnectionPool::PoolFailureReason::Timeout); } @@ -199,7 +200,7 @@ TEST_F(RocketmqRouterTest, PoolLocalConnectionFailure) { EXPECT_CALL(*active_message_, onReset()); startRequest(); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( Tcp::ConnectionPool::PoolFailureReason::LocalConnectionFailure); } @@ -215,7 +216,7 @@ TEST_F(RocketmqRouterTest, PoolOverflowFailure) { EXPECT_CALL(*active_message_, onReset()); startRequest(); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( Tcp::ConnectionPool::PoolFailureReason::Overflow); } @@ -244,7 +245,7 @@ TEST_F(RocketmqRouterTest, NoHealthyHosts) { .WillOnce(Invoke([&](absl::string_view error_message) -> void { EXPECT_THAT(error_message, ContainsRegex(".*No host available*.")); })); - EXPECT_CALL(context_.cluster_manager_, tcpConnPoolForCluster("fake_cluster", _, _)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(nullptr)); EXPECT_CALL(*active_message_, onReset()); diff --git a/test/extensions/filters/network/thrift_proxy/router_test.cc b/test/extensions/filters/network/thrift_proxy/router_test.cc index 91ed2f5e4774..6c61b8161fff 100644 --- a/test/extensions/filters/network/thrift_proxy/router_test.cc +++ b/test/extensions/filters/network/thrift_proxy/router_test.cc @@ -141,16 +141,19 @@ class ThriftRouterTestBase { } void connectUpstream() { - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, addUpstreamCallbacks(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + addUpstreamCallbacks(_)) .WillOnce(Invoke([&](Tcp::ConnectionPool::UpstreamCallbacks& cb) -> void { upstream_callbacks_ = &cb; })); conn_state_.reset(); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, connectionState()) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + connectionState()) .WillRepeatedly( Invoke([&]() -> Tcp::ConnectionPool::ConnectionState* { return conn_state_.get(); })); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, setConnectionState_(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + setConnectionState_(_)) .WillOnce(Invoke( [&](Tcp::ConnectionPool::ConnectionStatePtr& cs) -> void { conn_state_.swap(cs); })); @@ -162,7 +165,7 @@ class ThriftRouterTestBase { })); EXPECT_CALL(callbacks_, continueDecoding()); - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady(upstream_connection_); EXPECT_NE(nullptr, upstream_callbacks_); } @@ -176,7 +179,8 @@ class ThriftRouterTestBase { initializeMetadata(msg_type); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, addUpstreamCallbacks(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + addUpstreamCallbacks(_)) .WillOnce(Invoke([&](Tcp::ConnectionPool::UpstreamCallbacks& cb) -> void { upstream_callbacks_ = &cb; })); @@ -184,7 +188,8 @@ class ThriftRouterTestBase { if (!conn_state_) { conn_state_ = std::make_unique(); } - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, connectionState()) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + connectionState()) .WillRepeatedly( Invoke([&]() -> Tcp::ConnectionPool::ConnectionState* { return conn_state_.get(); })); @@ -213,11 +218,12 @@ class ThriftRouterTestBase { })); }; EXPECT_CALL(callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, newConnection(_)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, newConnection(_)) .WillOnce( Invoke([&](Tcp::ConnectionPool::Callbacks& cb) -> Tcp::ConnectionPool::Cancellable* { - context_.cluster_manager_.tcp_conn_pool_.newConnectionImpl(cb); - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.newConnectionImpl(cb); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady( + upstream_connection_); return nullptr; })); @@ -291,7 +297,8 @@ class ThriftRouterTestBase { EXPECT_CALL(upstream_connection_, write(_, false)); if (msg_type_ == MessageType::Oneway) { - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, released(Ref(upstream_connection_))); + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, + released(Ref(upstream_connection_))); } EXPECT_EQ(FilterStatus::Continue, router_->messageEnd()); @@ -309,7 +316,8 @@ class ThriftRouterTestBase { EXPECT_CALL(callbacks_, upstreamData(Ref(buffer))) .WillOnce(Return(ThriftFilters::ResponseStatus::Complete)); - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, released(Ref(upstream_connection_))); + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, + released(Ref(upstream_connection_))); upstream_callbacks_->onUpstreamData(buffer, false); } @@ -414,7 +422,7 @@ TEST_F(ThriftRouterTest, PoolRemoteConnectionFailure) { EXPECT_THAT(app_ex.what(), ContainsRegex(".*connection failure.*")); EXPECT_TRUE(end_stream); })); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( ConnectionPool::PoolFailureReason::RemoteConnectionFailure); } @@ -423,7 +431,7 @@ TEST_F(ThriftRouterTest, PoolLocalConnectionFailure) { startRequest(MessageType::Call); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( ConnectionPool::PoolFailureReason::LocalConnectionFailure); } @@ -439,7 +447,8 @@ TEST_F(ThriftRouterTest, PoolTimeout) { EXPECT_THAT(app_ex.what(), ContainsRegex(".*connection failure.*")); EXPECT_TRUE(end_stream); })); - context_.cluster_manager_.tcp_conn_pool_.poolFailure(ConnectionPool::PoolFailureReason::Timeout); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( + ConnectionPool::PoolFailureReason::Timeout); } TEST_F(ThriftRouterTest, PoolOverflowFailure) { @@ -454,8 +463,8 @@ TEST_F(ThriftRouterTest, PoolOverflowFailure) { EXPECT_THAT(app_ex.what(), ContainsRegex(".*too many connections.*")); EXPECT_TRUE(end_stream); })); - context_.cluster_manager_.tcp_conn_pool_.poolFailure(ConnectionPool::PoolFailureReason::Overflow, - true); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( + ConnectionPool::PoolFailureReason::Overflow, true); } TEST_F(ThriftRouterTest, PoolConnectionFailureWithOnewayMessage) { @@ -464,7 +473,7 @@ TEST_F(ThriftRouterTest, PoolConnectionFailureWithOnewayMessage) { EXPECT_CALL(callbacks_, sendLocalReply(_, _)).Times(0); EXPECT_CALL(callbacks_, resetDownstreamConnection()); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( ConnectionPool::PoolFailureReason::RemoteConnectionFailure); destroyRouter(); @@ -534,7 +543,7 @@ TEST_F(ThriftRouterTest, NoHealthyHosts) { EXPECT_CALL(callbacks_, route()).WillOnce(Return(route_ptr_)); EXPECT_CALL(*route_, routeEntry()).WillOnce(Return(&route_entry_)); EXPECT_CALL(route_entry_, clusterName()).WillRepeatedly(ReturnRef(cluster_name_)); - EXPECT_CALL(context_.cluster_manager_, tcpConnPoolForCluster(cluster_name_, _, _)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Return(nullptr)); EXPECT_CALL(callbacks_, sendLocalReply(_, _)) @@ -561,7 +570,8 @@ TEST_F(ThriftRouterTest, TruncatedResponse) { EXPECT_CALL(callbacks_, startUpstreamResponse(_, _)); EXPECT_CALL(callbacks_, upstreamData(Ref(buffer))) .WillOnce(Return(ThriftFilters::ResponseStatus::MoreData)); - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, released(Ref(upstream_connection_))); + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, + released(Ref(upstream_connection_))); EXPECT_CALL(callbacks_, resetDownstreamConnection()); upstream_callbacks_->onUpstreamData(buffer, true); @@ -679,8 +689,8 @@ TEST_F(ThriftRouterTest, UnexpectedRouterDestroyBeforeUpstreamConnect) { initializeRouter(); startRequest(MessageType::Call); - EXPECT_EQ(1, context_.cluster_manager_.tcp_conn_pool_.handles_.size()); - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_.handles_.front(), + EXPECT_EQ(1, context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.handles_.size()); + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.handles_.front(), cancel(Tcp::ConnectionPool::CancelPolicy::Default)); destroyRouter(); } @@ -697,15 +707,18 @@ TEST_F(ThriftRouterTest, ProtocolUpgrade) { initializeRouter(); startRequest(MessageType::Call); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, addUpstreamCallbacks(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + addUpstreamCallbacks(_)) .WillOnce(Invoke( [&](Tcp::ConnectionPool::UpstreamCallbacks& cb) -> void { upstream_callbacks_ = &cb; })); conn_state_.reset(); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, connectionState()) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + connectionState()) .WillRepeatedly( Invoke([&]() -> Tcp::ConnectionPool::ConnectionState* { return conn_state_.get(); })); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, setConnectionState_(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + setConnectionState_(_)) .WillOnce(Invoke( [&](Tcp::ConnectionPool::ConnectionStatePtr& cs) -> void { conn_state_.swap(cs); })); @@ -724,7 +737,7 @@ TEST_F(ThriftRouterTest, ProtocolUpgrade) { EXPECT_EQ("upgrade request", buffer.toString()); })); - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady(upstream_connection_); EXPECT_NE(nullptr, upstream_callbacks_); Buffer::OwnedImpl buffer; @@ -754,15 +767,18 @@ TEST_F(ThriftRouterTest, ProtocolUpgrade) { TEST_F(ThriftRouterTest, ProtocolUpgradeOnExistingUnusedConnection) { initializeRouter(); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, addUpstreamCallbacks(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + addUpstreamCallbacks(_)) .WillOnce(Invoke( [&](Tcp::ConnectionPool::UpstreamCallbacks& cb) -> void { upstream_callbacks_ = &cb; })); conn_state_.reset(); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, connectionState()) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + connectionState()) .WillRepeatedly( Invoke([&]() -> Tcp::ConnectionPool::ConnectionState* { return conn_state_.get(); })); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, setConnectionState_(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + setConnectionState_(_)) .WillOnce(Invoke( [&](Tcp::ConnectionPool::ConnectionStatePtr& cs) -> void { conn_state_.swap(cs); })); @@ -774,10 +790,10 @@ TEST_F(ThriftRouterTest, ProtocolUpgradeOnExistingUnusedConnection) { })); // Simulate an existing connection that's never been used. - EXPECT_CALL(context_.cluster_manager_.tcp_conn_pool_, newConnection(_)) + EXPECT_CALL(context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_, newConnection(_)) .WillOnce( Invoke([&](Tcp::ConnectionPool::Callbacks& cb) -> Tcp::ConnectionPool::Cancellable* { - context_.cluster_manager_.tcp_conn_pool_.newConnectionImpl(cb); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.newConnectionImpl(cb); EXPECT_CALL(*protocol_, supportsUpgrade()).WillOnce(Return(true)); @@ -788,7 +804,8 @@ TEST_F(ThriftRouterTest, ProtocolUpgradeOnExistingUnusedConnection) { return ThriftObjectPtr{upgrade_response}; })); - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady( + upstream_connection_); return nullptr; })); @@ -822,12 +839,14 @@ TEST_F(ThriftRouterTest, ProtocolUpgradeSkippedOnExistingConnection) { initializeRouter(); startRequest(MessageType::Call); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, addUpstreamCallbacks(_)) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + addUpstreamCallbacks(_)) .WillOnce(Invoke( [&](Tcp::ConnectionPool::UpstreamCallbacks& cb) -> void { upstream_callbacks_ = &cb; })); conn_state_ = std::make_unique(); - EXPECT_CALL(*context_.cluster_manager_.tcp_conn_pool_.connection_data_, connectionState()) + EXPECT_CALL(*context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.connection_data_, + connectionState()) .WillRepeatedly( Invoke([&]() -> Tcp::ConnectionPool::ConnectionState* { return conn_state_.get(); })); @@ -846,7 +865,7 @@ TEST_F(ThriftRouterTest, ProtocolUpgradeSkippedOnExistingConnection) { })); EXPECT_CALL(callbacks_, continueDecoding()); - context_.cluster_manager_.tcp_conn_pool_.poolReady(upstream_connection_); + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolReady(upstream_connection_); EXPECT_NE(nullptr, upstream_callbacks_); // Then the actual request... @@ -1042,7 +1061,7 @@ TEST_P(ThriftRouterPassthroughTest, PassthroughEnable) { EXPECT_THAT(app_ex.what(), ContainsRegex(".*connection failure.*")); EXPECT_TRUE(end_stream); })); - context_.cluster_manager_.tcp_conn_pool_.poolFailure( + context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( ConnectionPool::PoolFailureReason::RemoteConnectionFailure); } diff --git a/test/extensions/stats_sinks/common/statsd/statsd_test.cc b/test/extensions/stats_sinks/common/statsd/statsd_test.cc index 37d3fc1924bc..df6d10b649bb 100644 --- a/test/extensions/stats_sinks/common/statsd/statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/statsd_test.cc @@ -37,6 +37,7 @@ class TcpStatsdSinkTest : public Event::TestUsingSimulatedTime, public testing:: public: TcpStatsdSinkTest() { cluster_manager_.initializeClusters({"fake_cluster"}, {}); + cluster_manager_.initializeThreadLocalClusters({"fake_cluster"}); sink_ = std::make_unique( local_info_, "fake_cluster", tls_, cluster_manager_, cluster_manager_.active_clusters_["fake_cluster"]->info_->stats_store_); @@ -49,8 +50,7 @@ class TcpStatsdSinkTest : public Event::TestUsingSimulatedTime, public testing:: conn_info.host_description_ = Upstream::makeTestHost( std::make_unique>(), "tcp://127.0.0.1:80", simTime()); - EXPECT_CALL(cluster_manager_, tcpConnForCluster_("fake_cluster", _)) - .WillOnce(Return(conn_info)); + EXPECT_CALL(cluster_manager_.thread_local_cluster_, tcpConn_(_)).WillOnce(Return(conn_info)); EXPECT_CALL(*connection_, setConnectionStats(_)); EXPECT_CALL(*connection_, connect()); } @@ -153,7 +153,7 @@ TEST_F(TcpStatsdSinkTest, NoHost) { snapshot_.counters_.push_back({1, counter}); Upstream::MockHost::MockCreateConnectionData conn_info; - EXPECT_CALL(cluster_manager_, tcpConnForCluster_("fake_cluster", _)) + EXPECT_CALL(cluster_manager_.thread_local_cluster_, tcpConn_(_)) .WillOnce(Return(conn_info)) .WillOnce(Return(conn_info)); sink_->flush(snapshot_); diff --git a/test/extensions/tracers/datadog/datadog_tracer_impl_test.cc b/test/extensions/tracers/datadog/datadog_tracer_impl_test.cc index f6e938f95841..2276930a0677 100644 --- a/test/extensions/tracers/datadog/datadog_tracer_impl_test.cc +++ b/test/extensions/tracers/datadog/datadog_tracer_impl_test.cc @@ -50,8 +50,6 @@ class DatadogDriverTest : public testing::Test { void setup(envoy::config::trace::v3::DatadogConfig& datadog_config, bool init_timer) { cm_.thread_local_cluster_.cluster_.info_->name_ = "fake_cluster"; cm_.initializeThreadLocalClusters({"fake_cluster"}); - ON_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillByDefault(ReturnRef(cm_.async_client_)); if (init_timer) { timer_ = new NiceMock(&tls_.dispatcher_); @@ -136,10 +134,10 @@ TEST_F(DatadogDriverTest, AllowCollectorClusterToBeAddedViaApi) { TEST_F(DatadogDriverTest, FlushSpansTimer) { setupValidDriver(); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback; const absl::optional timeout(std::chrono::seconds(1)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, @@ -178,10 +176,10 @@ TEST_F(DatadogDriverTest, FlushSpansTimer) { TEST_F(DatadogDriverTest, NoBody) { setupValidDriver(); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback; const absl::optional timeout(std::chrono::seconds(1)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, @@ -233,8 +231,8 @@ TEST_F(DatadogDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterRemoval("fake_cluster"); // Verify that no report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster(_)).Times(0); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)).Times(0); // Trigger flush of a span. driver_ @@ -259,8 +257,8 @@ TEST_F(DatadogDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterAddOrUpdate(unrelated_cluster); // Verify that no report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster(_)).Times(0); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)).Times(0); // Trigger flush of a span. driver_ @@ -283,11 +281,11 @@ TEST_F(DatadogDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterAddOrUpdate(cm_.thread_local_cluster_); // Verify that report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillOnce(ReturnRef(cm_.async_client_)); - Http::MockAsyncClientRequest request(&cm_.async_client_); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback{}; - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(DoAll(WithArg<1>(SaveArgAddress(&callback)), Return(&request))); // Trigger flush of a span. @@ -314,11 +312,11 @@ TEST_F(DatadogDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterRemoval("unrelated_cluster"); // Verify that report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillOnce(ReturnRef(cm_.async_client_)); - Http::MockAsyncClientRequest request(&cm_.async_client_); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback{}; - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(DoAll(WithArg<1>(SaveArgAddress(&callback)), Return(&request))); // Trigger flush of a span. @@ -346,13 +344,15 @@ TEST_F(DatadogDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { TEST_F(DatadogDriverTest, CancelInflightRequestsOnDestruction) { setupValidDriver(); - StrictMock request1(&cm_.async_client_), - request2(&cm_.async_client_), request3(&cm_.async_client_), request4(&cm_.async_client_); + StrictMock request1(&cm_.thread_local_cluster_.async_client_), + request2(&cm_.thread_local_cluster_.async_client_), + request3(&cm_.thread_local_cluster_.async_client_), + request4(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback{}; const absl::optional timeout(std::chrono::seconds(1)); // Expect 4 separate report requests to be made. - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce(DoAll(WithArg<1>(SaveArgAddress(&callback)), Return(&request1))) .WillOnce(Return(&request2)) diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index 455d034627ea..239e30a85624 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -75,8 +75,8 @@ class LightStepDriverTest : public testing::Test { cm_.thread_local_cluster_.cluster_.info_->name_ = "fake_cluster"; cm_.initializeThreadLocalClusters({"fake_cluster"}); - ON_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillByDefault(ReturnRef(cm_.async_client_)); + ON_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillByDefault(ReturnRef(cm_.thread_local_cluster_.async_client_)); if (init_timer) { timer_ = new NiceMock(&tls_.dispatcher_); @@ -196,8 +196,8 @@ TEST_F(LightStepDriverTest, DeferredTlsInitialization) { opts->access_token = "sample_token"; opts->component_name = "component"; - ON_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillByDefault(ReturnRef(cm_.async_client_)); + ON_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillByDefault(ReturnRef(cm_.thread_local_cluster_.async_client_)); auto propagation_mode = Common::Ot::OpenTracingDriver::PropagationMode::TracerNative; @@ -228,11 +228,11 @@ TEST_F(LightStepDriverTest, AllowCollectorClusterToBeAddedViaApi) { TEST_F(LightStepDriverTest, FlushSeveralSpans) { setupValidDriver(2); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback = nullptr; const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, @@ -297,8 +297,8 @@ TEST_F(LightStepDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterRemoval("fake_cluster"); // Verify that no report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster(_)).Times(0); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)).Times(0); // Trigger flush of a span. driver_ @@ -320,8 +320,8 @@ TEST_F(LightStepDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterAddOrUpdate(unrelated_cluster); // Verify that no report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster(_)).Times(0); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)).Times(0); // Trigger flush of a span. driver_ @@ -341,11 +341,11 @@ TEST_F(LightStepDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterAddOrUpdate(cm_.thread_local_cluster_); // Verify that report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillOnce(ReturnRef(cm_.async_client_)); - Http::MockAsyncClientRequest request(&cm_.async_client_); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback{}; - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(DoAll(WithArg<1>(SaveArgAddress(&callback)), Return(&request))); // Trigger flush of a span. @@ -369,11 +369,11 @@ TEST_F(LightStepDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterRemoval("unrelated_cluster"); // Verify that report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillOnce(ReturnRef(cm_.async_client_)); - Http::MockAsyncClientRequest request(&cm_.async_client_); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback{}; - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(DoAll(WithArg<1>(SaveArgAddress(&callback)), Return(&request))); // Trigger flush of a span. @@ -398,11 +398,11 @@ TEST_F(LightStepDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { TEST_F(LightStepDriverTest, FlushOneFailure) { setupValidDriver(1); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback = nullptr; const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, @@ -445,11 +445,11 @@ TEST_F(LightStepDriverTest, FlushOneFailure) { TEST_F(LightStepDriverTest, FlushWithActiveReport) { setupValidDriver(1); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback = nullptr; const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, @@ -490,11 +490,11 @@ TEST_F(LightStepDriverTest, FlushWithActiveReport) { TEST_F(LightStepDriverTest, OnFullWithActiveReport) { setupValidDriver(1); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback = nullptr; const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, @@ -538,11 +538,11 @@ TEST_F(LightStepDriverTest, OnFullWithActiveReport) { TEST_F(LightStepDriverTest, FlushSpansTimer) { setupValidDriver(); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback = nullptr; const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& /*message*/, Http::AsyncClient::Callbacks& callbacks, @@ -575,11 +575,11 @@ TEST_F(LightStepDriverTest, FlushSpansTimer) { TEST_F(LightStepDriverTest, CancelRequestOnDestruction) { setupValidDriver(1); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback = nullptr; const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& /*message*/, Http::AsyncClient::Callbacks& callbacks, diff --git a/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc b/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc index 2fcce7450c7a..234e97cbc396 100644 --- a/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc +++ b/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc @@ -50,8 +50,8 @@ class ZipkinDriverTest : public testing::Test { void setup(envoy::config::trace::v3::ZipkinConfig& zipkin_config, bool init_timer) { cm_.thread_local_cluster_.cluster_.info_->name_ = "fake_cluster"; cm_.initializeThreadLocalClusters({"fake_cluster"}); - ON_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillByDefault(ReturnRef(cm_.async_client_)); + ON_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillByDefault(ReturnRef(cm_.thread_local_cluster_.async_client_)); if (init_timer) { timer_ = new NiceMock(&tls_.dispatcher_); @@ -89,11 +89,11 @@ class ZipkinDriverTest : public testing::Test { const std::string& hostname) { setupValidDriverWithHostname(version, hostname); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback; const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, @@ -233,11 +233,11 @@ TEST_F(ZipkinDriverTest, FlushSeveralSpansHttpProto) { TEST_F(ZipkinDriverTest, FlushOneSpanReportFailure) { setupValidDriver("HTTP_JSON"); - Http::MockAsyncClientRequest request(&cm_.async_client_); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback; const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, @@ -292,8 +292,8 @@ TEST_F(ZipkinDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterRemoval("fake_cluster"); // Verify that no report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster(_)).Times(0); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)).Times(0); // Trigger flush of a span. driver_ @@ -316,8 +316,8 @@ TEST_F(ZipkinDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterAddOrUpdate(unrelated_cluster); // Verify that no report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster(_)).Times(0); - EXPECT_CALL(cm_.async_client_, send_(_, _, _)).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()).Times(0); + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)).Times(0); // Trigger flush of a span. driver_ @@ -338,11 +338,11 @@ TEST_F(ZipkinDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterAddOrUpdate(cm_.thread_local_cluster_); // Verify that report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillOnce(ReturnRef(cm_.async_client_)); - Http::MockAsyncClientRequest request(&cm_.async_client_); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback{}; - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(DoAll(WithArg<1>(SaveArgAddress(&callback)), Return(&request))); // Trigger flush of a span. @@ -367,11 +367,11 @@ TEST_F(ZipkinDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { cluster_update_callbacks->onClusterRemoval("unrelated_cluster"); // Verify that report will be sent. - EXPECT_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) - .WillOnce(ReturnRef(cm_.async_client_)); - Http::MockAsyncClientRequest request(&cm_.async_client_); + EXPECT_CALL(cm_.thread_local_cluster_, httpAsyncClient()) + .WillOnce(ReturnRef(cm_.thread_local_cluster_.async_client_)); + Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback{}; - EXPECT_CALL(cm_.async_client_, send_(_, _, _)) + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, _)) .WillOnce(DoAll(WithArg<1>(SaveArgAddress(&callback)), Return(&request))); // Trigger flush of a span. @@ -397,13 +397,15 @@ TEST_F(ZipkinDriverTest, SkipReportIfCollectorClusterHasBeenRemoved) { TEST_F(ZipkinDriverTest, CancelInflightRequestsOnDestruction) { setupValidDriver("HTTP_JSON"); - StrictMock request1(&cm_.async_client_), - request2(&cm_.async_client_), request3(&cm_.async_client_), request4(&cm_.async_client_); + StrictMock request1(&cm_.thread_local_cluster_.async_client_), + request2(&cm_.thread_local_cluster_.async_client_), + request3(&cm_.thread_local_cluster_.async_client_), + request4(&cm_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback{}; const absl::optional timeout(std::chrono::seconds(5)); // Expect 4 separate report requests to be made. - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))) .WillOnce(DoAll(WithArg<1>(SaveArgAddress(&callback)), Return(&request1))) .WillOnce(Return(&request2)) @@ -458,7 +460,7 @@ TEST_F(ZipkinDriverTest, FlushSpansTimer) { setupValidDriver("HTTP_JSON"); const absl::optional timeout(std::chrono::seconds(5)); - EXPECT_CALL(cm_.async_client_, + EXPECT_CALL(cm_.thread_local_cluster_.async_client_, send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(timeout))); EXPECT_CALL(runtime_.snapshot_, getInteger("tracing.zipkin.min_flush_spans", 5)) diff --git a/test/extensions/upstreams/http/tcp/upstream_request_test.cc b/test/extensions/upstreams/http/tcp/upstream_request_test.cc index 889fed89a652..f921bcbf6336 100644 --- a/test/extensions/upstreams/http/tcp/upstream_request_test.cc +++ b/test/extensions/upstreams/http/tcp/upstream_request_test.cc @@ -38,7 +38,8 @@ class TcpConnPoolTest : public ::testing::Test { TcpConnPoolTest() : host_(std::make_shared>()) { NiceMock route_entry; NiceMock cm; - EXPECT_CALL(cm, tcpConnPoolForCluster(_, _, _)).WillOnce(Return(&mock_pool_)); + cm.initializeThreadLocalClusters({"fake_cluster"}); + EXPECT_CALL(cm.thread_local_cluster_, tcpConnPool(_, _)).WillOnce(Return(&mock_pool_)); conn_pool_ = std::make_unique(cm, true, route_entry, Envoy::Http::Protocol::Http11, nullptr); } diff --git a/test/integration/tcp_conn_pool_integration_test.cc b/test/integration/tcp_conn_pool_integration_test.cc index 159b083a3937..a4cca156cb67 100644 --- a/test/integration/tcp_conn_pool_integration_test.cc +++ b/test/integration/tcp_conn_pool_integration_test.cc @@ -24,8 +24,9 @@ class TestFilter : public Network::ReadFilter { Network::FilterStatus onData(Buffer::Instance& data, bool end_stream) override { UNREFERENCED_PARAMETER(end_stream); - Tcp::ConnectionPool::Instance* pool = cluster_manager_.tcpConnPoolForCluster( - "cluster_0", Upstream::ResourcePriority::Default, nullptr); + Tcp::ConnectionPool::Instance* pool = + cluster_manager_.getThreadLocalCluster("cluster_0") + ->tcpConnPool(Upstream::ResourcePriority::Default, nullptr); ASSERT(pool != nullptr); requests_.emplace_back(*this, data); diff --git a/test/mocks/http/conn_pool.h b/test/mocks/http/conn_pool.h index fcfb5e090c51..cdca565449d0 100644 --- a/test/mocks/http/conn_pool.h +++ b/test/mocks/http/conn_pool.h @@ -1,3 +1,5 @@ +#pragma once + #include #include "envoy/http/conn_pool.h" diff --git a/test/mocks/upstream/BUILD b/test/mocks/upstream/BUILD index b7a3ceaaedf6..a6d849e474c4 100644 --- a/test/mocks/upstream/BUILD +++ b/test/mocks/upstream/BUILD @@ -210,6 +210,9 @@ envoy_cc_mock( hdrs = ["thread_local_cluster.h"], deps = [ "//include/envoy/upstream:thread_local_cluster_interface", + "//test/mocks/http:conn_pool_mocks", + "//test/mocks/http:http_mocks", + "//test/mocks/tcp:tcp_mocks", "//test/mocks/upstream:cluster_priority_set_mocks", "//test/mocks/upstream:load_balancer_mocks", ], diff --git a/test/mocks/upstream/cluster_manager.cc b/test/mocks/upstream/cluster_manager.cc index 2aaf1d5140f1..a7a23633291f 100644 --- a/test/mocks/upstream/cluster_manager.cc +++ b/test/mocks/upstream/cluster_manager.cc @@ -20,10 +20,6 @@ MockClusterManager::MockClusterManager() : cluster_stat_names_(*symbol_table_), cluster_load_report_stat_names_(*symbol_table_), cluster_request_response_size_stat_names_(*symbol_table_), cluster_timeout_budget_stat_names_(*symbol_table_) { - ON_CALL(*this, httpConnPoolForCluster(_, _, _, _)).WillByDefault(Return(&conn_pool_)); - ON_CALL(*this, tcpConnPoolForCluster(_, _, _)).WillByDefault(Return(&tcp_conn_pool_)); - ON_CALL(*this, httpAsyncClientForCluster(_)).WillByDefault(ReturnRef(async_client_)); - ON_CALL(*this, httpAsyncClientForCluster(_)).WillByDefault((ReturnRef(async_client_))); ON_CALL(*this, bindConfig()).WillByDefault(ReturnRef(bind_config_)); ON_CALL(*this, adsMux()).WillByDefault(Return(ads_mux_)); ON_CALL(*this, grpcAsyncClientManager()).WillByDefault(ReturnRef(async_client_manager_)); diff --git a/test/mocks/upstream/cluster_manager.h b/test/mocks/upstream/cluster_manager.h index 445408d4eb5c..90cbec0ec757 100644 --- a/test/mocks/upstream/cluster_manager.h +++ b/test/mocks/upstream/cluster_manager.h @@ -26,12 +26,6 @@ class MockClusterManager : public ClusterManager { return ClusterUpdateCallbacksHandlePtr{addThreadLocalClusterUpdateCallbacks_(callbacks)}; } - Host::CreateConnectionData tcpConnForCluster(const std::string& cluster, - LoadBalancerContext* context) override { - MockHost::MockCreateConnectionData data = tcpConnForCluster_(cluster, context); - return {Network::ClientConnectionPtr{data.connection_}, data.host_description_}; - } - ClusterManagerFactory& clusterManagerFactory() override { return cluster_manager_factory_; } void initializeClusters(const std::vector& active_cluster_names, @@ -51,15 +45,6 @@ class MockClusterManager : public ClusterManager { MOCK_METHOD(const ClusterSet&, primaryClusters, ()); MOCK_METHOD(ThreadLocalCluster*, getThreadLocalCluster, (absl::string_view cluster)); - MOCK_METHOD(Http::ConnectionPool::Instance*, httpConnPoolForCluster, - (const std::string& cluster, ResourcePriority priority, - absl::optional downstream_protocol, LoadBalancerContext* context)); - MOCK_METHOD(Tcp::ConnectionPool::Instance*, tcpConnPoolForCluster, - (const std::string& cluster, ResourcePriority priority, - LoadBalancerContext* context)); - MOCK_METHOD(MockHost::MockCreateConnectionData, tcpConnForCluster_, - (const std::string& cluster, LoadBalancerContext* context)); - MOCK_METHOD(Http::AsyncClient&, httpAsyncClientForCluster, (const std::string& cluster)); MOCK_METHOD(bool, removeCluster, (const std::string& cluster)); MOCK_METHOD(void, shutdown, ()); MOCK_METHOD(const envoy::config::core::v3::BindConfig&, bindConfig, (), (const)); @@ -81,9 +66,6 @@ class MockClusterManager : public ClusterManager { return cluster_timeout_budget_stat_names_; } - NiceMock conn_pool_; - NiceMock async_client_; - NiceMock tcp_conn_pool_; NiceMock thread_local_cluster_; envoy::config::core::v3::BindConfig bind_config_; std::shared_ptr> ads_mux_; diff --git a/test/mocks/upstream/thread_local_cluster.cc b/test/mocks/upstream/thread_local_cluster.cc index 0ab62164b6ff..9e0d9eedd33e 100644 --- a/test/mocks/upstream/thread_local_cluster.cc +++ b/test/mocks/upstream/thread_local_cluster.cc @@ -3,14 +3,19 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -namespace Envoy { -namespace Upstream { using ::testing::Return; using ::testing::ReturnRef; + +namespace Envoy { +namespace Upstream { + MockThreadLocalCluster::MockThreadLocalCluster() { ON_CALL(*this, prioritySet()).WillByDefault(ReturnRef(cluster_.priority_set_)); ON_CALL(*this, info()).WillByDefault(Return(cluster_.info_)); ON_CALL(*this, loadBalancer()).WillByDefault(ReturnRef(lb_)); + ON_CALL(*this, httpConnPool(_, _, _)).WillByDefault(Return(&conn_pool_)); + ON_CALL(*this, tcpConnPool(_, _)).WillByDefault(Return(&tcp_conn_pool_)); + ON_CALL(*this, httpAsyncClient()).WillByDefault(ReturnRef(async_client_)); } MockThreadLocalCluster::~MockThreadLocalCluster() = default; diff --git a/test/mocks/upstream/thread_local_cluster.h b/test/mocks/upstream/thread_local_cluster.h index 34eda63df6cb..d4900d18c493 100644 --- a/test/mocks/upstream/thread_local_cluster.h +++ b/test/mocks/upstream/thread_local_cluster.h @@ -2,26 +2,48 @@ #include "envoy/upstream/thread_local_cluster.h" +#include "test/mocks/http/conn_pool.h" +#include "test/mocks/http/mocks.h" +#include "test/mocks/tcp/mocks.h" + #include "cluster_priority_set.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "load_balancer.h" +using ::testing::NiceMock; + namespace Envoy { namespace Upstream { -using ::testing::NiceMock; + class MockThreadLocalCluster : public ThreadLocalCluster { public: MockThreadLocalCluster(); ~MockThreadLocalCluster() override; + Host::CreateConnectionData tcpConn(LoadBalancerContext* context) override { + MockHost::MockCreateConnectionData data = tcpConn_(context); + return {Network::ClientConnectionPtr{data.connection_}, data.host_description_}; + } + // Upstream::ThreadLocalCluster MOCK_METHOD(const PrioritySet&, prioritySet, ()); MOCK_METHOD(ClusterInfoConstSharedPtr, info, ()); MOCK_METHOD(LoadBalancer&, loadBalancer, ()); + MOCK_METHOD(Http::ConnectionPool::Instance*, httpConnPool, + (ResourcePriority priority, absl::optional downstream_protocol, + LoadBalancerContext* context)); + MOCK_METHOD(Tcp::ConnectionPool::Instance*, tcpConnPool, + (ResourcePriority priority, LoadBalancerContext* context)); + MOCK_METHOD(MockHost::MockCreateConnectionData, tcpConn_, (LoadBalancerContext * context)); + MOCK_METHOD(Http::AsyncClient&, httpAsyncClient, ()); NiceMock cluster_; NiceMock lb_; + NiceMock conn_pool_; + NiceMock async_client_; + NiceMock tcp_conn_pool_; }; + } // namespace Upstream } // namespace Envoy diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index b0ad107ac87f..3adc03ff3ca9 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -77,7 +77,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/watchdog/profile_action:84.9" "source/server:94.5" "source/server/admin:95.1" -"source/server/config_validation:76.6" +"source/server/config_validation:75.9" ) [[ -z "${SRCDIR}" ]] && SRCDIR="${PWD}" diff --git a/test/server/config_validation/BUILD b/test/server/config_validation/BUILD index 4058746d0a21..235789ae12cf 100644 --- a/test/server/config_validation/BUILD +++ b/test/server/config_validation/BUILD @@ -6,19 +6,6 @@ licenses(["notice"]) # Apache 2 envoy_package() -envoy_cc_test( - name = "async_client_test", - srcs = ["async_client_test.cc"], - deps = [ - "//include/envoy/http:message_interface", - "//source/common/http:message_lib", - "//source/server/config_validation:async_client_lib", - "//source/server/config_validation:dns_lib", - "//test/mocks/http:http_mocks", - "//test/test_common:simulated_time_system_lib", - ], -) - envoy_cc_test( name = "cluster_manager_test", srcs = ["cluster_manager_test.cc"], diff --git a/test/server/config_validation/async_client_test.cc b/test/server/config_validation/async_client_test.cc deleted file mode 100644 index 5e49be122f7b..000000000000 --- a/test/server/config_validation/async_client_test.cc +++ /dev/null @@ -1,28 +0,0 @@ -#include "envoy/http/message.h" - -#include "common/http/message_impl.h" - -#include "server/config_validation/async_client.h" - -#include "test/mocks/http/mocks.h" -#include "test/test_common/simulated_time_system.h" - -namespace Envoy { -namespace Http { -namespace { - -TEST(ValidationAsyncClientTest, MockedMethods) { - RequestMessagePtr message{new RequestMessageImpl()}; - MockAsyncClientCallbacks callbacks; - MockAsyncClientStreamCallbacks stream_callbacks; - - Event::SimulatedTimeSystem time_system; - Api::ApiPtr api = Api::createApiForTest(time_system); - ValidationAsyncClient client(*api, time_system); - EXPECT_EQ(nullptr, client.send(std::move(message), callbacks, AsyncClient::RequestOptions())); - EXPECT_EQ(nullptr, client.start(stream_callbacks, AsyncClient::StreamOptions())); -} - -} // namespace -} // namespace Http -} // namespace Envoy diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index bcc3accfe878..69ef6b8fdf08 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -51,19 +51,11 @@ TEST(ValidationClusterManagerTest, MockedMethods) { ValidationClusterManagerFactory factory( admin, runtime, stats_store, tls, dns_resolver, ssl_context_manager, dispatcher, local_info, secret_manager, validation_context, *api, http_context, grpc_context, router_context, - log_manager, singleton_manager, time_system); + log_manager, singleton_manager); const envoy::config::bootstrap::v3::Bootstrap bootstrap; ClusterManagerPtr cluster_manager = factory.clusterManagerFromProto(bootstrap); - EXPECT_EQ(nullptr, cluster_manager->httpConnPoolForCluster("cluster", ResourcePriority::Default, - Http::Protocol::Http11, nullptr)); - Host::CreateConnectionData data = cluster_manager->tcpConnForCluster("cluster", nullptr); - EXPECT_EQ(nullptr, data.connection_); - EXPECT_EQ(nullptr, data.host_description_); - - Http::AsyncClient& client = cluster_manager->httpAsyncClientForCluster("cluster"); - Http::MockAsyncClientStreamCallbacks stream_callbacks; - EXPECT_EQ(nullptr, client.start(stream_callbacks, Http::AsyncClient::StreamOptions())); + EXPECT_EQ(nullptr, cluster_manager->getThreadLocalCluster("cluster")); } } // namespace From 424909395c90d7d68f1afeb3427c26c7c85f2672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Hern=C3=A1ndez?= Date: Fri, 11 Dec 2020 06:11:24 +0100 Subject: [PATCH 09/49] jwt_authn: Document that timeout is required in http_uri (#14278) This patch fixes some examples in the documentation that don't include the mandatory `http_uri.timeout` field and don't add the `tls_transport` field required to fetch JWT signature verification keys from HTTPS servers. Risk Level: None, docs only Testing: N/A Docs Changes: Added Release Notes: N/A Fixes #14277 Signed-off-by: Juan Hernandez --- api/envoy/extensions/filters/http/jwt_authn/v3/config.proto | 5 +++++ .../extensions/filters/http/jwt_authn/v4alpha/config.proto | 5 +++++ .../configuration/http/http_filters/jwt_authn_filter.rst | 5 ++++- .../envoy/extensions/filters/http/jwt_authn/v3/config.proto | 5 +++++ .../extensions/filters/http/jwt_authn/v4alpha/config.proto | 5 +++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto index a10fa68f3043..3a23f2bcb2bc 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto +++ b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto @@ -48,6 +48,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster +// timeout: 1s // cache_duration: // seconds: 300 // @@ -94,6 +95,7 @@ message JwtProvider { // http_uri: // uri: https://www.googleapis.com/oauth2/v1/certs // cluster: jwt.www.googleapis.com|443 + // timeout: 1s // cache_duration: // seconds: 300 // @@ -209,6 +211,7 @@ message RemoteJwks { // http_uri: // uri: https://www.googleapis.com/oauth2/v1/certs // cluster: jwt.www.googleapis.com|443 + // timeout: 1s // config.core.v3.HttpUri http_uri = 1; @@ -451,6 +454,7 @@ message FilterStateRule { // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster +// timeout: 1s // provider2: // issuer: issuer2 // local_jwks: @@ -495,6 +499,7 @@ message JwtAuthentication { // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster + // timeout: 1s // provider2: // issuer: provider2 // local_jwks: diff --git a/api/envoy/extensions/filters/http/jwt_authn/v4alpha/config.proto b/api/envoy/extensions/filters/http/jwt_authn/v4alpha/config.proto index 2746640fa738..708ccb23e6bd 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v4alpha/config.proto +++ b/api/envoy/extensions/filters/http/jwt_authn/v4alpha/config.proto @@ -48,6 +48,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster +// timeout: 1s // cache_duration: // seconds: 300 // @@ -94,6 +95,7 @@ message JwtProvider { // http_uri: // uri: https://www.googleapis.com/oauth2/v1/certs // cluster: jwt.www.googleapis.com|443 + // timeout: 1s // cache_duration: // seconds: 300 // @@ -209,6 +211,7 @@ message RemoteJwks { // http_uri: // uri: https://www.googleapis.com/oauth2/v1/certs // cluster: jwt.www.googleapis.com|443 + // timeout: 1s // config.core.v4alpha.HttpUri http_uri = 1; @@ -451,6 +454,7 @@ message FilterStateRule { // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster +// timeout: 1s // provider2: // issuer: issuer2 // local_jwks: @@ -495,6 +499,7 @@ message JwtAuthentication { // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster + // timeout: 1s // provider2: // issuer: provider2 // local_jwks: diff --git a/docs/root/configuration/http/http_filters/jwt_authn_filter.rst b/docs/root/configuration/http/http_filters/jwt_authn_filter.rst index 50790a230905..f8634e2971bc 100644 --- a/docs/root/configuration/http/http_filters/jwt_authn_filter.rst +++ b/docs/root/configuration/http/http_filters/jwt_authn_filter.rst @@ -77,6 +77,7 @@ Remote JWKS config example http_uri: uri: https://example.com/jwks.json cluster: example_jwks_cluster + timeout: 1s cache_duration: seconds: 300 @@ -97,7 +98,9 @@ Following cluster **example_jwks_cluster** is needed to fetch JWKS. address: socket_address: address: example.com - port_value: 80 + port_value: 443 + transport_socket: + name: envoy.transport_sockets.tls Inline JWKS config example diff --git a/generated_api_shadow/envoy/extensions/filters/http/jwt_authn/v3/config.proto b/generated_api_shadow/envoy/extensions/filters/http/jwt_authn/v3/config.proto index a10fa68f3043..3a23f2bcb2bc 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/jwt_authn/v3/config.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/jwt_authn/v3/config.proto @@ -48,6 +48,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster +// timeout: 1s // cache_duration: // seconds: 300 // @@ -94,6 +95,7 @@ message JwtProvider { // http_uri: // uri: https://www.googleapis.com/oauth2/v1/certs // cluster: jwt.www.googleapis.com|443 + // timeout: 1s // cache_duration: // seconds: 300 // @@ -209,6 +211,7 @@ message RemoteJwks { // http_uri: // uri: https://www.googleapis.com/oauth2/v1/certs // cluster: jwt.www.googleapis.com|443 + // timeout: 1s // config.core.v3.HttpUri http_uri = 1; @@ -451,6 +454,7 @@ message FilterStateRule { // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster +// timeout: 1s // provider2: // issuer: issuer2 // local_jwks: @@ -495,6 +499,7 @@ message JwtAuthentication { // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster + // timeout: 1s // provider2: // issuer: provider2 // local_jwks: diff --git a/generated_api_shadow/envoy/extensions/filters/http/jwt_authn/v4alpha/config.proto b/generated_api_shadow/envoy/extensions/filters/http/jwt_authn/v4alpha/config.proto index 2746640fa738..708ccb23e6bd 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/jwt_authn/v4alpha/config.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/jwt_authn/v4alpha/config.proto @@ -48,6 +48,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster +// timeout: 1s // cache_duration: // seconds: 300 // @@ -94,6 +95,7 @@ message JwtProvider { // http_uri: // uri: https://www.googleapis.com/oauth2/v1/certs // cluster: jwt.www.googleapis.com|443 + // timeout: 1s // cache_duration: // seconds: 300 // @@ -209,6 +211,7 @@ message RemoteJwks { // http_uri: // uri: https://www.googleapis.com/oauth2/v1/certs // cluster: jwt.www.googleapis.com|443 + // timeout: 1s // config.core.v4alpha.HttpUri http_uri = 1; @@ -451,6 +454,7 @@ message FilterStateRule { // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster +// timeout: 1s // provider2: // issuer: issuer2 // local_jwks: @@ -495,6 +499,7 @@ message JwtAuthentication { // http_uri: // uri: https://example.com/.well-known/jwks.json // cluster: example_jwks_cluster + // timeout: 1s // provider2: // issuer: provider2 // local_jwks: From b0fedbe914092124dbffb0e9d3e8ea8928f74bb9 Mon Sep 17 00:00:00 2001 From: Dmitry Rozhkov Date: Fri, 11 Dec 2020 19:15:05 +0200 Subject: [PATCH 10/49] compressor: add support for compressing request payloads (#14302) The compressor filter adds support for compressing request payloads. Its configuration is unified with the decompressor filter with two new fields for different directions - requests and responses. The latter deprecates the old response-specific fields and, if used, roots the response-specific stats in .compressor...response.* instead of .compressor...*. Signed-off-by: Dmitry Rozhkov --- .../http/compressor/v3/compressor.proto | 73 +++++- .../filters/http/compressor/v4alpha/BUILD | 13 + .../http/compressor/v4alpha/compressor.proto | 106 ++++++++ .../filters/http/gzip/v4alpha/BUILD | 13 + .../filters/http/gzip/v4alpha/gzip.proto | 84 +++++++ .../http/http_filters/compressor_filter.rst | 116 ++++++++- .../http/http_filters/decompressor_filter.rst | 4 +- docs/root/version_history/current.rst | 2 + .../http/compressor/v3/compressor.proto | 73 +++++- .../filters/http/compressor/v4alpha/BUILD | 13 + .../http/compressor/v4alpha/compressor.proto | 129 ++++++++++ .../filters/http/gzip/v4alpha/BUILD | 13 + .../filters/http/gzip/v4alpha/gzip.proto | 84 +++++++ source/common/http/headers.h | 2 + .../http/common/compressor/compressor.cc | 227 ++++++++++++++---- .../http/common/compressor/compressor.h | 144 ++++++++--- .../compressor/compressor_filter_test.cc | 138 ++++++++--- .../compressor_filter_integration_test.cc | 73 +++++- .../filters/http/gzip/gzip_filter_test.cc | 16 +- 19 files changed, 1153 insertions(+), 170 deletions(-) create mode 100644 api/envoy/extensions/filters/http/compressor/v4alpha/BUILD create mode 100644 api/envoy/extensions/filters/http/compressor/v4alpha/compressor.proto create mode 100644 api/envoy/extensions/filters/http/gzip/v4alpha/BUILD create mode 100644 api/envoy/extensions/filters/http/gzip/v4alpha/gzip.proto create mode 100644 generated_api_shadow/envoy/extensions/filters/http/compressor/v4alpha/BUILD create mode 100644 generated_api_shadow/envoy/extensions/filters/http/compressor/v4alpha/compressor.proto create mode 100644 generated_api_shadow/envoy/extensions/filters/http/gzip/v4alpha/BUILD create mode 100644 generated_api_shadow/envoy/extensions/filters/http/gzip/v4alpha/gzip.proto diff --git a/api/envoy/extensions/filters/http/compressor/v3/compressor.proto b/api/envoy/extensions/filters/http/compressor/v3/compressor.proto index 0bfa5c1860d4..6519697a89db 100644 --- a/api/envoy/extensions/filters/http/compressor/v3/compressor.proto +++ b/api/envoy/extensions/filters/http/compressor/v3/compressor.proto @@ -21,40 +21,99 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // Compressor :ref:`configuration overview `. // [#extension: envoy.filters.http.compressor] -// [#next-free-field: 7] +// [#next-free-field: 9] message Compressor { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.compressor.v2.Compressor"; + message CommonDirectionConfig { + // Runtime flag that controls whether compression is enabled or not for the direction this + // common config is put in. If set to false, the filter will operate as a pass-through filter + // in the chosen direction. If the field is omitted, the filter will be enabled. + config.core.v3.RuntimeFeatureFlag enabled = 1; + + // Minimum value of Content-Length header of request or response messages (depending on the direction + // this common config is put in), in bytes, which will trigger compression. The default value is 30. + google.protobuf.UInt32Value min_content_length = 2; + + // Set of strings that allows specifying which mime-types yield compression; e.g., + // application/json, text/html, etc. When this field is not defined, compression will be applied + // to the following mime-types: "application/javascript", "application/json", + // "application/xhtml+xml", "image/svg+xml", "text/css", "text/html", "text/plain", "text/xml" + // and their synonyms. + repeated string content_type = 3; + } + + // Configuration for filter behavior on the request direction. + message RequestDirectionConfig { + CommonDirectionConfig common_config = 1; + } + + // Configuration for filter behavior on the response direction. + message ResponseDirectionConfig { + CommonDirectionConfig common_config = 1; + + // If true, disables compression when the response contains an etag header. When it is false, the + // filter will preserve weak etags and remove the ones that require strong validation. + bool disable_on_etag_header = 2; + + // If true, removes accept-encoding from the request headers before dispatching it to the upstream + // so that responses do not get compressed before reaching the filter. + // + // .. attention:: + // + // To avoid interfering with other compression filters in the same chain use this option in + // the filter closest to the upstream. + bool remove_accept_encoding_header = 3; + } + // Minimum response length, in bytes, which will trigger compression. The default value is 30. - google.protobuf.UInt32Value content_length = 1; + google.protobuf.UInt32Value content_length = 1 [deprecated = true]; // Set of strings that allows specifying which mime-types yield compression; e.g., // application/json, text/html, etc. When this field is not defined, compression will be applied // to the following mime-types: "application/javascript", "application/json", // "application/xhtml+xml", "image/svg+xml", "text/css", "text/html", "text/plain", "text/xml" // and their synonyms. - repeated string content_type = 2; + repeated string content_type = 2 [deprecated = true]; // If true, disables compression when the response contains an etag header. When it is false, the // filter will preserve weak etags and remove the ones that require strong validation. - bool disable_on_etag_header = 3; + bool disable_on_etag_header = 3 [deprecated = true]; // If true, removes accept-encoding from the request headers before dispatching it to the upstream // so that responses do not get compressed before reaching the filter. - // .. attention: + // + // .. attention:: // // To avoid interfering with other compression filters in the same chain use this option in // the filter closest to the upstream. - bool remove_accept_encoding_header = 4; + bool remove_accept_encoding_header = 4 [deprecated = true]; // Runtime flag that controls whether the filter is enabled or not. If set to false, the // filter will operate as a pass-through filter. If not specified, defaults to enabled. - config.core.v3.RuntimeFeatureFlag runtime_enabled = 5; + config.core.v3.RuntimeFeatureFlag runtime_enabled = 5 [deprecated = true]; // A compressor library to use for compression. Currently only // :ref:`envoy.compression.gzip.compressor` // is included in Envoy. // This field is ignored if used in the context of the gzip http-filter, but is mandatory otherwise. config.core.v3.TypedExtensionConfig compressor_library = 6; + + // Configuration for request compression. Compression is disabled by default if left empty. + RequestDirectionConfig request_direction_config = 7; + + // Configuration for response compression. Compression is enabled by default if left empty. + // + // .. attention:: + // + // If the field is not empty then the duplicate deprecated fields of the `Compressor` message, + // such as `content_length`, `content_type`, `disable_on_etag_header`, + // `remove_accept_encoding_header` and `runtime_enabled`, are ignored. + // + // Also all the statistics related to response compression will be rooted in + // `.compressor...response.*` + // instead of + // `.compressor...*`. + ResponseDirectionConfig response_direction_config = 8; } diff --git a/api/envoy/extensions/filters/http/compressor/v4alpha/BUILD b/api/envoy/extensions/filters/http/compressor/v4alpha/BUILD new file mode 100644 index 000000000000..251b6da666af --- /dev/null +++ b/api/envoy/extensions/filters/http/compressor/v4alpha/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/core/v4alpha:pkg", + "//envoy/extensions/filters/http/compressor/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/filters/http/compressor/v4alpha/compressor.proto b/api/envoy/extensions/filters/http/compressor/v4alpha/compressor.proto new file mode 100644 index 000000000000..6f2f78988604 --- /dev/null +++ b/api/envoy/extensions/filters/http/compressor/v4alpha/compressor.proto @@ -0,0 +1,106 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.compressor.v4alpha; + +import "envoy/config/core/v4alpha/base.proto"; +import "envoy/config/core/v4alpha/extension.proto"; + +import "google/protobuf/any.proto"; +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.compressor.v4alpha"; +option java_outer_classname = "CompressorProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Compressor] +// Compressor :ref:`configuration overview `. +// [#extension: envoy.filters.http.compressor] + +// [#next-free-field: 9] +message Compressor { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.compressor.v3.Compressor"; + + message CommonDirectionConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.compressor.v3.Compressor.CommonDirectionConfig"; + + // Runtime flag that controls whether compression is enabled or not for the direction this + // common config is put in. If set to false, the filter will operate as a pass-through filter + // in the chosen direction. If the field is omitted, the filter will be enabled. + config.core.v4alpha.RuntimeFeatureFlag enabled = 1; + + // Minimum value of Content-Length header of request or response messages (depending on the direction + // this common config is put in), in bytes, which will trigger compression. The default value is 30. + google.protobuf.UInt32Value min_content_length = 2; + + // Set of strings that allows specifying which mime-types yield compression; e.g., + // application/json, text/html, etc. When this field is not defined, compression will be applied + // to the following mime-types: "application/javascript", "application/json", + // "application/xhtml+xml", "image/svg+xml", "text/css", "text/html", "text/plain", "text/xml" + // and their synonyms. + repeated string content_type = 3; + } + + // Configuration for filter behavior on the request direction. + message RequestDirectionConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.compressor.v3.Compressor.RequestDirectionConfig"; + + CommonDirectionConfig common_config = 1; + } + + // Configuration for filter behavior on the response direction. + message ResponseDirectionConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.compressor.v3.Compressor.ResponseDirectionConfig"; + + CommonDirectionConfig common_config = 1; + + // If true, disables compression when the response contains an etag header. When it is false, the + // filter will preserve weak etags and remove the ones that require strong validation. + bool disable_on_etag_header = 2; + + // If true, removes accept-encoding from the request headers before dispatching it to the upstream + // so that responses do not get compressed before reaching the filter. + // + // .. attention:: + // + // To avoid interfering with other compression filters in the same chain use this option in + // the filter closest to the upstream. + bool remove_accept_encoding_header = 3; + } + + reserved 1, 2, 3, 4, 5; + + reserved "content_length", "content_type", "disable_on_etag_header", + "remove_accept_encoding_header", "runtime_enabled"; + + // A compressor library to use for compression. Currently only + // :ref:`envoy.compression.gzip.compressor` + // is included in Envoy. + // This field is ignored if used in the context of the gzip http-filter, but is mandatory otherwise. + config.core.v4alpha.TypedExtensionConfig compressor_library = 6; + + // Configuration for request compression. Compression is disabled by default if left empty. + RequestDirectionConfig request_direction_config = 7; + + // Configuration for response compression. Compression is enabled by default if left empty. + // + // .. attention:: + // + // If the field is not empty then the duplicate deprecated fields of the `Compressor` message, + // such as `content_length`, `content_type`, `disable_on_etag_header`, + // `remove_accept_encoding_header` and `runtime_enabled`, are ignored. + // + // Also all the statistics related to response compression will be rooted in + // `.compressor...response.*` + // instead of + // `.compressor...*`. + ResponseDirectionConfig response_direction_config = 8; +} diff --git a/api/envoy/extensions/filters/http/gzip/v4alpha/BUILD b/api/envoy/extensions/filters/http/gzip/v4alpha/BUILD new file mode 100644 index 000000000000..3b9648df0929 --- /dev/null +++ b/api/envoy/extensions/filters/http/gzip/v4alpha/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/extensions/filters/http/compressor/v4alpha:pkg", + "//envoy/extensions/filters/http/gzip/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/filters/http/gzip/v4alpha/gzip.proto b/api/envoy/extensions/filters/http/gzip/v4alpha/gzip.proto new file mode 100644 index 000000000000..f89be9a69742 --- /dev/null +++ b/api/envoy/extensions/filters/http/gzip/v4alpha/gzip.proto @@ -0,0 +1,84 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.gzip.v4alpha; + +import "envoy/extensions/filters/http/compressor/v4alpha/compressor.proto"; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.gzip.v4alpha"; +option java_outer_classname = "GzipProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Gzip] +// Gzip :ref:`configuration overview `. +// [#extension: envoy.filters.http.gzip] + +// [#next-free-field: 12] +message Gzip { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.gzip.v3.Gzip"; + + enum CompressionStrategy { + DEFAULT = 0; + FILTERED = 1; + HUFFMAN = 2; + RLE = 3; + } + + message CompressionLevel { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.gzip.v3.Gzip.CompressionLevel"; + + enum Enum { + DEFAULT = 0; + BEST = 1; + SPEED = 2; + } + } + + reserved 2, 6, 7, 8; + + reserved "content_length", "content_type", "disable_on_etag_header", + "remove_accept_encoding_header"; + + // Value from 1 to 9 that controls the amount of internal memory used by zlib. Higher values + // use more memory, but are faster and produce better compression results. The default value is 5. + google.protobuf.UInt32Value memory_level = 1 [(validate.rules).uint32 = {lte: 9 gte: 1}]; + + // A value used for selecting the zlib compression level. This setting will affect speed and + // amount of compression applied to the content. "BEST" provides higher compression at the cost of + // higher latency, "SPEED" provides lower compression with minimum impact on response time. + // "DEFAULT" provides an optimal result between speed and compression. This field will be set to + // "DEFAULT" if not specified. + CompressionLevel.Enum compression_level = 3 [(validate.rules).enum = {defined_only: true}]; + + // A value used for selecting the zlib compression strategy which is directly related to the + // characteristics of the content. Most of the time "DEFAULT" will be the best choice, though + // there are situations which changing this parameter might produce better results. For example, + // run-length encoding (RLE) is typically used when the content is known for having sequences + // which same data occurs many consecutive times. For more information about each strategy, please + // refer to zlib manual. + CompressionStrategy compression_strategy = 4 [(validate.rules).enum = {defined_only: true}]; + + // Value from 9 to 15 that represents the base two logarithmic of the compressor's window size. + // Larger window results in better compression at the expense of memory usage. The default is 12 + // which will produce a 4096 bytes window. For more details about this parameter, please refer to + // zlib manual > deflateInit2. + google.protobuf.UInt32Value window_bits = 9 [(validate.rules).uint32 = {lte: 15 gte: 9}]; + + // Set of configuration parameters common for all compression filters. If this field is set then + // the fields `content_length`, `content_type`, `disable_on_etag_header` and + // `remove_accept_encoding_header` are ignored. + compressor.v4alpha.Compressor compressor = 10; + + // Value for Zlib's next output buffer. If not set, defaults to 4096. + // See https://www.zlib.net/manual.html for more details. Also see + // https://github.com/envoyproxy/envoy/issues/8448 for context on this filter's performance. + google.protobuf.UInt32Value chunk_size = 11 [(validate.rules).uint32 = {lte: 65536 gte: 4096}]; +} diff --git a/docs/root/configuration/http/http_filters/compressor_filter.rst b/docs/root/configuration/http/http_filters/compressor_filter.rst index 2fa10f00d6bf..bb13424048ec 100644 --- a/docs/root/configuration/http/http_filters/compressor_filter.rst +++ b/docs/root/configuration/http/http_filters/compressor_filter.rst @@ -34,11 +34,18 @@ An example configuration of the filter may look like the following: - name: envoy.filters.http.compressor typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - disable_on_etag_header: true - content_length: 100 - content_type: - - text/html - - application/json + response_direction_config: + common_config: + min_content_length: 100 + content_type: + - text/html + - application/json + disable_on_etag_header: true + request_direction_config: + common_config: + enabled: + default_value: false + runtime_key: request_compressor_enabled compressor_library: name: text_optimized typed_config: @@ -48,7 +55,18 @@ An example configuration of the filter may look like the following: compression_level: best_compression compression_strategy: default_strategy -By *default* compression will be *skipped* when: +By *default* request compression is disabled, but when enabled it will be *skipped* if: + +- A request does not contain a *content-type* value that matches one of the selected + mime-types, which default to *application/javascript*, *application/json*, + *application/xhtml+xml*, *image/svg+xml*, *text/css*, *text/html*, *text/plain*, + *text/xml*. +- *content-length* header is not present in the request. +- A request contains a *content-encoding* header. +- A request contains a *transfer-encoding* header whose value includes a known + compression name. + +By *default* response compression is enabled, but it will be *skipped* when: - A request does NOT contain *accept-encoding* header. - A request includes *accept-encoding* header, but it does not contain "gzip" or "\*". @@ -60,7 +78,8 @@ By *default* compression will be *skipped* when: weight than "gzip"'s given the corresponding compression filter is present in the chain. - A response contains a *content-encoding* header. - A response contains a *cache-control* header whose value includes "no-transform". -- A response contains a *transfer-encoding* header whose value includes "gzip". +- A response contains a *transfer-encoding* header whose value includes a known + compression name. - A response does not contain a *content-type* value that matches one of the selected mime-types, which default to *application/javascript*, *application/json*, *application/xhtml+xml*, *image/svg+xml*, *text/css*, *text/html*, *text/plain*, @@ -74,7 +93,7 @@ Please note that in case the filter is configured to use a compression library e other than gzip it looks for content encoding in the *accept-encoding* header provided by the extension. -When compression is *applied*: +When response compression is *applied*: - The *content-length* is removed from response headers. - Response headers contain "*transfer-encoding: chunked*", and @@ -88,13 +107,68 @@ Otherwise, if an uncompressed response is cached by a caching proxy in front of the proxy won't know to fetch a new incoming request with compatible "*accept-encoding*" from upstream. +When request compression is *applied*: + +- *content-length* is removed from request headers. +- *content-encoding* with the compression scheme used (e.g., ``gzip``) is added to + request headers. + +Using different compressors for requests and responses +-------------------------------------------------------- + +If different compression libraries are desired for requests and responses, it is possible to install +multiple compressor filters enabled only for requests or responses. For instance: + +.. code-block:: yaml + + http_filters: + # This filter is only enabled for responses. + - name: envoy.filters.http.compressor + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor + request_direction_config: + common_config: + enabled: + default_value: false + runtime_key: request_compressor_enabled + compressor_library: + name: for_response + typed_config: + "@type": type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip + memory_level: 3 + window_bits: 10 + compression_level: best_compression + compression_strategy: default_strategy + # This filter is only enabled for requests. + - name: envoy.filters.http.compressor + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor + response_direction_config: + common_config: + enabled: + default_value: false + runtime_key: response_compressor_enabled + request_direction_config: + common_config: + enabled: + default_value: true + runtime_key: request_compressor_enabled + compressor_library: + name: for_request + typed_config: + "@type": type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip + memory_level: 9 + window_bits: 15 + compression_level: best_speed + compression_strategy: default_strategy + .. _compressor-statistics: Statistics ---------- Every configured Compressor filter has statistics rooted at -.compressor...* +.compressor....* with the following: .. csv-table:: @@ -103,13 +177,29 @@ with the following: compressed, Counter, Number of requests compressed. not_compressed, Counter, Number of requests not compressed. + total_uncompressed_bytes, Counter, The total uncompressed bytes of all the requests that were marked for compression. + total_compressed_bytes, Counter, The total compressed bytes of all the requests that were marked for compression. + content_length_too_small, Counter, Number of requests that accepted the compressor encoding but did not compress because the payload was too small. + +In addition to the statics common for requests and responses there are statistics +specific to responses only: + +.. csv-table:: + :header: Name, Type, Description + :widths: 1, 1, 2 + no_accept_header, Counter, Number of requests with no accept header sent. header_identity, Counter, Number of requests sent with "identity" set as the *accept-encoding*. - header_compressor_used, Counter, Number of requests sent with "gzip" set as the *accept-encoding*. + header_compressor_used, Counter, Number of requests sent with filter's configured encoding set as the *accept-encoding*. header_compressor_overshadowed, Counter, Number of requests skipped by this filter instance because they were handled by another filter in the same filter chain. header_wildcard, Counter, Number of requests sent with "\*" set as the *accept-encoding*. header_not_valid, Counter, Number of requests sent with a not valid *accept-encoding* header (aka "q=0" or an unsupported encoding type). - total_uncompressed_bytes, Counter, The total uncompressed bytes of all the requests that were marked for compression. - total_compressed_bytes, Counter, The total compressed bytes of all the requests that were marked for compression. - content_length_too_small, Counter, Number of requests that accepted gzip encoding but did not compress because the payload was too small. not_compressed_etag, Counter, Number of requests that were not compressed due to the etag header. *disable_on_etag_header* must be turned on for this to happen. + +.. attention: + + In case the compressor is not configured to compress responses with the field + `response_direction_config` of the :ref:`Compressor ` + message the stats are rooted in the legacy tree + .compressor...*, that is without + the direction prefix. diff --git a/docs/root/configuration/http/http_filters/decompressor_filter.rst b/docs/root/configuration/http/http_filters/decompressor_filter.rst index a906144c5541..e004fedfb0f3 100644 --- a/docs/root/configuration/http/http_filters/decompressor_filter.rst +++ b/docs/root/configuration/http/http_filters/decompressor_filter.rst @@ -56,8 +56,6 @@ When decompression is *applied*: - *x-envoy-decompressor---bytes* trailers are added to the request/response to relay information about decompression. -.. _decompressor-statistics: - Using different decompressors for requests and responses -------------------------------------------------------- @@ -98,6 +96,8 @@ multiple decompressor filters enabled only for requests or responses. For instan default_value: false runtime_key: request_decompressor_enabled +.. _decompressor-statistics: + Statistics ---------- diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 5bfd82541566..18e582ac71e5 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -57,6 +57,7 @@ Removed Config or Runtime New Features ------------ +* compression: the :ref:`compressor ` filter adds support for compressing request payloads. Its configuration is unified with the :ref:`decompressor ` filter with two new fields for different directions - :ref:`requests ` and :ref:`responses `. The latter deprecates the old response-specific fields and, if used, roots the response-specific stats in `.compressor...response.*` instead of `.compressor...*`. * config: added new runtime feature `envoy.features.enable_all_deprecated_features` that allows the use of all deprecated features. * grpc: implemented header value syntax support when defining :ref:`initial metadata ` for gRPC-based `ext_authz` :ref:`HTTP ` and :ref:`network ` filters, and :ref:`ratelimit ` filters. * grpc-json: added support for configuring :ref:`unescaping behavior ` for path components. @@ -89,6 +90,7 @@ New Features Deprecated ---------- * cluster: HTTP configuration for upstream clusters has beem reworked. HTTP-specific configuration is now done in the new :ref:`http_protocol_options ` message, configured via the cluster's :ref:`extension_protocol_options`. This replaces explicit HTTP configuration in cluster config, including :ref:`upstream_http_protocol_options` :ref:`common_http_protocol_options` :ref:`http_protocol_options` :ref:`http2_protocol_options` and :ref:`protocol_selection`. Examples of before-and-after configuration can be found in the :ref:`http_protocol_options docs ` and all of Envoy's example configurations have been updated to the new style of config. +* compression: the fields :ref:`content_length `, :ref:`content_type `, :ref:`disable_on_etag_header `, :ref:`remove_accept_encoding_header ` and :ref:`runtime_enabled ` of the :ref:`Compressor ` message have been deprecated in favor of :ref:`response_direction_config `. * gzip: :ref:`HTTP Gzip filter ` is rejected now unless explicitly allowed with :ref:`runtime override ` `envoy.deprecated_features.allow_deprecated_gzip_http_filter` set to `true`. * logging: the `--log-format-prefix-with-location` option is removed. * ratelimit: the :ref:`dynamic metadata ` action is deprecated in favor of the more generic :ref:`metadata ` action. diff --git a/generated_api_shadow/envoy/extensions/filters/http/compressor/v3/compressor.proto b/generated_api_shadow/envoy/extensions/filters/http/compressor/v3/compressor.proto index 0bfa5c1860d4..6519697a89db 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/compressor/v3/compressor.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/compressor/v3/compressor.proto @@ -21,40 +21,99 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // Compressor :ref:`configuration overview `. // [#extension: envoy.filters.http.compressor] -// [#next-free-field: 7] +// [#next-free-field: 9] message Compressor { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.compressor.v2.Compressor"; + message CommonDirectionConfig { + // Runtime flag that controls whether compression is enabled or not for the direction this + // common config is put in. If set to false, the filter will operate as a pass-through filter + // in the chosen direction. If the field is omitted, the filter will be enabled. + config.core.v3.RuntimeFeatureFlag enabled = 1; + + // Minimum value of Content-Length header of request or response messages (depending on the direction + // this common config is put in), in bytes, which will trigger compression. The default value is 30. + google.protobuf.UInt32Value min_content_length = 2; + + // Set of strings that allows specifying which mime-types yield compression; e.g., + // application/json, text/html, etc. When this field is not defined, compression will be applied + // to the following mime-types: "application/javascript", "application/json", + // "application/xhtml+xml", "image/svg+xml", "text/css", "text/html", "text/plain", "text/xml" + // and their synonyms. + repeated string content_type = 3; + } + + // Configuration for filter behavior on the request direction. + message RequestDirectionConfig { + CommonDirectionConfig common_config = 1; + } + + // Configuration for filter behavior on the response direction. + message ResponseDirectionConfig { + CommonDirectionConfig common_config = 1; + + // If true, disables compression when the response contains an etag header. When it is false, the + // filter will preserve weak etags and remove the ones that require strong validation. + bool disable_on_etag_header = 2; + + // If true, removes accept-encoding from the request headers before dispatching it to the upstream + // so that responses do not get compressed before reaching the filter. + // + // .. attention:: + // + // To avoid interfering with other compression filters in the same chain use this option in + // the filter closest to the upstream. + bool remove_accept_encoding_header = 3; + } + // Minimum response length, in bytes, which will trigger compression. The default value is 30. - google.protobuf.UInt32Value content_length = 1; + google.protobuf.UInt32Value content_length = 1 [deprecated = true]; // Set of strings that allows specifying which mime-types yield compression; e.g., // application/json, text/html, etc. When this field is not defined, compression will be applied // to the following mime-types: "application/javascript", "application/json", // "application/xhtml+xml", "image/svg+xml", "text/css", "text/html", "text/plain", "text/xml" // and their synonyms. - repeated string content_type = 2; + repeated string content_type = 2 [deprecated = true]; // If true, disables compression when the response contains an etag header. When it is false, the // filter will preserve weak etags and remove the ones that require strong validation. - bool disable_on_etag_header = 3; + bool disable_on_etag_header = 3 [deprecated = true]; // If true, removes accept-encoding from the request headers before dispatching it to the upstream // so that responses do not get compressed before reaching the filter. - // .. attention: + // + // .. attention:: // // To avoid interfering with other compression filters in the same chain use this option in // the filter closest to the upstream. - bool remove_accept_encoding_header = 4; + bool remove_accept_encoding_header = 4 [deprecated = true]; // Runtime flag that controls whether the filter is enabled or not. If set to false, the // filter will operate as a pass-through filter. If not specified, defaults to enabled. - config.core.v3.RuntimeFeatureFlag runtime_enabled = 5; + config.core.v3.RuntimeFeatureFlag runtime_enabled = 5 [deprecated = true]; // A compressor library to use for compression. Currently only // :ref:`envoy.compression.gzip.compressor` // is included in Envoy. // This field is ignored if used in the context of the gzip http-filter, but is mandatory otherwise. config.core.v3.TypedExtensionConfig compressor_library = 6; + + // Configuration for request compression. Compression is disabled by default if left empty. + RequestDirectionConfig request_direction_config = 7; + + // Configuration for response compression. Compression is enabled by default if left empty. + // + // .. attention:: + // + // If the field is not empty then the duplicate deprecated fields of the `Compressor` message, + // such as `content_length`, `content_type`, `disable_on_etag_header`, + // `remove_accept_encoding_header` and `runtime_enabled`, are ignored. + // + // Also all the statistics related to response compression will be rooted in + // `.compressor...response.*` + // instead of + // `.compressor...*`. + ResponseDirectionConfig response_direction_config = 8; } diff --git a/generated_api_shadow/envoy/extensions/filters/http/compressor/v4alpha/BUILD b/generated_api_shadow/envoy/extensions/filters/http/compressor/v4alpha/BUILD new file mode 100644 index 000000000000..251b6da666af --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/http/compressor/v4alpha/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/core/v4alpha:pkg", + "//envoy/extensions/filters/http/compressor/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/filters/http/compressor/v4alpha/compressor.proto b/generated_api_shadow/envoy/extensions/filters/http/compressor/v4alpha/compressor.proto new file mode 100644 index 000000000000..38e6e5676e18 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/http/compressor/v4alpha/compressor.proto @@ -0,0 +1,129 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.compressor.v4alpha; + +import "envoy/config/core/v4alpha/base.proto"; +import "envoy/config/core/v4alpha/extension.proto"; + +import "google/protobuf/any.proto"; +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.compressor.v4alpha"; +option java_outer_classname = "CompressorProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Compressor] +// Compressor :ref:`configuration overview `. +// [#extension: envoy.filters.http.compressor] + +// [#next-free-field: 9] +message Compressor { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.compressor.v3.Compressor"; + + message CommonDirectionConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.compressor.v3.Compressor.CommonDirectionConfig"; + + // Runtime flag that controls whether compression is enabled or not for the direction this + // common config is put in. If set to false, the filter will operate as a pass-through filter + // in the chosen direction. If the field is omitted, the filter will be enabled. + config.core.v4alpha.RuntimeFeatureFlag enabled = 1; + + // Minimum value of Content-Length header of request or response messages (depending on the direction + // this common config is put in), in bytes, which will trigger compression. The default value is 30. + google.protobuf.UInt32Value min_content_length = 2; + + // Set of strings that allows specifying which mime-types yield compression; e.g., + // application/json, text/html, etc. When this field is not defined, compression will be applied + // to the following mime-types: "application/javascript", "application/json", + // "application/xhtml+xml", "image/svg+xml", "text/css", "text/html", "text/plain", "text/xml" + // and their synonyms. + repeated string content_type = 3; + } + + // Configuration for filter behavior on the request direction. + message RequestDirectionConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.compressor.v3.Compressor.RequestDirectionConfig"; + + CommonDirectionConfig common_config = 1; + } + + // Configuration for filter behavior on the response direction. + message ResponseDirectionConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.compressor.v3.Compressor.ResponseDirectionConfig"; + + CommonDirectionConfig common_config = 1; + + // If true, disables compression when the response contains an etag header. When it is false, the + // filter will preserve weak etags and remove the ones that require strong validation. + bool disable_on_etag_header = 2; + + // If true, removes accept-encoding from the request headers before dispatching it to the upstream + // so that responses do not get compressed before reaching the filter. + // + // .. attention:: + // + // To avoid interfering with other compression filters in the same chain use this option in + // the filter closest to the upstream. + bool remove_accept_encoding_header = 3; + } + + // Minimum response length, in bytes, which will trigger compression. The default value is 30. + google.protobuf.UInt32Value hidden_envoy_deprecated_content_length = 1 [deprecated = true]; + + // Set of strings that allows specifying which mime-types yield compression; e.g., + // application/json, text/html, etc. When this field is not defined, compression will be applied + // to the following mime-types: "application/javascript", "application/json", + // "application/xhtml+xml", "image/svg+xml", "text/css", "text/html", "text/plain", "text/xml" + // and their synonyms. + repeated string hidden_envoy_deprecated_content_type = 2 [deprecated = true]; + + // If true, disables compression when the response contains an etag header. When it is false, the + // filter will preserve weak etags and remove the ones that require strong validation. + bool hidden_envoy_deprecated_disable_on_etag_header = 3 [deprecated = true]; + + // If true, removes accept-encoding from the request headers before dispatching it to the upstream + // so that responses do not get compressed before reaching the filter. + // + // .. attention:: + // + // To avoid interfering with other compression filters in the same chain use this option in + // the filter closest to the upstream. + bool hidden_envoy_deprecated_remove_accept_encoding_header = 4 [deprecated = true]; + + // Runtime flag that controls whether the filter is enabled or not. If set to false, the + // filter will operate as a pass-through filter. If not specified, defaults to enabled. + config.core.v4alpha.RuntimeFeatureFlag hidden_envoy_deprecated_runtime_enabled = 5 + [deprecated = true]; + + // A compressor library to use for compression. Currently only + // :ref:`envoy.compression.gzip.compressor` + // is included in Envoy. + // This field is ignored if used in the context of the gzip http-filter, but is mandatory otherwise. + config.core.v4alpha.TypedExtensionConfig compressor_library = 6; + + // Configuration for request compression. Compression is disabled by default if left empty. + RequestDirectionConfig request_direction_config = 7; + + // Configuration for response compression. Compression is enabled by default if left empty. + // + // .. attention:: + // + // If the field is not empty then the duplicate deprecated fields of the `Compressor` message, + // such as `content_length`, `content_type`, `disable_on_etag_header`, + // `remove_accept_encoding_header` and `runtime_enabled`, are ignored. + // + // Also all the statistics related to response compression will be rooted in + // `.compressor...response.*` + // instead of + // `.compressor...*`. + ResponseDirectionConfig response_direction_config = 8; +} diff --git a/generated_api_shadow/envoy/extensions/filters/http/gzip/v4alpha/BUILD b/generated_api_shadow/envoy/extensions/filters/http/gzip/v4alpha/BUILD new file mode 100644 index 000000000000..3b9648df0929 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/http/gzip/v4alpha/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/extensions/filters/http/compressor/v4alpha:pkg", + "//envoy/extensions/filters/http/gzip/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/filters/http/gzip/v4alpha/gzip.proto b/generated_api_shadow/envoy/extensions/filters/http/gzip/v4alpha/gzip.proto new file mode 100644 index 000000000000..f89be9a69742 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/http/gzip/v4alpha/gzip.proto @@ -0,0 +1,84 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.gzip.v4alpha; + +import "envoy/extensions/filters/http/compressor/v4alpha/compressor.proto"; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.gzip.v4alpha"; +option java_outer_classname = "GzipProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Gzip] +// Gzip :ref:`configuration overview `. +// [#extension: envoy.filters.http.gzip] + +// [#next-free-field: 12] +message Gzip { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.gzip.v3.Gzip"; + + enum CompressionStrategy { + DEFAULT = 0; + FILTERED = 1; + HUFFMAN = 2; + RLE = 3; + } + + message CompressionLevel { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.filters.http.gzip.v3.Gzip.CompressionLevel"; + + enum Enum { + DEFAULT = 0; + BEST = 1; + SPEED = 2; + } + } + + reserved 2, 6, 7, 8; + + reserved "content_length", "content_type", "disable_on_etag_header", + "remove_accept_encoding_header"; + + // Value from 1 to 9 that controls the amount of internal memory used by zlib. Higher values + // use more memory, but are faster and produce better compression results. The default value is 5. + google.protobuf.UInt32Value memory_level = 1 [(validate.rules).uint32 = {lte: 9 gte: 1}]; + + // A value used for selecting the zlib compression level. This setting will affect speed and + // amount of compression applied to the content. "BEST" provides higher compression at the cost of + // higher latency, "SPEED" provides lower compression with minimum impact on response time. + // "DEFAULT" provides an optimal result between speed and compression. This field will be set to + // "DEFAULT" if not specified. + CompressionLevel.Enum compression_level = 3 [(validate.rules).enum = {defined_only: true}]; + + // A value used for selecting the zlib compression strategy which is directly related to the + // characteristics of the content. Most of the time "DEFAULT" will be the best choice, though + // there are situations which changing this parameter might produce better results. For example, + // run-length encoding (RLE) is typically used when the content is known for having sequences + // which same data occurs many consecutive times. For more information about each strategy, please + // refer to zlib manual. + CompressionStrategy compression_strategy = 4 [(validate.rules).enum = {defined_only: true}]; + + // Value from 9 to 15 that represents the base two logarithmic of the compressor's window size. + // Larger window results in better compression at the expense of memory usage. The default is 12 + // which will produce a 4096 bytes window. For more details about this parameter, please refer to + // zlib manual > deflateInit2. + google.protobuf.UInt32Value window_bits = 9 [(validate.rules).uint32 = {lte: 15 gte: 9}]; + + // Set of configuration parameters common for all compression filters. If this field is set then + // the fields `content_length`, `content_type`, `disable_on_etag_header` and + // `remove_accept_encoding_header` are ignored. + compressor.v4alpha.Compressor compressor = 10; + + // Value for Zlib's next output buffer. If not set, defaults to 4096. + // See https://www.zlib.net/manual.html for more details. Also see + // https://github.com/envoyproxy/envoy/issues/8448 for context on this filter's performance. + google.protobuf.UInt32Value chunk_size = 11 [(validate.rules).uint32 = {lte: 65536 gte: 4096}]; +} diff --git a/source/common/http/headers.h b/source/common/http/headers.h index 7d89713a3f76..9d1774fd17b5 100644 --- a/source/common/http/headers.h +++ b/source/common/http/headers.h @@ -306,6 +306,8 @@ class HeaderValues { } SchemeValues; struct { + const std::string Brotli{"br"}; + const std::string Compress{"compress"}; const std::string Chunked{"chunked"}; const std::string Deflate{"deflate"}; const std::string Gzip{"gzip"}; diff --git a/source/extensions/filters/http/common/compressor/compressor.cc b/source/extensions/filters/http/common/compressor/compressor.cc index e8e604295198..c14c42ed6eca 100644 --- a/source/extensions/filters/http/common/compressor/compressor.cc +++ b/source/extensions/filters/http/common/compressor/compressor.cc @@ -15,13 +15,16 @@ Http::RegisterCustomInlineHeader cache_control_handle(Http::CustomHeaders::get().CacheControl); -Http::RegisterCustomInlineHeader - content_encoding_handle(Http::CustomHeaders::get().ContentEncoding); Http::RegisterCustomInlineHeader etag_handle(Http::CustomHeaders::get().Etag); Http::RegisterCustomInlineHeader vary_handle(Http::CustomHeaders::get().Vary); +Http::RegisterCustomInlineHeader + request_content_encoding_handle(Http::CustomHeaders::get().ContentEncoding); +Http::RegisterCustomInlineHeader + response_content_encoding_handle(Http::CustomHeaders::get().ContentEncoding); + // Default minimum length of an upstream response that allows compression. const uint64_t DefaultMinimumContentLength = 30; @@ -43,35 +46,103 @@ struct CompressorRegistry : public StreamInfo::FilterState::Object { // Key to per stream CompressorRegistry objects. const std::string& compressorRegistryKey() { CONSTRUCT_ON_FIRST_USE(std::string, "compressors"); } +void compressAndUpdateStats(const Compression::Compressor::CompressorPtr& compressor, + const CompressorStats& stats, Buffer::Instance& data, bool end_stream) { + ASSERT(compressor != nullptr); + stats.total_uncompressed_bytes_.add(data.length()); + compressor->compress(data, end_stream ? Envoy::Compression::Compressor::State::Finish + : Envoy::Compression::Compressor::State::Flush); + stats.total_compressed_bytes_.add(data.length()); +} + } // namespace +CompressorFilterConfig::DirectionConfig::DirectionConfig( + const envoy::extensions::filters::http::compressor::v3::Compressor::CommonDirectionConfig& + proto_config, + const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) + : compression_enabled_(proto_config.enabled(), runtime), + min_content_length_{contentLengthUint(proto_config.min_content_length().value())}, + content_type_values_(contentTypeSet(proto_config.content_type())), stats_{generateStats( + stats_prefix, scope)} { +} + CompressorFilterConfig::CompressorFilterConfig( const envoy::extensions::filters::http::compressor::v3::Compressor& compressor, const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime, const std::string& content_encoding) - : content_length_(contentLengthUint(compressor.content_length().value())), - content_type_values_(contentTypeSet(compressor.content_type())), - disable_on_etag_header_(compressor.disable_on_etag_header()), - remove_accept_encoding_header_(compressor.remove_accept_encoding_header()), - stats_(generateStats(stats_prefix, scope)), enabled_(compressor.runtime_enabled(), runtime), + : request_direction_config_(compressor, stats_prefix, scope, runtime), + response_direction_config_(compressor, stats_prefix, scope, runtime), content_encoding_(content_encoding) {} -StringUtil::CaseUnorderedSet -CompressorFilterConfig::contentTypeSet(const Protobuf::RepeatedPtrField& types) { +StringUtil::CaseUnorderedSet CompressorFilterConfig::DirectionConfig::contentTypeSet( + const Protobuf::RepeatedPtrField& types) { const auto& default_content_encodings = defaultContentEncoding(); return types.empty() ? StringUtil::CaseUnorderedSet(default_content_encodings.begin(), default_content_encodings.end()) : StringUtil::CaseUnorderedSet(types.cbegin(), types.cend()); } -uint32_t CompressorFilterConfig::contentLengthUint(Protobuf::uint32 length) { +uint32_t CompressorFilterConfig::DirectionConfig::contentLengthUint(Protobuf::uint32 length) { return length > 0 ? length : DefaultMinimumContentLength; } +CompressorFilterConfig::RequestDirectionConfig::RequestDirectionConfig( + const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, + const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) + : DirectionConfig(proto_config.request_direction_config().common_config(), + stats_prefix + "request.", scope, runtime), + is_set_{proto_config.has_request_direction_config()} {} + +CompressorFilterConfig::ResponseDirectionConfig::ResponseDirectionConfig( + const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, + const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) + : DirectionConfig(commonConfig(proto_config), + proto_config.has_response_direction_config() ? stats_prefix + "response." + : stats_prefix, + scope, runtime), + disable_on_etag_header_( + proto_config.has_response_direction_config() + ? proto_config.response_direction_config().disable_on_etag_header() + : proto_config.disable_on_etag_header()), + remove_accept_encoding_header_( + proto_config.has_response_direction_config() + ? proto_config.response_direction_config().remove_accept_encoding_header() + : proto_config.remove_accept_encoding_header()), + response_stats_{generateResponseStats(stats_prefix, scope)} {} + +const envoy::extensions::filters::http::compressor::v3::Compressor::CommonDirectionConfig +CompressorFilterConfig::ResponseDirectionConfig::commonConfig( + const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config) { + if (proto_config.has_response_direction_config()) { + return proto_config.response_direction_config().common_config(); + } + envoy::extensions::filters::http::compressor::v3::Compressor::CommonDirectionConfig config = {}; + if (proto_config.has_content_length()) { + config.set_allocated_min_content_length( + // According to + // https://developers.google.com/protocol-buffers/docs/reference/cpp-generated#embeddedmessage + // the message Compressor takes ownership of the allocated Protobuf::Uint32Value object. + new Protobuf::UInt32Value(proto_config.content_length())); + } + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + for (const std::string& ctype : proto_config.content_type()) { + config.add_content_type(ctype); + } + config.set_allocated_enabled( + // According to + // https://developers.google.com/protocol-buffers/docs/reference/cpp-generated#embeddedmessage + // the message Compressor takes ownership of the allocated Protobuf::Uint32Value object. + new envoy::config::core::v3::RuntimeFeatureFlag(proto_config.runtime_enabled())); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + return config; +} + CompressorFilter::CompressorFilter(const CompressorFilterConfigSharedPtr config) - : skip_compression_{true}, config_(std::move(config)) {} + : config_(std::move(config)) {} -Http::FilterHeadersStatus CompressorFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool) { +Http::FilterHeadersStatus CompressorFilter::decodeHeaders(Http::RequestHeaderMap& headers, + bool end_stream) { const Http::HeaderEntry* accept_encoding = headers.getInline(accept_encoding_handle.handle()); if (accept_encoding != nullptr) { // Capture the value of the "Accept-Encoding" request header to use it later when making @@ -79,13 +150,49 @@ Http::FilterHeadersStatus CompressorFilter::decodeHeaders(Http::RequestHeaderMap accept_encoding_ = std::make_unique(accept_encoding->value().getStringView()); } - if (config_->enabled() && config_->removeAcceptEncodingHeader()) { + const auto& response_config = config_->responseDirectionConfig(); + if (response_config.compressionEnabled() && response_config.removeAcceptEncodingHeader()) { headers.removeInline(accept_encoding_handle.handle()); } + const auto& request_config = config_->requestDirectionConfig(); + if (!end_stream && request_config.compressionEnabled() && + request_config.isMinimumContentLength(headers) && + request_config.isContentTypeAllowed(headers) && + !headers.getInline(request_content_encoding_handle.handle()) && + isTransferEncodingAllowed(headers)) { + headers.removeContentLength(); + headers.setInline(request_content_encoding_handle.handle(), config_->contentEncoding()); + request_config.stats().compressed_.inc(); + request_compressor_ = config_->makeCompressor(); + } else { + request_config.stats().not_compressed_.inc(); + } + return Http::FilterHeadersStatus::Continue; } +Http::FilterDataStatus CompressorFilter::decodeData(Buffer::Instance& data, bool end_stream) { + if (request_compressor_ != nullptr) { + compressAndUpdateStats(request_compressor_, config_->requestDirectionConfig().stats(), data, + end_stream); + } + return Http::FilterDataStatus::Continue; +} + +Http::FilterTrailersStatus CompressorFilter::decodeTrailers(Http::RequestTrailerMap&) { + if (request_compressor_ != nullptr) { + Buffer::OwnedImpl empty_buffer; + // The presence of trailers means the stream is ended, but decodeData() + // is never called with end_stream=true, thus let the compression library know + // that the stream is ended. + compressAndUpdateStats(request_compressor_, config_->requestDirectionConfig().stats(), + empty_buffer, true); + decoder_callbacks_->addDecodedData(empty_buffer, true); + } + return Http::FilterTrailersStatus::Continue; +} + void CompressorFilter::setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) { decoder_callbacks_ = &callbacks; @@ -111,22 +218,23 @@ void CompressorFilter::setDecoderFilterCallbacks(Http::StreamDecoderFilterCallba Http::FilterHeadersStatus CompressorFilter::encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) { + const auto& config = config_->responseDirectionConfig(); const bool isEnabledAndContentLengthBigEnough = - config_->enabled() && isMinimumContentLength(headers); - const bool isCompressible = isEnabledAndContentLengthBigEnough && isContentTypeAllowed(headers) && + config.compressionEnabled() && config.isMinimumContentLength(headers); + const bool isCompressible = isEnabledAndContentLengthBigEnough && + config.isContentTypeAllowed(headers) && !hasCacheControlNoTransform(headers) && isEtagAllowed(headers) && - !headers.getInline(content_encoding_handle.handle()); + !headers.getInline(response_content_encoding_handle.handle()); if (!end_stream && isEnabledAndContentLengthBigEnough && isAcceptEncodingAllowed(headers) && isCompressible && isTransferEncodingAllowed(headers)) { - skip_compression_ = false; sanitizeEtagHeader(headers); headers.removeContentLength(); - headers.setInline(content_encoding_handle.handle(), config_->contentEncoding()); - config_->stats().compressed_.inc(); + headers.setInline(response_content_encoding_handle.handle(), config_->contentEncoding()); + config.stats().compressed_.inc(); // Finally instantiate the compressor. - compressor_ = config_->makeCompressor(); + response_compressor_ = config_->makeCompressor(); } else { - config_->stats().not_compressed_.inc(); + config.stats().not_compressed_.inc(); } // Even if we decided not to compress due to incompatible Accept-Encoding value, @@ -140,20 +248,21 @@ Http::FilterHeadersStatus CompressorFilter::encodeHeaders(Http::ResponseHeaderMa } Http::FilterDataStatus CompressorFilter::encodeData(Buffer::Instance& data, bool end_stream) { - if (!skip_compression_) { - config_->stats().total_uncompressed_bytes_.add(data.length()); - compressor_->compress(data, end_stream ? Envoy::Compression::Compressor::State::Finish - : Envoy::Compression::Compressor::State::Flush); - config_->stats().total_compressed_bytes_.add(data.length()); + if (response_compressor_ != nullptr) { + compressAndUpdateStats(response_compressor_, config_->responseDirectionConfig().stats(), data, + end_stream); } return Http::FilterDataStatus::Continue; } Http::FilterTrailersStatus CompressorFilter::encodeTrailers(Http::ResponseTrailerMap&) { - if (!skip_compression_) { + if (response_compressor_ != nullptr) { Buffer::OwnedImpl empty_buffer; - compressor_->compress(empty_buffer, Envoy::Compression::Compressor::State::Finish); - config_->stats().total_compressed_bytes_.add(empty_buffer.length()); + // The presence of trailers means the stream is ended, but encodeData() + // is never called with end_stream=true, thus let the compression library know + // that the stream is ended. + compressAndUpdateStats(response_compressor_, config_->responseDirectionConfig().stats(), + empty_buffer, true); encoder_callbacks_->addEncodedData(empty_buffer, true); } return Http::FilterTrailersStatus::Continue; @@ -204,9 +313,11 @@ CompressorFilter::chooseEncoding(const Http::ResponseHeaderMap& headers) const { // "gzip;q=1,deflate;q=.5". The corresponding response content type is "application/javascript". // If "gzip" is not excluded from the decision process then it will take precedence over // "deflate" and the resulting response won't be compressed at all. - if (!content_type_value.empty() && !filter_config->contentTypeValues().empty()) { - auto iter = filter_config->contentTypeValues().find(content_type_value); - if (iter == filter_config->contentTypeValues().end()) { + if (!content_type_value.empty() && + !filter_config->responseDirectionConfig().contentTypeValues().empty()) { + auto iter = + filter_config->responseDirectionConfig().contentTypeValues().find(content_type_value); + if (iter == filter_config->responseDirectionConfig().contentTypeValues().end()) { // Skip adding this filter to the list of allowed compressors. continue; } @@ -306,29 +417,30 @@ CompressorFilter::chooseEncoding(const Http::ResponseHeaderMap& headers) const { bool CompressorFilter::shouldCompress(const CompressorFilter::EncodingDecision& decision) const { const bool should_compress = absl::EqualsIgnoreCase(config_->contentEncoding(), decision.encoding()); + const ResponseCompressorStats& stats = config_->responseDirectionConfig().responseStats(); switch (decision.stat()) { case CompressorFilter::EncodingDecision::HeaderStat::ValidCompressor: if (should_compress) { - config_->stats().header_compressor_used_.inc(); + stats.header_compressor_used_.inc(); // TODO(rojkov): Remove this increment when the gzip-specific stat is gone. if (absl::EqualsIgnoreCase("gzip", config_->contentEncoding())) { - config_->stats().header_gzip_.inc(); + stats.header_gzip_.inc(); } } else { // Some other compressor filter in the same chain compressed the response body, // but not this filter. - config_->stats().header_compressor_overshadowed_.inc(); + stats.header_compressor_overshadowed_.inc(); } break; case CompressorFilter::EncodingDecision::HeaderStat::Identity: - config_->stats().header_identity_.inc(); + stats.header_identity_.inc(); break; case CompressorFilter::EncodingDecision::HeaderStat::Wildcard: - config_->stats().header_wildcard_.inc(); + stats.header_wildcard_.inc(); break; default: - config_->stats().header_not_valid_.inc(); + stats.header_not_valid_.inc(); break; } @@ -337,7 +449,7 @@ bool CompressorFilter::shouldCompress(const CompressorFilter::EncodingDecision& bool CompressorFilter::isAcceptEncodingAllowed(const Http::ResponseHeaderMap& headers) const { if (accept_encoding_ == nullptr) { - config_->stats().no_accept_header_.inc(); + config_->responseDirectionConfig().responseStats().no_accept_header_.inc(); return false; } @@ -360,35 +472,37 @@ bool CompressorFilter::isAcceptEncodingAllowed(const Http::ResponseHeaderMap& he return result; } -bool CompressorFilter::isContentTypeAllowed(Http::ResponseHeaderMap& headers) const { +bool CompressorFilterConfig::DirectionConfig::isContentTypeAllowed( + const Http::RequestOrResponseHeaderMap& headers) const { const Http::HeaderEntry* content_type = headers.ContentType(); - if (content_type != nullptr && !config_->contentTypeValues().empty()) { + if (content_type != nullptr && !content_type_values_.empty()) { const absl::string_view value = StringUtil::trim(StringUtil::cropRight(content_type->value().getStringView(), ";")); - return config_->contentTypeValues().find(value) != config_->contentTypeValues().end(); + return content_type_values_.find(value) != content_type_values_.end(); } return true; } bool CompressorFilter::isEtagAllowed(Http::ResponseHeaderMap& headers) const { - const bool is_etag_allowed = - !(config_->disableOnEtagHeader() && headers.getInline(etag_handle.handle())); + const bool is_etag_allowed = !(config_->responseDirectionConfig().disableOnEtagHeader() && + headers.getInline(etag_handle.handle())); if (!is_etag_allowed) { - config_->stats().not_compressed_etag_.inc(); + config_->responseDirectionConfig().responseStats().not_compressed_etag_.inc(); } return is_etag_allowed; } -bool CompressorFilter::isMinimumContentLength(Http::ResponseHeaderMap& headers) const { +bool CompressorFilterConfig::DirectionConfig::isMinimumContentLength( + const Http::RequestOrResponseHeaderMap& headers) const { const Http::HeaderEntry* content_length = headers.ContentLength(); if (content_length != nullptr) { uint64_t length; const bool is_minimum_content_length = absl::SimpleAtoi(content_length->value().getStringView(), &length) && - length >= config_->minimumLength(); + length >= min_content_length_; if (!is_minimum_content_length) { - config_->stats().content_length_too_small_.inc(); + stats_.content_length_too_small_.inc(); } return is_minimum_content_length; } @@ -397,17 +511,24 @@ bool CompressorFilter::isMinimumContentLength(Http::ResponseHeaderMap& headers) Http::Headers::get().TransferEncodingValues.Chunked); } -bool CompressorFilter::isTransferEncodingAllowed(Http::ResponseHeaderMap& headers) const { +bool CompressorFilter::isTransferEncodingAllowed(Http::RequestOrResponseHeaderMap& headers) const { const Http::HeaderEntry* transfer_encoding = headers.TransferEncoding(); if (transfer_encoding != nullptr) { for (absl::string_view header_value : StringUtil::splitToken(transfer_encoding->value().getStringView(), ",", true)) { const auto trimmed_value = StringUtil::trim(header_value); - if (absl::EqualsIgnoreCase(trimmed_value, config_->contentEncoding()) || - // or any other compression type known to Envoy - absl::EqualsIgnoreCase(trimmed_value, Http::Headers::get().TransferEncodingValues.Gzip) || + // Check if the message is already compressed with any compression encoding + // known to Envoy. + if (absl::EqualsIgnoreCase(trimmed_value, Http::Headers::get().TransferEncodingValues.Gzip) || + absl::EqualsIgnoreCase(trimmed_value, + Http::Headers::get().TransferEncodingValues.Brotli) || + absl::EqualsIgnoreCase(trimmed_value, + Http::Headers::get().TransferEncodingValues.Deflate) || absl::EqualsIgnoreCase(trimmed_value, - Http::Headers::get().TransferEncodingValues.Deflate)) { + Http::Headers::get().TransferEncodingValues.Compress) || + // or with a custom non-standard compression provided by an external + // compression library. + absl::EqualsIgnoreCase(trimmed_value, config_->contentEncoding())) { return false; } } diff --git a/source/extensions/filters/http/common/compressor/compressor.h b/source/extensions/filters/http/common/compressor/compressor.h index 3b775c6b4b61..c18cc1aaccf9 100644 --- a/source/extensions/filters/http/common/compressor/compressor.h +++ b/source/extensions/filters/http/common/compressor/compressor.h @@ -18,12 +18,21 @@ namespace Common { namespace Compressors { /** - * All compressor filter stats. @see stats_macros.h - * "total_uncompressed_bytes" only includes bytes from requests that were marked for compression. - * If the request was not marked for compression, the filter increments "not_compressed", but does - * not add to "total_uncompressed_bytes". This way, the user can measure the memory performance of - * the compression. - * + * Compressor filter stats common for responses and requests. @see stats_macros.h + * "total_uncompressed_bytes" only includes bytes from requests or responses that were marked for + * compression. If the request (or response) was not marked for compression, the filter increments + * "not_compressed", but does not add to "total_uncompressed_bytes". This way, the user can + * measure the memory performance of the compression. + */ +#define COMMON_COMPRESSOR_STATS(COUNTER) \ + COUNTER(compressed) \ + COUNTER(not_compressed) \ + COUNTER(total_uncompressed_bytes) \ + COUNTER(total_compressed_bytes) \ + COUNTER(content_length_too_small) + +/** + * Compressor filter stats specific to responses only. @see stats_macros.h * "header_compressor_used" is a number of requests whose Accept-Encoding header explicitly stated * that the response body should be compressed with the encoding provided by this filter instance. * @@ -33,9 +42,7 @@ namespace Compressors { * "header_gzip" is specific to the gzip filter and is deprecated since it duplicates * "header_compressor_used". */ -#define ALL_COMPRESSOR_STATS(COUNTER) \ - COUNTER(compressed) \ - COUNTER(not_compressed) \ +#define RESPONSE_COMPRESSOR_STATS(COUNTER) \ COUNTER(no_accept_header) \ COUNTER(header_identity) \ COUNTER(header_gzip) \ @@ -43,34 +50,104 @@ namespace Compressors { COUNTER(header_compressor_overshadowed) \ COUNTER(header_wildcard) \ COUNTER(header_not_valid) \ - COUNTER(total_uncompressed_bytes) \ - COUNTER(total_compressed_bytes) \ - COUNTER(content_length_too_small) \ COUNTER(not_compressed_etag) /** - * Struct definition for compressor stats. @see stats_macros.h + * Struct definitions for compressor stats. @see stats_macros.h */ struct CompressorStats { - ALL_COMPRESSOR_STATS(GENERATE_COUNTER_STRUCT) + COMMON_COMPRESSOR_STATS(GENERATE_COUNTER_STRUCT) +}; +struct ResponseCompressorStats { + RESPONSE_COMPRESSOR_STATS(GENERATE_COUNTER_STRUCT) }; // TODO(rojkov): merge this class with Compressor::CompressorFilterConfig when the filter // `envoy.filters.http.gzip` is fully deprecated and dropped. class CompressorFilterConfig { public: + class DirectionConfig { + public: + DirectionConfig( + const envoy::extensions::filters::http::compressor::v3::Compressor::CommonDirectionConfig& + proto_config, + const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + + virtual ~DirectionConfig() = default; + + virtual bool compressionEnabled() const PURE; + + const CompressorStats& stats() const { return stats_; } + const StringUtil::CaseUnorderedSet& contentTypeValues() const { return content_type_values_; } + uint32_t minimumLength() const { return min_content_length_; } + bool isMinimumContentLength(const Http::RequestOrResponseHeaderMap& headers) const; + bool isContentTypeAllowed(const Http::RequestOrResponseHeaderMap& headers) const; + + protected: + const Runtime::FeatureFlag compression_enabled_; + + private: + static CompressorStats generateStats(const std::string& prefix, Stats::Scope& scope) { + return CompressorStats{COMMON_COMPRESSOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + } + + static uint32_t contentLengthUint(Protobuf::uint32 length); + + static StringUtil::CaseUnorderedSet + contentTypeSet(const Protobuf::RepeatedPtrField& types); + + const uint32_t min_content_length_; + const StringUtil::CaseUnorderedSet content_type_values_; + const CompressorStats stats_; + }; + + class RequestDirectionConfig : public DirectionConfig { + public: + RequestDirectionConfig( + const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, + const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + + bool compressionEnabled() const override { return is_set_ && compression_enabled_.enabled(); } + + private: + const bool is_set_; + }; + + class ResponseDirectionConfig : public DirectionConfig { + public: + ResponseDirectionConfig( + const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, + const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + + bool compressionEnabled() const override { return compression_enabled_.enabled(); } + const ResponseCompressorStats& responseStats() const { return response_stats_; } + bool disableOnEtagHeader() const { return disable_on_etag_header_; } + bool removeAcceptEncodingHeader() const { return remove_accept_encoding_header_; } + + private: + static ResponseCompressorStats generateResponseStats(const std::string& prefix, + Stats::Scope& scope) { + return ResponseCompressorStats{RESPONSE_COMPRESSOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + } + + // TODO(rojkov): delete this translation function once the deprecated fields + // are removed from envoy::extensions::filters::http::compressor::v3::Compressor. + static const envoy::extensions::filters::http::compressor::v3::Compressor::CommonDirectionConfig + commonConfig(const envoy::extensions::filters::http::compressor::v3::Compressor&); + + const bool disable_on_etag_header_; + const bool remove_accept_encoding_header_; + const ResponseCompressorStats response_stats_; + }; + CompressorFilterConfig() = delete; virtual ~CompressorFilterConfig() = default; virtual Envoy::Compression::Compressor::CompressorPtr makeCompressor() PURE; - bool enabled() const { return enabled_.enabled(); } - const CompressorStats& stats() { return stats_; } - const StringUtil::CaseUnorderedSet& contentTypeValues() const { return content_type_values_; } - bool disableOnEtagHeader() const { return disable_on_etag_header_; } - bool removeAcceptEncodingHeader() const { return remove_accept_encoding_header_; } - uint32_t minimumLength() const { return content_length_; } const std::string contentEncoding() const { return content_encoding_; }; + const RequestDirectionConfig& requestDirectionConfig() { return request_direction_config_; } + const ResponseDirectionConfig& responseDirectionConfig() { return response_direction_config_; } protected: CompressorFilterConfig( @@ -79,22 +156,9 @@ class CompressorFilterConfig { const std::string& content_encoding); private: - static StringUtil::CaseUnorderedSet - contentTypeSet(const Protobuf::RepeatedPtrField& types); - - static uint32_t contentLengthUint(Protobuf::uint32 length); - - static CompressorStats generateStats(const std::string& prefix, Stats::Scope& scope) { - return CompressorStats{ALL_COMPRESSOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; - } - - const uint32_t content_length_; - const StringUtil::CaseUnorderedSet content_type_values_; - const bool disable_on_etag_header_; - const bool remove_accept_encoding_header_; + const RequestDirectionConfig request_direction_config_; + const ResponseDirectionConfig response_direction_config_; - const CompressorStats stats_; - Runtime::FeatureFlag enabled_; const std::string content_encoding_; }; using CompressorFilterConfigSharedPtr = std::shared_ptr; @@ -109,7 +173,9 @@ class CompressorFilter : public Http::PassThroughFilter { // Http::StreamDecoderFilter Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) override; + Http::FilterDataStatus decodeData(Buffer::Instance& buffer, bool end_stream) override; void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override; + Http::FilterTrailersStatus decodeTrailers(Http::RequestTrailerMap&) override; // Http::StreamEncoderFilter Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, @@ -120,10 +186,8 @@ class CompressorFilter : public Http::PassThroughFilter { private: bool hasCacheControlNoTransform(Http::ResponseHeaderMap& headers) const; bool isAcceptEncodingAllowed(const Http::ResponseHeaderMap& headers) const; - bool isContentTypeAllowed(Http::ResponseHeaderMap& headers) const; bool isEtagAllowed(Http::ResponseHeaderMap& headers) const; - bool isMinimumContentLength(Http::ResponseHeaderMap& headers) const; - bool isTransferEncodingAllowed(Http::ResponseHeaderMap& headers) const; + bool isTransferEncodingAllowed(Http::RequestOrResponseHeaderMap& headers) const; void sanitizeEtagHeader(Http::ResponseHeaderMap& headers); void insertVaryHeader(Http::ResponseHeaderMap& headers); @@ -144,8 +208,8 @@ class CompressorFilter : public Http::PassThroughFilter { std::unique_ptr chooseEncoding(const Http::ResponseHeaderMap& headers) const; bool shouldCompress(const EncodingDecision& decision) const; - bool skip_compression_; - Envoy::Compression::Compressor::CompressorPtr compressor_; + Envoy::Compression::Compressor::CompressorPtr response_compressor_; + Envoy::Compression::Compressor::CompressorPtr request_compressor_; const CompressorFilterConfigSharedPtr config_; std::unique_ptr accept_encoding_; }; diff --git a/test/extensions/filters/http/common/compressor/compressor_filter_test.cc b/test/extensions/filters/http/common/compressor/compressor_filter_test.cc index 104b70899a42..c8b4b79093e7 100644 --- a/test/extensions/filters/http/common/compressor/compressor_filter_test.cc +++ b/test/extensions/filters/http/common/compressor/compressor_filter_test.cc @@ -81,17 +81,43 @@ class CompressorFilterTest : public testing::Test { EXPECT_EQ(data_.length(), stats_.counter("test.test.total_compressed_bytes").value()); } - void feedBuffer(uint64_t size) { + void populateBuffer(uint64_t size) { + data_.drain(data_.length()); TestUtility::feedBufferWithRandomCharacters(data_, size); - expected_str_ += data_.toString(); + expected_str_ = data_.toString(); } - void doRequest(Http::TestRequestHeaderMapImpl&& headers) { + void doRequestCompression(Http::TestRequestHeaderMapImpl&& headers, bool with_trailers) { + doRequest(headers, true, with_trailers); + } + + void doRequestNoCompression(Http::TestRequestHeaderMapImpl&& headers) { + doRequest(headers, false, false); + } + + void doRequest(Http::TestRequestHeaderMapImpl& headers, bool with_compression, + bool with_trailers) { + uint64_t buffer_content_size; + if (!absl::SimpleAtoi(headers.get_("content-length"), &buffer_content_size)) { + buffer_content_size = 5; + } + populateBuffer(buffer_content_size); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, false)); - Buffer::OwnedImpl data("hello"); - EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); - Http::TestRequestTrailerMapImpl trailers; - EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(trailers)); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); + if (with_compression) { + if (with_trailers) { + EXPECT_CALL(decoder_callbacks_, addDecodedData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance& data, bool) { data_.move(data); })); + Http::TestRequestTrailerMapImpl trailers; + EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(trailers)); + } + EXPECT_EQ(expected_str_.length(), + stats_.counter("test.test.request.total_uncompressed_bytes").value()); + EXPECT_EQ(data_.length(), stats_.counter("test.test.request.total_compressed_bytes").value()); + EXPECT_EQ(1, stats_.counter("test.test.request.compressed").value()); + } else { + EXPECT_EQ(1, stats_.counter("test.test.request.not_compressed").value()); + } } void doResponseCompression(Http::TestResponseHeaderMapImpl& headers, bool with_trailers) { @@ -99,7 +125,7 @@ class CompressorFilterTest : public testing::Test { } void doResponseNoCompression(Http::TestResponseHeaderMapImpl& headers) { - doResponse(headers, false, true); + doResponse(headers, false, false); } void doResponse(Http::TestResponseHeaderMapImpl& headers, bool with_compression, @@ -111,7 +137,7 @@ class CompressorFilterTest : public testing::Test { // In case of chunked stream just feed the buffer with 1000 bytes. buffer_content_size = 1000; } - feedBuffer(buffer_content_size); + populateBuffer(buffer_content_size); Http::TestResponseHeaderMapImpl continue_headers; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode100ContinueHeaders(continue_headers)); @@ -134,9 +160,8 @@ class CompressorFilterTest : public testing::Test { } else { EXPECT_EQ("", headers.get_("content-encoding")); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(data_, false)); - Http::TestResponseTrailerMapImpl trailers; - EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(trailers)); - EXPECT_EQ(1, stats_.counter("test.test.not_compressed").value()); + EXPECT_EQ(1, stats_.counter(fmt::format("test.test.{}not_compressed", response_stats_prefix_)) + .value()); } } @@ -144,6 +169,7 @@ class CompressorFilterTest : public testing::Test { std::unique_ptr filter_; Buffer::OwnedImpl data_; std::string expected_str_; + std::string response_stats_prefix_{}; Stats::TestUtil::TestStore stats_; NiceMock runtime_; NiceMock decoder_callbacks_; @@ -154,9 +180,13 @@ class CompressorFilterTest : public testing::Test { TEST_F(CompressorFilterTest, DecodeHeadersWithRuntimeDisabled) { setUpFilter(R"EOF( { - "runtime_enabled": { - "default_value": true, - "runtime_key": "foo_key" + "response_direction_config": { + "common_config": { + "enabled": { + "default_value": true, + "runtime_key": "foo_key" + } + } }, "compressor_library": { "name": "test", @@ -166,10 +196,11 @@ TEST_F(CompressorFilterTest, DecodeHeadersWithRuntimeDisabled) { } } )EOF"); + response_stats_prefix_ = "response."; EXPECT_CALL(runtime_.snapshot_, getBoolean("foo_key", true)) .Times(2) .WillRepeatedly(Return(false)); - doRequest({{":method", "get"}, {"accept-encoding", "deflate, test"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "deflate, test"}}); Http::TestResponseHeaderMapImpl headers{{":method", "get"}, {"content-length", "256"}}; doResponseNoCompression(headers); EXPECT_FALSE(headers.has("vary")); @@ -177,29 +208,66 @@ TEST_F(CompressorFilterTest, DecodeHeadersWithRuntimeDisabled) { // Default config values. TEST_F(CompressorFilterTest, DefaultConfigValues) { - EXPECT_EQ(30, config_->minimumLength()); - EXPECT_EQ(false, config_->disableOnEtagHeader()); - EXPECT_EQ(false, config_->removeAcceptEncodingHeader()); - EXPECT_EQ(18, config_->contentTypeValues().size()); + EXPECT_EQ(30, config_->responseDirectionConfig().minimumLength()); + EXPECT_EQ(30, config_->requestDirectionConfig().minimumLength()); + EXPECT_EQ(false, config_->responseDirectionConfig().disableOnEtagHeader()); + EXPECT_EQ(false, config_->responseDirectionConfig().removeAcceptEncodingHeader()); + EXPECT_EQ(18, config_->responseDirectionConfig().contentTypeValues().size()); + EXPECT_EQ(18, config_->requestDirectionConfig().contentTypeValues().size()); +} + +TEST_F(CompressorFilterTest, CompressRequest) { + setUpFilter(R"EOF( +{ + "request_direction_config": {}, + "compressor_library": { + "name": "test", + "typed_config": { + "@type": "type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip" + } + } +} +)EOF"); + doRequestCompression({{":method", "post"}, {"content-length", "256"}}, false); + Http::TestResponseHeaderMapImpl headers{{":method", "post"}, {"content-length", "256"}}; + doResponseNoCompression(headers); +} + +TEST_F(CompressorFilterTest, CompressRequestWithTrailers) { + setUpFilter(R"EOF( +{ + "request_direction_config": {}, + "compressor_library": { + "name": "test", + "typed_config": { + "@type": "type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip" + } + } +} +)EOF"); + config_->setExpectedCompressCalls(2); + doRequestCompression({{":method", "post"}, {"content-length", "256"}}, true); + Http::TestResponseHeaderMapImpl headers{{":method", "post"}, {"content-length", "256"}}; + doResponseNoCompression(headers); } // Acceptance Testing with default configuration. TEST_F(CompressorFilterTest, AcceptanceTestEncoding) { - doRequest({{":method", "get"}, {"accept-encoding", "deflate, test"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "deflate, test"}}); Http::TestResponseHeaderMapImpl headers{{":method", "get"}, {"content-length", "256"}}; doResponseCompression(headers, false); } TEST_F(CompressorFilterTest, AcceptanceTestEncodingWithTrailers) { - doRequest({{":method", "get"}, {"accept-encoding", "deflate, test"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "deflate, test"}}); Http::TestResponseHeaderMapImpl headers{{":method", "get"}, {"content-length", "256"}}; config_->setExpectedCompressCalls(2); doResponseCompression(headers, true); } TEST_F(CompressorFilterTest, NoAcceptEncodingHeader) { - doRequest({{":method", "get"}, {}}); + doRequestNoCompression({{":method", "get"}, {}}); Http::TestResponseHeaderMapImpl headers{{":method", "get"}, {"content-length", "256"}}; doResponseNoCompression(headers); EXPECT_EQ(1, stats_.counter("test.test.no_accept_header").value()); @@ -209,7 +277,7 @@ TEST_F(CompressorFilterTest, NoAcceptEncodingHeader) { TEST_F(CompressorFilterTest, CacheIdentityDecision) { // check if identity stat is increased twice (the second time via the cached path). config_->setExpectedCompressCalls(0); - doRequest({{":method", "get"}, {"accept-encoding", "identity"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "identity"}}); Http::TestResponseHeaderMapImpl headers{{":method", "get"}, {"content-length", "256"}}; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(headers, false)); EXPECT_EQ(1, stats_.counter("test.test.header_identity").value()); @@ -220,7 +288,7 @@ TEST_F(CompressorFilterTest, CacheIdentityDecision) { TEST_F(CompressorFilterTest, CacheHeaderNotValidDecision) { // check if not_valid stat is increased twice (the second time via the cached path). config_->setExpectedCompressCalls(0); - doRequest({{":method", "get"}, {"accept-encoding", "test;q=invalid"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test;q=invalid"}}); Http::TestResponseHeaderMapImpl headers{{":method", "get"}, {"content-length", "256"}}; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(headers, false)); EXPECT_EQ(1, stats_.counter("test.test.header_not_valid").value()); @@ -230,10 +298,10 @@ TEST_F(CompressorFilterTest, CacheHeaderNotValidDecision) { // Content-Encoding: upstream response is already encoded. TEST_F(CompressorFilterTest, ContentEncodingAlreadyEncoded) { - doRequest({{":method", "get"}, {"accept-encoding", "test"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test"}}); Http::TestResponseHeaderMapImpl response_headers{ {":method", "get"}, {"content-length", "256"}, {"content-encoding", "deflate, gzip"}}; - feedBuffer(256); + populateBuffer(256); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, false)); EXPECT_TRUE(response_headers.has("content-length")); EXPECT_FALSE(response_headers.has("transfer-encoding")); @@ -322,7 +390,7 @@ TEST_P(IsAcceptEncodingAllowedTest, Validate) { const int not_valid = std::get<4>(GetParam()); const int identity = std::get<5>(GetParam()); - doRequest({{":method", "get"}, {"accept-encoding", accept_encoding}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", accept_encoding}}); Http::TestResponseHeaderMapImpl headers{{":method", "get"}, {"content-length", "256"}}; doResponse(headers, is_compression_expected, false); EXPECT_EQ(compressor_used, stats_.counter("test.test.header_compressor_used").value()); @@ -385,7 +453,7 @@ TEST_P(IsContentTypeAllowedTest, Validate) { )EOF"); } - doRequest({{":method", "get"}, {"accept-encoding", "test, deflate"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test, deflate"}}); Http::TestResponseHeaderMapImpl headers{ {":method", "get"}, {"content-length", "256"}, {"content-type", content_type}}; doResponse(headers, should_compress, false); @@ -409,7 +477,7 @@ TEST_P(CompressWithEtagTest, CompressionIsEnabledOnEtag) { const std::string& header_value = std::get<1>(GetParam()); const bool is_weak_etag = std::get<2>(GetParam()); - doRequest({{":method", "get"}, {"accept-encoding", "test, deflate"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test, deflate"}}); Http::TestResponseHeaderMapImpl headers{ {":method", "get"}, {"content-length", "256"}, {header_name, header_value}}; doResponseCompression(headers, false); @@ -438,7 +506,7 @@ TEST_P(CompressWithEtagTest, CompressionIsDisabledOnEtag) { } )EOF"); - doRequest({{":method", "get"}, {"accept-encoding", "test, deflate"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test, deflate"}}); Http::TestResponseHeaderMapImpl headers{ {":method", "get"}, {"content-length", "256"}, {header_name, header_value}}; if (StringUtil::CaseInsensitiveCompare()("etag", header_name)) { @@ -468,7 +536,7 @@ TEST_P(HasCacheControlNoTransformTest, Validate) { const std::string& cache_control = std::get<0>(GetParam()); const bool is_compression_expected = std::get<1>(GetParam()); - doRequest({{":method", "get"}, {"accept-encoding", "test, deflate"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test, deflate"}}); Http::TestResponseHeaderMapImpl headers{ {":method", "get"}, {"content-length", "256"}, {"cache-control", cache_control}}; doResponse(headers, is_compression_expected, false); @@ -510,7 +578,7 @@ TEST_P(IsMinimumContentLengthTest, Validate) { )EOF", content_length_config)); - doRequest({{":method", "get"}, {"accept-encoding", "test, deflate"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test, deflate"}}); Http::TestResponseHeaderMapImpl headers{{":method", "get"}, {header_name, header_value}}; doResponse(headers, is_compression_expected, false); EXPECT_EQ(is_compression_expected, headers.has("vary")); @@ -537,7 +605,7 @@ TEST_P(IsTransferEncodingAllowedTest, Validate) { const std::string& header_value = std::get<1>(GetParam()); const bool is_compression_expected = std::get<2>(GetParam()); - doRequest({{":method", "get"}, {"accept-encoding", "test"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test"}}); Http::TestResponseHeaderMapImpl headers{ {":method", "get"}, {"content-length", "256"}, {header_name, header_value}}; doResponse(headers, is_compression_expected, false); @@ -563,7 +631,7 @@ TEST_P(InsertVaryHeaderTest, Validate) { const std::string& header_value = std::get<1>(GetParam()); const std::string& expected = std::get<2>(GetParam()); - doRequest({{":method", "get"}, {"accept-encoding", "test"}}); + doRequestNoCompression({{":method", "get"}, {"accept-encoding", "test"}}); Http::TestResponseHeaderMapImpl headers{ {":method", "get"}, {"content-length", "256"}, {header_name, header_value}}; doResponseCompression(headers, false); diff --git a/test/extensions/filters/http/compressor/compressor_filter_integration_test.cc b/test/extensions/filters/http/compressor/compressor_filter_integration_test.cc index 623512513303..aaaf2908facf 100644 --- a/test/extensions/filters/http/compressor/compressor_filter_integration_test.cc +++ b/test/extensions/filters/http/compressor/compressor_filter_integration_test.cc @@ -25,6 +25,38 @@ class CompressorIntegrationTest : public testing::TestWithParamcomplete()); + EXPECT_EQ(Http::CustomHeaders::get().ContentEncodingValues.Gzip, + upstream_request_->headers() + .get(Http::CustomHeaders::get().ContentEncoding)[0] + ->value() + .getStringView()); + EXPECT_EQ(Http::Headers::get().TransferEncodingValues.Chunked, + upstream_request_->headers().getTransferEncodingValue()); + EXPECT_GT(upstream_request_->bodyLength(), 0U); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + ASSERT_TRUE(response->headers().get(Http::CustomHeaders::get().ContentEncoding).empty()); + ASSERT_EQ(response_content_length, response->body().size()); + EXPECT_EQ(response->body(), std::string(response_content_length, 'a')); + + Buffer::OwnedImpl decompressed_request{}; + const Buffer::OwnedImpl compressed_request{upstream_request_->body()}; + decompressor_.decompress(compressed_request, decompressed_request); + ASSERT_EQ(request_content_length, decompressed_request.length()); + EXPECT_TRUE(TestUtility::buffersEqual(expected_request, decompressed_request)); + } + void doRequestAndCompression(Http::TestRequestHeaderMapImpl&& request_headers, Http::TestResponseHeaderMapImpl&& response_headers) { uint64_t content_length; @@ -70,11 +102,26 @@ class CompressorIntegrationTest : public testing::TestWithParamheaders().get(Http::CustomHeaders::get().Vary)[0]->value().getStringView()); } + +/** + * Exercises gzip request compression with full configuration. + */ +TEST_P(CompressorIntegrationTest, CompressedRequestAcceptanceFullConfigTest) { + initializeFilter(full_config); + doCompressedRequest(Http::TestRequestHeaderMapImpl{{":method", "PUT"}, + {":path", "/test/long/url"}, + {":scheme", "http"}, + {":authority", "host"}, + {"content-length", "256"}}, + Http::TestResponseHeaderMapImpl{{":status", "200"}, + {"content-length", "10"}, + {"content-type", "application/json"}}); +} + } // namespace Envoy diff --git a/test/extensions/filters/http/gzip/gzip_filter_test.cc b/test/extensions/filters/http/gzip/gzip_filter_test.cc index 34fe7c3610d6..980c3b1beab3 100644 --- a/test/extensions/filters/http/gzip/gzip_filter_test.cc +++ b/test/extensions/filters/http/gzip/gzip_filter_test.cc @@ -134,11 +134,11 @@ class GzipFilterTest : public testing::Test { EXPECT_EQ(strategy, config_->compressionStrategy()); EXPECT_EQ(level, config_->compressionLevel()); EXPECT_EQ(5, config_->memoryLevel()); - EXPECT_EQ(30, config_->minimumLength()); + EXPECT_EQ(30, config_->responseDirectionConfig().minimumLength()); EXPECT_EQ(28, config_->windowBits()); - EXPECT_EQ(false, config_->disableOnEtagHeader()); - EXPECT_EQ(false, config_->removeAcceptEncodingHeader()); - EXPECT_EQ(18, config_->contentTypeValues().size()); + EXPECT_EQ(false, config_->responseDirectionConfig().disableOnEtagHeader()); + EXPECT_EQ(false, config_->responseDirectionConfig().removeAcceptEncodingHeader()); + EXPECT_EQ(18, config_->responseDirectionConfig().contentTypeValues().size()); } void doResponseNoCompression(Http::TestResponseHeaderMapImpl&& headers) { @@ -193,15 +193,15 @@ TEST_F(GzipFilterTest, RuntimeDisabled) { // Default config values. TEST_F(GzipFilterTest, DefaultConfigValues) { EXPECT_EQ(5, config_->memoryLevel()); - EXPECT_EQ(30, config_->minimumLength()); + EXPECT_EQ(30, config_->responseDirectionConfig().minimumLength()); EXPECT_EQ(28, config_->windowBits()); - EXPECT_EQ(false, config_->disableOnEtagHeader()); - EXPECT_EQ(false, config_->removeAcceptEncodingHeader()); + EXPECT_EQ(false, config_->responseDirectionConfig().disableOnEtagHeader()); + EXPECT_EQ(false, config_->responseDirectionConfig().removeAcceptEncodingHeader()); EXPECT_EQ(Compression::Gzip::Compressor::ZlibCompressorImpl::CompressionStrategy::Standard, config_->compressionStrategy()); EXPECT_EQ(Compression::Gzip::Compressor::ZlibCompressorImpl::CompressionLevel::Standard, config_->compressionLevel()); - EXPECT_EQ(18, config_->contentTypeValues().size()); + EXPECT_EQ(18, config_->responseDirectionConfig().contentTypeValues().size()); } TEST_F(GzipFilterTest, AvailableCombinationCompressionStrategyAndLevelConfig) { From 6246920219ac0ba215bfd0b5462ef78036363b75 Mon Sep 17 00:00:00 2001 From: Alex Konradi Date: Fri, 11 Dec 2020 13:55:26 -0500 Subject: [PATCH 11/49] overload: scale http stream idle timeout (#14155) The connection timeout applies to the idle timeout in the common HTTP protocol options message in the HttpConnectionManager, not to the RouteAction idle timeout. Signed-off-by: Alex Konradi --- api/envoy/config/core/v3/protocol.proto | 4 ++ api/envoy/config/core/v4alpha/protocol.proto | 4 ++ api/envoy/config/overload/v3/overload.proto | 9 +++- .../config/route/v3/route_components.proto | 2 +- .../route/v4alpha/route_components.proto | 2 +- .../v3/http_connection_manager.proto | 4 ++ .../v4alpha/http_connection_manager.proto | 4 ++ .../overload_manager/overload_manager.rst | 4 +- docs/root/version_history/current.rst | 2 +- .../envoy/config/core/v3/protocol.proto | 4 ++ .../envoy/config/core/v4alpha/protocol.proto | 4 ++ .../envoy/config/overload/v3/overload.proto | 9 +++- .../config/route/v3/route_components.proto | 2 +- .../route/v4alpha/route_components.proto | 2 +- .../v3/http_connection_manager.proto | 4 ++ .../v4alpha/http_connection_manager.proto | 4 ++ .../overload/thread_local_overload_state.h | 3 ++ source/common/http/conn_manager_impl.cc | 9 ++-- source/server/overload_manager_impl.cc | 2 + test/integration/overload_integration_test.cc | 42 +++++++++++++++++++ test/server/overload_manager_impl_test.cc | 2 + 21 files changed, 109 insertions(+), 13 deletions(-) diff --git a/api/envoy/config/core/v3/protocol.proto b/api/envoy/config/core/v3/protocol.proto index 5acdd010e3ab..c294370366df 100644 --- a/api/envoy/config/core/v3/protocol.proto +++ b/api/envoy/config/core/v3/protocol.proto @@ -77,6 +77,10 @@ message HttpProtocolOptions { // .. warning:: // Disabling this timeout has a highly likelihood of yielding connection leaks due to lost TCP // FIN packets, etc. + // + // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" + // is configured, this timeout is scaled for downstream connections according to the value for + // :ref:`HTTP_DOWNSTREAM_CONNECTION_IDLE `. google.protobuf.Duration idle_timeout = 1; // The maximum duration of a connection. The duration is defined as a period since a connection diff --git a/api/envoy/config/core/v4alpha/protocol.proto b/api/envoy/config/core/v4alpha/protocol.proto index bfc8511b5b4a..e33d83442afc 100644 --- a/api/envoy/config/core/v4alpha/protocol.proto +++ b/api/envoy/config/core/v4alpha/protocol.proto @@ -77,6 +77,10 @@ message HttpProtocolOptions { // .. warning:: // Disabling this timeout has a highly likelihood of yielding connection leaks due to lost TCP // FIN packets, etc. + // + // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" + // is configured, this timeout is scaled for downstream connections according to the value for + // :ref:`HTTP_DOWNSTREAM_CONNECTION_IDLE `. google.protobuf.Duration idle_timeout = 1; // The maximum duration of a connection. The duration is defined as a period since a connection diff --git a/api/envoy/config/overload/v3/overload.proto b/api/envoy/config/overload/v3/overload.proto index ebea9ff36933..55a66500c94e 100644 --- a/api/envoy/config/overload/v3/overload.proto +++ b/api/envoy/config/overload/v3/overload.proto @@ -91,8 +91,15 @@ message ScaleTimersOverloadActionConfig { UNSPECIFIED = 0; // Adjusts the idle timer for downstream HTTP connections that takes effect when there are no active streams. - // This affects the value of :ref:`RouteAction.idle_timeout `. + // This affects the value of :ref:`HttpConnectionManager.common_http_protocol_options.idle_timeout + // ` HTTP_DOWNSTREAM_CONNECTION_IDLE = 1; + + // Adjusts the idle timer for HTTP streams initiated by downstream clients. + // This affects the value of :ref:`RouteAction.idle_timeout ` and + // :ref:`HttpConnectionManager.stream_idle_timeout + // ` + HTTP_DOWNSTREAM_STREAM_IDLE = 2; } message ScaleTimer { diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 6915c62922fa..c1bc1ace9130 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -980,7 +980,7 @@ message RouteAction { // // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" // is configured, this timeout is scaled according to the value for - // :ref:`HTTP_DOWNSTREAM_CONNECTION_IDLE `. + // :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `. google.protobuf.Duration idle_timeout = 24; // Indicates that the route has a retry policy. Note that if this is set, diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 3d6dbfd41130..4083513009d7 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -980,7 +980,7 @@ message RouteAction { // // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" // is configured, this timeout is scaled according to the value for - // :ref:`HTTP_DOWNSTREAM_CONNECTION_IDLE `. + // :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `. google.protobuf.Duration idle_timeout = 24; // Indicates that the route has a retry policy. Note that if this is set, diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto index f83cc368ea0c..5878beffa5f9 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto +++ b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto @@ -340,6 +340,10 @@ message HttpConnectionManager { // ` does not apply to // this corner case. // + // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" + // is configured, this timeout is scaled according to the value for + // :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `. + // // Note that it is possible to idle timeout even if the wire traffic for a stream is non-idle, due // to the granularity of events presented to the connection manager. For example, while receiving // very large request headers, it may be the case that there is traffic regularly arriving on the diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto b/api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto index 013b676b8a63..51fdb6bf3dbf 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto +++ b/api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto @@ -339,6 +339,10 @@ message HttpConnectionManager { // ` does not apply to // this corner case. // + // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" + // is configured, this timeout is scaled according to the value for + // :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `. + // // Note that it is possible to idle timeout even if the wire traffic for a stream is non-idle, due // to the granularity of events presented to the connection manager. For example, while receiving // very large request headers, it may be the case that there is traffic regularly arriving on the diff --git a/docs/root/configuration/operations/overload_manager/overload_manager.rst b/docs/root/configuration/operations/overload_manager/overload_manager.rst index fbc921e55fa8..089a56276012 100644 --- a/docs/root/configuration/operations/overload_manager/overload_manager.rst +++ b/docs/root/configuration/operations/overload_manager/overload_manager.rst @@ -127,7 +127,7 @@ As an example, here is a single overload action entry that enables timeout reduc It configures the overload manager to change the amount of time that HTTP connections are allowed to remain idle before being closed in response to heap size. When the heap usage is less than 85%, idle connections will time out at their usual time, which is configured through -:ref:`RouteAction.idle_timeout `. +:ref:`HttpConnectionManager.common_http_protocol_options.idle_timeout `. When the heap usage is at or above 95%, idle connections will be closed after the specified `min_timeout`, here 2 seconds. If the heap usage is between 85% and 95%, the idle connection timeout will vary between those two based on the formula for the :ref:`scaled trigger ` @@ -136,7 +136,7 @@ out after :math:`2s + (600s - 2s) \cdot (95\% - 92\%) / (95\% - 85\%) = 181.4s`. Note in the example that the minimum idle time is specified as an absolute duration. If, instead, `min_timeout: 2s` were to be replaced with `min_scale: { value: 10 }`, the minimum timer value -would be computed based on the maximum (specified elsewhere). So if `RouteAction.idle_timeout` is +would be computed based on the maximum (specified elsewhere). So if `idle_timeout` is again 600 seconds, then the minimum timer value would be :math:`10\% \cdot 600s = 60s`. Limiting Active Connections diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 18e582ac71e5..c7fbbbd56b06 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -72,7 +72,7 @@ New Features * lua: added `downstreamDirectRemoteAddress()` and `downstreamLocalAddress()` APIs to :ref:`streamInfo() `. * mongo_proxy: the list of commands to produce metrics for is now :ref:`configurable `. * network: added a :ref:`timeout ` for incoming connections completing transport-level negotiation, including TLS and ALTS hanshakes. -* overload: add :ref:`envoy.overload_actions.reduce_timeouts ` overload action to enable scaling timeouts down with load. +* overload: add :ref:`envoy.overload_actions.reduce_timeouts ` overload action to enable scaling timeouts down with load. Scaling support :ref:`is limited ` to the HTTP connection and stream idle timeouts. * ratelimit: added support for use of various :ref:`metadata ` as a ratelimit action. * ratelimit: added :ref:`disable_x_envoy_ratelimited_header ` option to disable `X-Envoy-RateLimited` header. * sds: improved support for atomic :ref:`key rotations ` and added configurable rotation triggers for diff --git a/generated_api_shadow/envoy/config/core/v3/protocol.proto b/generated_api_shadow/envoy/config/core/v3/protocol.proto index 5acdd010e3ab..c294370366df 100644 --- a/generated_api_shadow/envoy/config/core/v3/protocol.proto +++ b/generated_api_shadow/envoy/config/core/v3/protocol.proto @@ -77,6 +77,10 @@ message HttpProtocolOptions { // .. warning:: // Disabling this timeout has a highly likelihood of yielding connection leaks due to lost TCP // FIN packets, etc. + // + // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" + // is configured, this timeout is scaled for downstream connections according to the value for + // :ref:`HTTP_DOWNSTREAM_CONNECTION_IDLE `. google.protobuf.Duration idle_timeout = 1; // The maximum duration of a connection. The duration is defined as a period since a connection diff --git a/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto b/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto index dca2a0598fc6..2063d6d6793e 100644 --- a/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto +++ b/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto @@ -77,6 +77,10 @@ message HttpProtocolOptions { // .. warning:: // Disabling this timeout has a highly likelihood of yielding connection leaks due to lost TCP // FIN packets, etc. + // + // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" + // is configured, this timeout is scaled for downstream connections according to the value for + // :ref:`HTTP_DOWNSTREAM_CONNECTION_IDLE `. google.protobuf.Duration idle_timeout = 1; // The maximum duration of a connection. The duration is defined as a period since a connection diff --git a/generated_api_shadow/envoy/config/overload/v3/overload.proto b/generated_api_shadow/envoy/config/overload/v3/overload.proto index 1afd7a3f7500..21dd02811a34 100644 --- a/generated_api_shadow/envoy/config/overload/v3/overload.proto +++ b/generated_api_shadow/envoy/config/overload/v3/overload.proto @@ -89,8 +89,15 @@ message ScaleTimersOverloadActionConfig { UNSPECIFIED = 0; // Adjusts the idle timer for downstream HTTP connections that takes effect when there are no active streams. - // This affects the value of :ref:`RouteAction.idle_timeout `. + // This affects the value of :ref:`HttpConnectionManager.common_http_protocol_options.idle_timeout + // ` HTTP_DOWNSTREAM_CONNECTION_IDLE = 1; + + // Adjusts the idle timer for HTTP streams initiated by downstream clients. + // This affects the value of :ref:`RouteAction.idle_timeout ` and + // :ref:`HttpConnectionManager.stream_idle_timeout + // ` + HTTP_DOWNSTREAM_STREAM_IDLE = 2; } message ScaleTimer { 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 7813038930a3..89c0a1c76cf7 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -989,7 +989,7 @@ message RouteAction { // // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" // is configured, this timeout is scaled according to the value for - // :ref:`HTTP_DOWNSTREAM_CONNECTION_IDLE `. + // :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `. google.protobuf.Duration idle_timeout = 24; // Indicates that the route has a retry policy. Note that if this is set, diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index 0860c9d56700..b67c4efa3952 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -988,7 +988,7 @@ message RouteAction { // // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" // is configured, this timeout is scaled according to the value for - // :ref:`HTTP_DOWNSTREAM_CONNECTION_IDLE `. + // :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `. google.protobuf.Duration idle_timeout = 24; // Indicates that the route has a retry policy. Note that if this is set, diff --git a/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto index fcec5a4e235b..1cdb60df3ccb 100644 --- a/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto +++ b/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto @@ -342,6 +342,10 @@ message HttpConnectionManager { // ` does not apply to // this corner case. // + // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" + // is configured, this timeout is scaled according to the value for + // :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `. + // // Note that it is possible to idle timeout even if the wire traffic for a stream is non-idle, due // to the granularity of events presented to the connection manager. For example, while receiving // very large request headers, it may be the case that there is traffic regularly arriving on the diff --git a/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto b/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto index 013b676b8a63..51fdb6bf3dbf 100644 --- a/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto +++ b/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto @@ -339,6 +339,10 @@ message HttpConnectionManager { // ` does not apply to // this corner case. // + // If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts" + // is configured, this timeout is scaled according to the value for + // :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `. + // // Note that it is possible to idle timeout even if the wire traffic for a stream is non-idle, due // to the granularity of events presented to the connection manager. For example, while receiving // very large request headers, it may be the case that there is traffic regularly arriving on the diff --git a/include/envoy/server/overload/thread_local_overload_state.h b/include/envoy/server/overload/thread_local_overload_state.h index 5a4248b6e34a..ef7a701618d9 100644 --- a/include/envoy/server/overload/thread_local_overload_state.h +++ b/include/envoy/server/overload/thread_local_overload_state.h @@ -46,6 +46,9 @@ enum class OverloadTimerType { // The amount of time an HTTP connection to a downstream client can remain idle (no streams). This // corresponds to the HTTP_DOWNSTREAM_CONNECTION_IDLE TimerType in overload.proto. HttpDownstreamIdleConnectionTimeout, + // The amount of time an HTTP stream from a downstream client can remain idle. This corresponds to + // the HTTP_DOWNSTREAM_STREAM_IDLE TimerType in overload.proto. + HttpDownstreamIdleStreamTimeout, }; /** diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index c7c98bc5d3b5..fe435c3977e8 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -637,7 +637,8 @@ ConnectionManagerImpl::ActiveStream::ActiveStream(ConnectionManagerImpl& connect if (connection_manager_.config_.streamIdleTimeout().count()) { idle_timeout_ms_ = connection_manager_.config_.streamIdleTimeout(); - stream_idle_timer_ = connection_manager_.read_callbacks_->connection().dispatcher().createTimer( + stream_idle_timer_ = connection_manager_.overload_state_.createScaledTimer( + Server::OverloadTimerType::HttpDownstreamIdleStreamTimeout, [this]() -> void { onIdleTimeout(); }); resetIdleTimer(); } @@ -1061,9 +1062,9 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapPtr&& he if (idle_timeout_ms_.count()) { // If we have a route-level idle timeout but no global stream idle timeout, create a timer. if (stream_idle_timer_ == nullptr) { - stream_idle_timer_ = - connection_manager_.read_callbacks_->connection().dispatcher().createTimer( - [this]() -> void { onIdleTimeout(); }); + stream_idle_timer_ = connection_manager_.overload_state_.createScaledTimer( + Server::OverloadTimerType::HttpDownstreamIdleStreamTimeout, + [this]() -> void { onIdleTimeout(); }); } } else if (stream_idle_timer_ != nullptr) { // If we had a global stream idle timeout but the route-level idle timeout is set to zero diff --git a/source/server/overload_manager_impl.cc b/source/server/overload_manager_impl.cc index 2ec86122eaa2..07872b98442b 100644 --- a/source/server/overload_manager_impl.cc +++ b/source/server/overload_manager_impl.cc @@ -148,6 +148,8 @@ OverloadTimerType parseTimerType( switch (config_timer_type) { case Config::HTTP_DOWNSTREAM_CONNECTION_IDLE: return OverloadTimerType::HttpDownstreamIdleConnectionTimeout; + case Config::HTTP_DOWNSTREAM_STREAM_IDLE: + return OverloadTimerType::HttpDownstreamIdleStreamTimeout; default: throw EnvoyException(fmt::format("Unknown timer type {}", config_timer_type)); } diff --git a/test/integration/overload_integration_test.cc b/test/integration/overload_integration_test.cc index 00341395591b..a901087212e1 100644 --- a/test/integration/overload_integration_test.cc +++ b/test/integration/overload_integration_test.cc @@ -13,6 +13,8 @@ namespace Envoy { +using testing::HasSubstr; + class FakeResourceMonitorFactory; class FakeResourceMonitor : public Server::ResourceMonitor { @@ -316,4 +318,44 @@ TEST_P(OverloadScaledTimerIntegrationTest, CloseIdleHttpConnections) { codec_client_->close(); } +TEST_P(OverloadScaledTimerIntegrationTest, CloseIdleHttpStream) { + initializeOverloadManager( + TestUtility::parseYaml(R"EOF( + timer_scale_factors: + - timer: HTTP_DOWNSTREAM_STREAM_IDLE + min_timeout: 5s + )EOF")); + + const Http::TestRequestHeaderMapImpl request_headers{ + {":method", "GET"}, {":path", "/test/long/url"}, {":scheme", "http"}, {":authority", "host"}}; + + // Create an HTTP connection and start a request. + FakeStreamPtr http_stream; + codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); + auto response = codec_client_->makeRequestWithBody(request_headers, 10); + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, http_stream)); + ASSERT_TRUE(http_stream->waitForHeadersComplete()); + // At this point, Envoy is waiting for the upstream to respond, but that won't + // happen before it hits the stream timeout. + + // Set the load so the timer is reduced but not to the minimum value. + updateResource(0.8); + test_server_->waitForGaugeGe("overload.envoy.overload_actions.reduce_timeouts.scale_percent", 50); + // Advancing past the minimum time shouldn't end the stream. + timeSystem().advanceTimeWait(std::chrono::seconds(5)); + + // Increase load so that the minimum time has now elapsed. + updateResource(0.9); + test_server_->waitForGaugeEq("overload.envoy.overload_actions.reduce_timeouts.scale_percent", + 100); + + // Wait for the proxy to notice and take action for the overload. + test_server_->waitForCounterGe("http.config_test.downstream_rq_idle_timeout", 1); + response->waitForEndStream(); + + EXPECT_EQ(response->headers().getStatusValue(), "408"); + EXPECT_THAT(response->body(), HasSubstr("stream timeout")); +} + } // namespace Envoy diff --git a/test/server/overload_manager_impl_test.cc b/test/server/overload_manager_impl_test.cc index 71fc4c6d290f..d160b42ceae8 100644 --- a/test/server/overload_manager_impl_test.cc +++ b/test/server/overload_manager_impl_test.cc @@ -483,6 +483,8 @@ constexpr char kReducedTimeoutsConfig[] = R"YAML( timer_scale_factors: - timer: HTTP_DOWNSTREAM_CONNECTION_IDLE min_timeout: 2s + - timer: HTTP_DOWNSTREAM_STREAM_IDLE + min_scale: { value: 10 } triggers: - name: "envoy.resource_monitors.fake_resource1" scaled: From 16fa8c7e244106261c96e264e3cfcca45d951859 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 11 Dec 2020 14:35:29 -0500 Subject: [PATCH 12/49] stats: use 2-phase creation for circuit breaker stats; (#14371) Commit Message: Use 2-phase creation for circuit breaker stats. This was a little messy because of the creative use of the 1-phase stats macros to conditionally null out some of the gauges. This was resolved by manually instantiating the stats structure. Additional Description: Risk Level: low Testing: //tes/t... Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a Signed-off-by: Joshua Marantz --- include/envoy/upstream/cluster_manager.h | 1 + include/envoy/upstream/upstream.h | 41 ++++++++++------ .../common/upstream/cluster_manager_impl.cc | 1 + source/common/upstream/cluster_manager_impl.h | 4 ++ source/common/upstream/upstream_impl.cc | 48 ++++++++++++++----- source/common/upstream/upstream_impl.h | 10 ++-- .../upstream/resource_manager_impl_test.cc | 15 +++--- test/mocks/upstream/cluster_info.cc | 6 ++- test/mocks/upstream/cluster_info.h | 1 + test/mocks/upstream/cluster_manager.cc | 1 + test/mocks/upstream/cluster_manager.h | 4 ++ 11 files changed, 92 insertions(+), 40 deletions(-) diff --git a/include/envoy/upstream/cluster_manager.h b/include/envoy/upstream/cluster_manager.h index b467d0ed52cc..ca4b69a56718 100644 --- a/include/envoy/upstream/cluster_manager.h +++ b/include/envoy/upstream/cluster_manager.h @@ -300,6 +300,7 @@ class ClusterManager { */ virtual const ClusterStatNames& clusterStatNames() const PURE; virtual const ClusterLoadReportStatNames& clusterLoadReportStatNames() const PURE; + virtual const ClusterCircuitBreakersStatNames& clusterCircuitBreakersStatNames() const PURE; virtual const ClusterRequestResponseSizeStatNames& clusterRequestResponseSizeStatNames() const PURE; virtual const ClusterTimeoutBudgetStatNames& clusterTimeoutBudgetStatNames() const PURE; diff --git a/include/envoy/upstream/upstream.h b/include/envoy/upstream/upstream.h index 647706fd371d..5c5c64cf1e4a 100644 --- a/include/envoy/upstream/upstream.h +++ b/include/envoy/upstream/upstream.h @@ -610,20 +610,28 @@ class PrioritySet { COUNTER(upstream_rq_dropped) /** - * Cluster circuit breakers stats. Open circuit breaker stats and remaining resource stats - * can be handled differently by passing in different macros. + * Cluster circuit breakers gauges. Note that we do not generate a stats + * structure from this macro. This is because depending on flags, we want to use + * null gauges for all the "remaining" ones. This is hard to automate with the + * 2-phase macros, so ClusterInfoImpl::generateCircuitBreakersStats is + * hand-coded and must be changed if we alter the set of gauges in this macro. + * We also include stat-names in this structure that are used when composing + * the circuit breaker names, depending on priority settings. */ -#define ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(OPEN_GAUGE, REMAINING_GAUGE) \ - OPEN_GAUGE(cx_open, Accumulate) \ - OPEN_GAUGE(cx_pool_open, Accumulate) \ - OPEN_GAUGE(rq_open, Accumulate) \ - OPEN_GAUGE(rq_pending_open, Accumulate) \ - OPEN_GAUGE(rq_retry_open, Accumulate) \ - REMAINING_GAUGE(remaining_cx, Accumulate) \ - REMAINING_GAUGE(remaining_cx_pools, Accumulate) \ - REMAINING_GAUGE(remaining_pending, Accumulate) \ - REMAINING_GAUGE(remaining_retries, Accumulate) \ - REMAINING_GAUGE(remaining_rq, Accumulate) +#define ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(COUNTER, GAUGE, HISTOGRAM, TEXT_READOUT, STATNAME) \ + GAUGE(cx_open, Accumulate) \ + GAUGE(cx_pool_open, Accumulate) \ + GAUGE(rq_open, Accumulate) \ + GAUGE(rq_pending_open, Accumulate) \ + GAUGE(rq_retry_open, Accumulate) \ + GAUGE(remaining_cx, Accumulate) \ + GAUGE(remaining_cx_pools, Accumulate) \ + GAUGE(remaining_pending, Accumulate) \ + GAUGE(remaining_retries, Accumulate) \ + GAUGE(remaining_rq, Accumulate) \ + STATNAME(circuit_breakers) \ + STATNAME(default) \ + STATNAME(high) /** * All stats tracking request/response headers and body sizes. Not used by default. @@ -651,6 +659,11 @@ MAKE_STAT_NAMES_STRUCT(ClusterLoadReportStatNames, ALL_CLUSTER_LOAD_REPORT_STATS MAKE_STATS_STRUCT(ClusterLoadReportStats, ClusterLoadReportStatNames, ALL_CLUSTER_LOAD_REPORT_STATS); +// We can't use macros to make the Stats class for circuit breakers due to +// the conditional inclusion of 'remaining' gauges. But we do auto-generate +// the StatNames struct. +MAKE_STAT_NAMES_STRUCT(ClusterCircuitBreakersStatNames, ALL_CLUSTER_CIRCUIT_BREAKERS_STATS); + MAKE_STAT_NAMES_STRUCT(ClusterRequestResponseSizeStatNames, ALL_CLUSTER_REQUEST_RESPONSE_SIZE_STATS); MAKE_STATS_STRUCT(ClusterRequestResponseSizeStats, ClusterRequestResponseSizeStatNames, @@ -664,7 +677,7 @@ MAKE_STATS_STRUCT(ClusterTimeoutBudgetStats, ClusterTimeoutBudgetStatNames, * Struct definition for cluster circuit breakers stats. @see stats_macros.h */ struct ClusterCircuitBreakersStats { - ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(GENERATE_GAUGE_STRUCT, GENERATE_GAUGE_STRUCT) + ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(c, GENERATE_GAUGE_STRUCT, h, tr, GENERATE_STATNAME_STRUCT) }; using ClusterRequestResponseSizeStatsPtr = std::unique_ptr; diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index f7b856380e00..7c9246d88b61 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -261,6 +261,7 @@ ClusterManagerImpl::ClusterManagerImpl( http_context_(http_context), router_context_(router_context), cluster_stat_names_(stats.symbolTable()), cluster_load_report_stat_names_(stats.symbolTable()), + cluster_circuit_breakers_stat_names_(stats.symbolTable()), cluster_request_response_size_stat_names_(stats.symbolTable()), cluster_timeout_budget_stat_names_(stats.symbolTable()), subscription_factory_(local_info, main_thread_dispatcher, *this, diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index ca55b3fbcdb1..ebbd98c55b17 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -290,6 +290,9 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable Stats::Gauge& { + return Stats::Utility::gaugeFromElements(scope, + {stat_names.circuit_breakers_, prefix, stat_name}, + Stats::Gauge::ImportMode::Accumulate); + }; + +#define REMAINING_GAUGE(stat_name) track_remaining ? make_gauge(stat_name) : scope.nullGauge("") + + return { + make_gauge(stat_names.cx_open_), + make_gauge(stat_names.cx_pool_open_), + make_gauge(stat_names.rq_open_), + make_gauge(stat_names.rq_pending_open_), + make_gauge(stat_names.rq_retry_open_), + REMAINING_GAUGE(stat_names.remaining_cx_), + REMAINING_GAUGE(stat_names.remaining_cx_pools_), + REMAINING_GAUGE(stat_names.remaining_pending_), + REMAINING_GAUGE(stat_names.remaining_retries_), + REMAINING_GAUGE(stat_names.remaining_rq_), + }; + +#undef REMAINING_GAUGE } Http::Http1::CodecStats& ClusterInfoImpl::http1CodecStats() const { @@ -1201,12 +1219,15 @@ ClusterInfoImpl::ResourceManagers::load(const envoy::config::cluster::v3::Cluste bool track_remaining = false; + Stats::StatName priority_stat_name; std::string priority_name; switch (priority) { case envoy::config::core::v3::DEFAULT: + priority_stat_name = circuit_breakers_stat_names_.default_; priority_name = "default"; break; case envoy::config::core::v3::HIGH: + priority_stat_name = circuit_breakers_stat_names_.high_; priority_name = "high"; break; default: @@ -1251,7 +1272,8 @@ ClusterInfoImpl::ResourceManagers::load(const envoy::config::cluster::v3::Cluste return std::make_unique( runtime, runtime_prefix, max_connections, max_pending_requests, max_requests, max_retries, max_connection_pools, - ClusterInfoImpl::generateCircuitBreakersStats(stats_scope, priority_name, track_remaining), + ClusterInfoImpl::generateCircuitBreakersStats(stats_scope, priority_stat_name, + track_remaining, circuit_breakers_stat_names_), budget_percent, min_retry_concurrency); } diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 3b22d6577482..8f8f0fea1049 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -530,9 +530,9 @@ class ClusterInfoImpl : public ClusterInfo, protected Logger::Loggable; Managers managers_; + const ClusterCircuitBreakersStatNames& circuit_breakers_stat_names_; }; struct OptionalClusterStats { diff --git a/test/common/upstream/resource_manager_impl_test.cc b/test/common/upstream/resource_manager_impl_test.cc index 26e48264f36d..f92cf3009d63 100644 --- a/test/common/upstream/resource_manager_impl_test.cc +++ b/test/common/upstream/resource_manager_impl_test.cc @@ -17,6 +17,11 @@ namespace Envoy { namespace Upstream { namespace { +ClusterCircuitBreakersStats clusterCircuitBreakersStats(Stats::Store& store) { + return { + ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(c, POOL_GAUGE(store), h, tr, GENERATE_STATNAME_STRUCT)}; +} + TEST(ResourceManagerImplTest, RuntimeResourceManager) { NiceMock runtime; NiceMock gauge; @@ -26,9 +31,7 @@ TEST(ResourceManagerImplTest, RuntimeResourceManager) { ResourceManagerImpl resource_manager( runtime, "circuit_breakers.runtime_resource_manager_test.default.", 0, 0, 0, 1, 0, - ClusterCircuitBreakersStats{ - ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(POOL_GAUGE(store), POOL_GAUGE(store))}, - absl::nullopt, absl::nullopt); + clusterCircuitBreakersStats(store), absl::nullopt, absl::nullopt); EXPECT_CALL( runtime.snapshot_, @@ -83,8 +86,7 @@ TEST(ResourceManagerImplTest, RemainingResourceGauges) { NiceMock runtime; Stats::IsolatedStoreImpl store; - auto stats = ClusterCircuitBreakersStats{ - ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(POOL_GAUGE(store), POOL_GAUGE(store))}; + auto stats = clusterCircuitBreakersStats(store); ResourceManagerImpl resource_manager(runtime, "circuit_breakers.runtime_resource_manager_test.default.", 1, 2, 1, 0, 3, stats, absl::nullopt, absl::nullopt); @@ -149,8 +151,7 @@ TEST(ResourceManagerImplTest, RetryBudgetOverrideGauge) { NiceMock runtime; Stats::IsolatedStoreImpl store; - auto stats = ClusterCircuitBreakersStats{ - ALL_CLUSTER_CIRCUIT_BREAKERS_STATS(POOL_GAUGE(store), POOL_GAUGE(store))}; + auto stats = clusterCircuitBreakersStats(store); // Test retry budgets disable remaining_retries gauge (it should always be 0). ResourceManagerImpl rm(runtime, "circuit_breakers.runtime_resource_manager_test.default.", 1, 2, diff --git a/test/mocks/upstream/cluster_info.cc b/test/mocks/upstream/cluster_info.cc index c6694c47aa29..1a0a7fa4f2e9 100644 --- a/test/mocks/upstream/cluster_info.cc +++ b/test/mocks/upstream/cluster_info.cc @@ -41,6 +41,7 @@ MockClusterInfo::MockClusterInfo() envoy::config::core::v3::Http2ProtocolOptions())), stat_names_(stats_store_.symbolTable()), cluster_load_report_stat_names_(stats_store_.symbolTable()), + cluster_circuit_breakers_stat_names_(stats_store_.symbolTable()), cluster_request_response_size_stat_names_(stats_store_.symbolTable()), cluster_timeout_budget_stat_names_(stats_store_.symbolTable()), stats_(ClusterInfoImpl::generateStats(stats_store_, stat_names_)), @@ -53,8 +54,9 @@ MockClusterInfo::MockClusterInfo() timeout_budget_stats_( std::make_unique(ClusterInfoImpl::generateTimeoutBudgetStats( timeout_budget_stats_store_, cluster_timeout_budget_stat_names_))), - circuit_breakers_stats_( - ClusterInfoImpl::generateCircuitBreakersStats(stats_store_, "default", true)), + circuit_breakers_stats_(ClusterInfoImpl::generateCircuitBreakersStats( + stats_store_, cluster_circuit_breakers_stat_names_.default_, true, + cluster_circuit_breakers_stat_names_)), resource_manager_(new Upstream::ResourceManagerImpl( runtime_, "fake_key", 1, 1024, 1024, 1, std::numeric_limits::max(), circuit_breakers_stats_, absl::nullopt, absl::nullopt)) { diff --git a/test/mocks/upstream/cluster_info.h b/test/mocks/upstream/cluster_info.h index fc2fb337e21e..7674b6671a2b 100644 --- a/test/mocks/upstream/cluster_info.h +++ b/test/mocks/upstream/cluster_info.h @@ -158,6 +158,7 @@ class MockClusterInfo : public ClusterInfo { NiceMock stats_store_; ClusterStatNames stat_names_; ClusterLoadReportStatNames cluster_load_report_stat_names_; + ClusterCircuitBreakersStatNames cluster_circuit_breakers_stat_names_; ClusterRequestResponseSizeStatNames cluster_request_response_size_stat_names_; ClusterTimeoutBudgetStatNames cluster_timeout_budget_stat_names_; ClusterStats stats_; diff --git a/test/mocks/upstream/cluster_manager.cc b/test/mocks/upstream/cluster_manager.cc index a7a23633291f..a789f485d47b 100644 --- a/test/mocks/upstream/cluster_manager.cc +++ b/test/mocks/upstream/cluster_manager.cc @@ -18,6 +18,7 @@ MockClusterManager::MockClusterManager(TimeSource&) : MockClusterManager() {} MockClusterManager::MockClusterManager() : cluster_stat_names_(*symbol_table_), cluster_load_report_stat_names_(*symbol_table_), + cluster_circuit_breakers_stat_names_(*symbol_table_), cluster_request_response_size_stat_names_(*symbol_table_), cluster_timeout_budget_stat_names_(*symbol_table_) { ON_CALL(*this, bindConfig()).WillByDefault(ReturnRef(bind_config_)); diff --git a/test/mocks/upstream/cluster_manager.h b/test/mocks/upstream/cluster_manager.h index 90cbec0ec757..bcff9e0fde11 100644 --- a/test/mocks/upstream/cluster_manager.h +++ b/test/mocks/upstream/cluster_manager.h @@ -59,6 +59,9 @@ class MockClusterManager : public ClusterManager { const ClusterLoadReportStatNames& clusterLoadReportStatNames() const override { return cluster_load_report_stat_names_; } + const ClusterCircuitBreakersStatNames& clusterCircuitBreakersStatNames() const override { + return cluster_circuit_breakers_stat_names_; + } const ClusterRequestResponseSizeStatNames& clusterRequestResponseSizeStatNames() const override { return cluster_request_response_size_stat_names_; } @@ -78,6 +81,7 @@ class MockClusterManager : public ClusterManager { Stats::TestSymbolTable symbol_table_; ClusterStatNames cluster_stat_names_; ClusterLoadReportStatNames cluster_load_report_stat_names_; + ClusterCircuitBreakersStatNames cluster_circuit_breakers_stat_names_; ClusterRequestResponseSizeStatNames cluster_request_response_size_stat_names_; ClusterTimeoutBudgetStatNames cluster_timeout_budget_stat_names_; }; From 046b0e315e04f645aa0d62119a056bc560497ed7 Mon Sep 17 00:00:00 2001 From: Lizan Zhou Date: Fri, 11 Dec 2020 16:43:32 -0800 Subject: [PATCH 13/49] sds: refactor to use shared target for init manager registration (#14356) Signed-off-by: Lizan Zhou --- include/envoy/secret/BUILD | 1 + include/envoy/secret/secret_provider.h | 7 +++++++ source/common/secret/sds_api.h | 26 +++++++++----------------- test/common/secret/sds_api_test.cc | 26 +++++++++++++------------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/include/envoy/secret/BUILD b/include/envoy/secret/BUILD index 219884c19e81..04adec2fab50 100644 --- a/include/envoy/secret/BUILD +++ b/include/envoy/secret/BUILD @@ -30,6 +30,7 @@ envoy_cc_library( hdrs = ["secret_manager.h"], deps = [ ":secret_provider_interface", + "//include/envoy/init:target_interface", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", ], diff --git a/include/envoy/secret/secret_provider.h b/include/envoy/secret/secret_provider.h index 2dbc31e5791a..0b6c098bc2cd 100644 --- a/include/envoy/secret/secret_provider.h +++ b/include/envoy/secret/secret_provider.h @@ -5,6 +5,7 @@ #include "envoy/common/callback.h" #include "envoy/common/pure.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" +#include "envoy/init/target.h" #include "envoy/ssl/certificate_validation_context_config.h" #include "envoy/ssl/tls_certificate_config.h" @@ -41,6 +42,12 @@ template class SecretProvider { * @return CallbackHandle the handle which can remove that update callback. */ virtual Common::CallbackHandle* addUpdateCallback(std::function callback) PURE; + + /** + * @return const Init::Target* A shared init target that can be used by multiple init managers. + * nullptr if the provider isn't dynamic. + */ + virtual const Init::Target* initTarget() { return nullptr; } }; using TlsCertificatePtr = diff --git a/source/common/secret/sds_api.h b/source/common/secret/sds_api.h index 1c58feb6c5bc..4be8fb63d4fb 100644 --- a/source/common/secret/sds_api.h +++ b/source/common/secret/sds_api.h @@ -59,19 +59,9 @@ class SdsApi : public Envoy::Config::SubscriptionBase< Config::SubscriptionFactory& subscription_factory, TimeSource& time_source, ProtobufMessage::ValidationVisitor& validation_visitor, Stats::Store& stats, std::function destructor_cb, Event::Dispatcher& dispatcher, Api::Api& api); - ~SdsApi() override { - RELEASE_ASSERT(registered_init_target_, - "Init target was not registered with an init manager. registerInitTarget() must " - "be called after Sds api concrete class instantiation."); - }; SecretData secretData(); - void registerInitTarget(Init::Manager& init_manager) { - init_manager.add(init_target_); - registered_init_target_ = true; - } - protected: // Ordered for hash stability. using FileContentMap = std::map; @@ -97,7 +87,7 @@ class SdsApi : public Envoy::Config::SubscriptionBase< void resolveDataSource(const FileContentMap& files, envoy::config::core::v3::DataSource& data_source); - Init::TargetImpl init_target_; + Init::SharedTargetImpl init_target_; Event::Dispatcher& dispatcher_; Api::Api& api_; @@ -124,7 +114,6 @@ class SdsApi : public Envoy::Config::SubscriptionBase< TimeSource& time_source_; SecretData secret_data_; std::unique_ptr watcher_; - bool registered_init_target_{false}; }; class TlsCertificateSdsApi; @@ -154,7 +143,7 @@ class TlsCertificateSdsApi : public SdsApi, public TlsCertificateConfigProvider secret_provider_context.dispatcher().timeSource(), secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(), destructor_cb, secret_provider_context.dispatcher(), secret_provider_context.api()); - ret->registerInitTarget(secret_provider_context.initManager()); + secret_provider_context.initManager().add(*ret->initTarget()); return ret; } @@ -182,6 +171,7 @@ class TlsCertificateSdsApi : public SdsApi, public TlsCertificateConfigProvider } return update_callback_manager_.add(callback); } + const Init::Target* initTarget() override { return &init_target_; } protected: void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override { @@ -238,7 +228,7 @@ class CertificateValidationContextSdsApi : public SdsApi, secret_provider_context.dispatcher().timeSource(), secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(), destructor_cb, secret_provider_context.dispatcher(), secret_provider_context.api()); - ret->registerInitTarget(secret_provider_context.initManager()); + secret_provider_context.initManager().add(*ret->initTarget()); return ret; } CertificateValidationContextSdsApi(const envoy::config::core::v3::ConfigSource& sds_config, @@ -262,13 +252,13 @@ class CertificateValidationContextSdsApi : public SdsApi, } return update_callback_manager_.add(callback); } - Common::CallbackHandle* addValidationCallback( std::function< void(const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext&)> callback) override { return validation_callback_manager_.add(callback); } + const Init::Target* initTarget() override { return &init_target_; } protected: void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override { @@ -333,7 +323,7 @@ class TlsSessionTicketKeysSdsApi : public SdsApi, public TlsSessionTicketKeysCon secret_provider_context.dispatcher().timeSource(), secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(), destructor_cb, secret_provider_context.dispatcher(), secret_provider_context.api()); - ret->registerInitTarget(secret_provider_context.initManager()); + secret_provider_context.initManager().add(*ret->initTarget()); return ret; } @@ -366,6 +356,7 @@ class TlsSessionTicketKeysSdsApi : public SdsApi, public TlsSessionTicketKeysCon callback) override { return validation_callback_manager_.add(callback); } + const Init::Target* initTarget() override { return &init_target_; } protected: void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override { @@ -405,7 +396,7 @@ class GenericSecretSdsApi : public SdsApi, public GenericSecretConfigProvider { secret_provider_context.dispatcher().timeSource(), secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(), destructor_cb, secret_provider_context.dispatcher(), secret_provider_context.api()); - ret->registerInitTarget(secret_provider_context.initManager()); + secret_provider_context.initManager().add(*ret->initTarget()); return ret; } @@ -430,6 +421,7 @@ class GenericSecretSdsApi : public SdsApi, public GenericSecretConfigProvider { callback) override { return validation_callback_manager_.add(callback); } + const Init::Target* initTarget() override { return &init_target_; } protected: void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override { diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index 8e8908fa1296..90ccdec8ebd8 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -76,7 +76,7 @@ TEST_F(SdsApiTest, BasicTest) { TlsCertificateSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); initialize(); } @@ -126,7 +126,7 @@ TEST_F(SdsApiTest, InitManagerInitialised) { TlsCertificateSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - EXPECT_NO_THROW(sds_api.registerInitTarget(init_manager)); + EXPECT_NO_THROW(init_manager.add(*sds_api.initTarget())); } // Validate that bad ConfigSources are caught at construction time. This is a regression test for @@ -153,7 +153,7 @@ TEST_F(SdsApiTest, DynamicTlsCertificateUpdateSuccess) { TlsCertificateSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); initialize(); NiceMock secret_callback; auto handle = @@ -217,7 +217,7 @@ class TlsCertificateSdsRotationApiTest : public testing::TestWithParam, sds_api_ = std::make_unique( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, mock_dispatcher_, *api_); - sds_api_->registerInitTarget(init_manager_); + init_manager_.add(*sds_api_->initTarget()); initialize(); handle_ = sds_api_->addUpdateCallback([this]() { secret_callback_.onAddOrUpdateSecret(); }); } @@ -287,7 +287,7 @@ class CertificateValidationContextSdsRotationApiTest : public testing::TestWithP sds_api_ = std::make_unique( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, mock_dispatcher_, *api_); - sds_api_->registerInitTarget(init_manager_); + init_manager_.add(*sds_api_->initTarget()); initialize(); handle_ = sds_api_->addUpdateCallback([this]() { secret_callback_.onAddOrUpdateSecret(); }); } @@ -500,7 +500,7 @@ class PartialMockSds : public SdsApi { : SdsApi( config_source, "abc.com", subscription_factory, time_source, validation_visitor_, stats, []() {}, dispatcher, api) { - registerInitTarget(init_manager); + init_manager.add(init_target_); } MOCK_METHOD(void, onConfigUpdate, @@ -554,7 +554,7 @@ TEST_F(SdsApiTest, DeltaUpdateSuccess) { TlsCertificateSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); NiceMock secret_callback; auto handle = @@ -599,7 +599,7 @@ TEST_F(SdsApiTest, DynamicCertificateValidationContextUpdateSuccess) { CertificateValidationContextSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); NiceMock secret_callback; auto handle = @@ -653,7 +653,7 @@ TEST_F(SdsApiTest, DefaultCertificateValidationContextTest) { CertificateValidationContextSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); NiceMock secret_callback; auto handle = @@ -741,7 +741,7 @@ TEST_F(SdsApiTest, GenericSecretSdsApiTest) { GenericSecretSdsApi sds_api( config_source, "encryption_key", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); NiceMock secret_callback; auto handle = @@ -786,7 +786,7 @@ TEST_F(SdsApiTest, EmptyResource) { TlsCertificateSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); initialize(); EXPECT_THROW_WITH_MESSAGE(subscription_factory_.callbacks_->onConfigUpdate({}, ""), @@ -801,7 +801,7 @@ TEST_F(SdsApiTest, SecretUpdateWrongSize) { TlsCertificateSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); std::string yaml = R"EOF( @@ -831,7 +831,7 @@ TEST_F(SdsApiTest, SecretUpdateWrongSecretName) { TlsCertificateSdsApi sds_api( config_source, "abc.com", subscription_factory_, time_system_, validation_visitor_, stats_, []() {}, *dispatcher_, *api_); - sds_api.registerInitTarget(init_manager_); + init_manager_.add(*sds_api.initTarget()); std::string yaml = R"EOF( From 6a8e681d6e39327e702a474f2aa74c71f2e3d2c7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 13 Dec 2020 16:49:59 -0500 Subject: [PATCH 14/49] stats: Suggest new stats macros to avoid hot-path contention. (#14312) Signed-off-by: Joshua Marantz --- source/docs/stats.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/docs/stats.md b/source/docs/stats.md index 8b67355e24c8..d6f82e80e445 100644 --- a/source/docs/stats.md +++ b/source/docs/stats.md @@ -184,7 +184,14 @@ showing the memory layout for a few scenarios of constructing and joining symbol There are several ways to create hot-path contention looking up stats by name, and there is no bulletproof way to prevent it from occurring. - * The [stats macros](https://github.com/envoyproxy/envoy/blob/master/include/envoy/stats/stats_macros.h) may be used in a data structure which is constructed in response to requests. + * The [stats macros](https://github.com/envoyproxy/envoy/blob/master/include/envoy/stats/stats_macros.h) may be used in a data structure which is constructed in response to requests. In this + scenario, consider factoring out the symbolization phase using MAKE_STAT_NAMES_STRUCT + in a factory or context during startup, and using MAKE_STATS_STRUCT in the hot-path and during + control-plane updates, so that we do not need to take symbol-table locks. As an example, see + [ClusterInfoImpl::generateStats](https://github.com/envoyproxy/envoy/blob/8188e232a9e0b15111d30f4724cbc7bf77d3964a/source/common/upstream/upstream_impl.cc#L641) + and its + [MAKE_STAT_NAMES_STRUCT](https://github.com/envoyproxy/envoy/blob/8188e232a9e0b15111d30f4724cbc7bf77d3964a/include/envoy/upstream/upstream.h#L646). + invocation. * An explicit symbol-table lookup, via `StatNamePool` or `StatNameSet` can be made in the hot path. From 22fa7679551d360143e5e8c62149db558e5e437a Mon Sep 17 00:00:00 2001 From: John Esmet Date: Sun, 13 Dec 2020 16:50:25 -0500 Subject: [PATCH 15/49] Add regex_rewrite to redirect action (#14351) Signed-off-by: John Esmet --- .../config/route/v3/route_components.proto | 27 +++++++- .../route/v4alpha/route_components.proto | 27 +++++++- docs/root/version_history/current.rst | 1 + .../config/route/v3/route_components.proto | 27 +++++++- .../route/v4alpha/route_components.proto | 27 +++++++- source/common/router/config_impl.cc | 37 +++++++++- source/common/router/config_impl.h | 15 ++-- test/common/router/config_impl_test.cc | 68 +++++++++++++++++++ 8 files changed, 218 insertions(+), 11 deletions(-) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index c1bc1ace9130..2d85fd0dc7db 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1326,7 +1326,7 @@ message HedgePolicy { bool hedge_on_per_try_timeout = 3; } -// [#next-free-field: 9] +// [#next-free-field: 10] message RedirectAction { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RedirectAction"; @@ -1397,6 +1397,31 @@ message RedirectAction { // :ref:`RouteAction's prefix_rewrite `. string prefix_rewrite = 5 [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; + + // Indicates that during redirect, 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. + // + // 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`` + // 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``. + type.matcher.v3.RegexMatchAndSubstitute regex_rewrite = 9; } // The HTTP status code to use in the redirect response. The default response diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 4083513009d7..166b19671917 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1273,7 +1273,7 @@ message HedgePolicy { bool hedge_on_per_try_timeout = 3; } -// [#next-free-field: 9] +// [#next-free-field: 10] message RedirectAction { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.RedirectAction"; @@ -1345,6 +1345,31 @@ message RedirectAction { // :ref:`RouteAction's prefix_rewrite `. string prefix_rewrite = 5 [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; + + // Indicates that during redirect, 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. + // + // 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`` + // 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``. + type.matcher.v4alpha.RegexMatchAndSubstitute regex_rewrite = 9; } // The HTTP status code to use in the redirect response. The default response diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index c7fbbbd56b06..40b38e1c62ec 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -75,6 +75,7 @@ New Features * overload: add :ref:`envoy.overload_actions.reduce_timeouts ` overload action to enable scaling timeouts down with load. Scaling support :ref:`is limited ` to the HTTP connection and stream idle timeouts. * ratelimit: added support for use of various :ref:`metadata ` as a ratelimit action. * ratelimit: added :ref:`disable_x_envoy_ratelimited_header ` option to disable `X-Envoy-RateLimited` header. +* router: added support for regex rewrites during HTTP redirects using :ref:`regex_rewrite `. * sds: improved support for atomic :ref:`key rotations ` and added configurable rotation triggers for :ref:`TlsCertificate ` and :ref:`CertificateValidationContext `. 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 89c0a1c76cf7..e90d3bce7e05 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -1333,7 +1333,7 @@ message HedgePolicy { bool hedge_on_per_try_timeout = 3; } -// [#next-free-field: 9] +// [#next-free-field: 10] message RedirectAction { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RedirectAction"; @@ -1404,6 +1404,31 @@ message RedirectAction { // :ref:`RouteAction's prefix_rewrite `. string prefix_rewrite = 5 [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; + + // Indicates that during redirect, 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. + // + // 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`` + // 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``. + type.matcher.v3.RegexMatchAndSubstitute regex_rewrite = 9; } // The HTTP status code to use in the redirect response. The default response diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index b67c4efa3952..935610424b1a 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -1340,7 +1340,7 @@ message HedgePolicy { bool hedge_on_per_try_timeout = 3; } -// [#next-free-field: 9] +// [#next-free-field: 10] message RedirectAction { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.RedirectAction"; @@ -1412,6 +1412,31 @@ message RedirectAction { // :ref:`RouteAction's prefix_rewrite `. string prefix_rewrite = 5 [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; + + // Indicates that during redirect, 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. + // + // 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`` + // 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``. + type.matcher.v4alpha.RegexMatchAndSubstitute regex_rewrite = 9; } // The HTTP status code to use in the redirect response. The default response diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 531a46957003..56b6e613d4c9 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -463,6 +463,13 @@ RouteEntryImplBase::RouteEntryImplBase(const VirtualHostImpl& vhost, regex_rewrite_substitution_ = rewrite_spec.substitution(); } + if (route.redirect().has_regex_rewrite()) { + ASSERT(prefix_rewrite_redirect_.empty()); + auto rewrite_spec = route.redirect().regex_rewrite(); + regex_rewrite_redirect_ = Regex::Utility::parseRegex(rewrite_spec.pattern()); + regex_rewrite_redirect_substitution_ = rewrite_spec.substitution(); + } + if (enable_preserve_query_in_path_redirects_ && path_redirect_has_query_ && strip_query_) { ENVOY_LOG(warn, "`strip_query` is set to true, but `path_redirect` contains query string and it will " @@ -567,7 +574,8 @@ void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers, } // Handle path rewrite - if (!getPathRewrite().empty() || regex_rewrite_ != nullptr) { + absl::optional container; + if (!getPathRewrite(headers, container).empty() || regex_rewrite_ != nullptr) { rewritePathHeader(headers, insert_envoy_original_path); } } @@ -602,6 +610,30 @@ RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& return runtime; } +const std::string& +RouteEntryImplBase::getPathRewrite(const Http::RequestHeaderMap& headers, + absl::optional& container) const { + // Just use the prefix rewrite if this isn't a redirect. + if (!isRedirect()) { + return prefix_rewrite_; + } + + // Return the regex rewrite substitution for redirects, if set. + if (regex_rewrite_redirect_ != nullptr) { + // Copy just the path and rewrite it using the regex. + // + // Store the result in the output container, and return a reference to the underlying string. + auto just_path(Http::PathUtil::removeQueryAndFragment(headers.getPathValue())); + container = + regex_rewrite_redirect_->replaceAll(just_path, regex_rewrite_redirect_substitution_); + + return container.value(); + } + + // Otherwise, return the prefix rewrite used for redirects. + return prefix_rewrite_redirect_; +} + // finalizePathHeaders does the "standard" path rewriting, meaning that it // handles the "prefix_rewrite" and "regex_rewrite" route actions, only one of // which can be specified. The "matched_path" argument applies only to the @@ -612,7 +644,8 @@ RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& void RouteEntryImplBase::finalizePathHeader(Http::RequestHeaderMap& headers, absl::string_view matched_path, bool insert_envoy_original_path) const { - const auto& rewrite = getPathRewrite(); + absl::optional container; + const auto& rewrite = getPathRewrite(headers, container); if (rewrite.empty() && regex_rewrite_ == nullptr) { // There are no rewrites configured. Just return. return; diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index 3b7583c578e0..e62045ed86ad 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -458,7 +458,8 @@ class RouteEntryImplBase : public RouteEntry, if (!isDirectResponse()) { return false; } - return !host_redirect_.empty() || !path_redirect_.empty() || !prefix_rewrite_redirect_.empty(); + return !host_redirect_.empty() || !path_redirect_.empty() || + !prefix_rewrite_redirect_.empty() || regex_rewrite_redirect_ != nullptr; } bool matchRoute(const Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo& stream_info, @@ -553,7 +554,9 @@ class RouteEntryImplBase : public RouteEntry, const bool case_sensitive_; const std::string prefix_rewrite_; Regex::CompiledMatcherPtr regex_rewrite_; + Regex::CompiledMatcherPtr regex_rewrite_redirect_; std::string regex_rewrite_substitution_; + std::string regex_rewrite_redirect_substitution_; const std::string host_rewrite_; bool include_vh_rate_limits_; absl::optional connect_config_; @@ -561,11 +564,13 @@ class RouteEntryImplBase : public RouteEntry, RouteConstSharedPtr clusterEntry(const Http::HeaderMap& headers, uint64_t random_value) const; /** - * returns the correct path rewrite string for this route. + * Returns the correct path rewrite string for this route. + * + * The provided container may be used to store memory backing the return value + * therefore it must outlive any use of the return value. */ - const std::string& getPathRewrite() const { - return (isRedirect()) ? prefix_rewrite_redirect_ : prefix_rewrite_; - } + const std::string& getPathRewrite(const Http::RequestHeaderMap& headers, + absl::optional& container) const; void finalizePathHeader(Http::RequestHeaderMap& headers, absl::string_view matched_path, bool insert_envoy_original_path) const; diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 6439a43bc9f9..2913f1f46629 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6521,6 +6521,74 @@ TEST_F(RouteConfigurationV2, RedirectPrefixRewrite) { } } +TEST_F(RouteConfigurationV2, RedirectRegexRewrite) { + std::string yaml = R"EOF( +virtual_hosts: + - name: redirect + domains: [redirect.lyft.com] + routes: + - match: + safe_regex: + google_re2: {} + regex: /foo/([0-9]{4})/(.*) + redirect: + regex_rewrite: + pattern: + google_re2: {} + regex: /foo/([0-9]{4})/(.*) + substitution: /\2/\1/baz + - match: + safe_regex: + google_re2: {} + regex: /strip-query/([0-9]{4})/(.*) + redirect: + strip_query: true + regex_rewrite: + pattern: + google_re2: {} + regex: /strip-query/([0-9]{4})/(.*) + substitution: /\2/\1/baz + )EOF"; + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + + EXPECT_EQ(nullptr, config.route(genRedirectHeaders("www.foo.com", "/foo", true, true), 0)); + + // Regex rewrite with a query, no strip_query + { + Http::TestRequestHeaderMapImpl headers = + genRedirectHeaders("redirect.lyft.com", "/foo/9000/endpoint?lang=eng&con=US", false, false); + const DirectResponseEntry* redirect = config.route(headers, 0)->directResponseEntry(); + redirect->rewritePathHeader(headers, true); + EXPECT_EQ("http://redirect.lyft.com/endpoint/9000/baz?lang=eng&con=US", + redirect->newPath(headers)); + } + // Regex rewrite without a query, no strip_query + { + Http::TestRequestHeaderMapImpl headers = + genRedirectHeaders("redirect.lyft.com", "/foo/1984/bar/anything", false, false); + const DirectResponseEntry* redirect = config.route(headers, 0)->directResponseEntry(); + redirect->rewritePathHeader(headers, true); + EXPECT_EQ("http://redirect.lyft.com/bar/anything/1984/baz", redirect->newPath(headers)); + } + // Regex rewrite with a query, with strip_query + { + Http::TestRequestHeaderMapImpl headers = genRedirectHeaders( + "redirect.lyft.com", "/strip-query/9000/endpoint?lang=eng&con=US", false, false); + const DirectResponseEntry* redirect = config.route(headers, 0)->directResponseEntry(); + redirect->rewritePathHeader(headers, true); + EXPECT_EQ("http://redirect.lyft.com/endpoint/9000/baz", redirect->newPath(headers)); + } + // Regex rewrite without a query, with strip_query + { + Http::TestRequestHeaderMapImpl headers = + genRedirectHeaders("redirect.lyft.com", "/strip-query/1984/bar/anything", false, false); + const DirectResponseEntry* redirect = config.route(headers, 0)->directResponseEntry(); + redirect->rewritePathHeader(headers, true); + EXPECT_EQ("http://redirect.lyft.com/bar/anything/1984/baz", redirect->newPath(headers)); + } +} + TEST_F(RouteConfigurationV2, PathRedirectQueryNotPreserved) { TestScopedRuntime scoped_runtime; Runtime::LoaderSingleton::getExisting()->mergeValues( From 98e8bf3f09fa2af3735217bfc7046517250cdfd2 Mon Sep 17 00:00:00 2001 From: Christoph Pakulski Date: Sun, 13 Dec 2020 17:03:48 -0500 Subject: [PATCH 16/49] transport socket: api and implementation for startTls transport socket (#13112) Signed-off-by: Christoph Pakulski --- CODEOWNERS | 2 + api/BUILD | 1 + .../transport_sockets/starttls/v3/BUILD | 13 + .../starttls/v3/starttls.proto | 38 ++ .../transport_sockets/starttls/v4alpha/BUILD | 14 + .../starttls/v4alpha/starttls.proto | 41 ++ api/versioning/BUILD | 1 + docs/root/version_history/current.rst | 1 + .../transport_sockets/starttls/v3/BUILD | 13 + .../starttls/v3/starttls.proto | 38 ++ .../transport_sockets/starttls/v4alpha/BUILD | 14 + .../starttls/v4alpha/starttls.proto | 41 ++ include/envoy/network/connection.h | 8 + include/envoy/network/transport_socket.h | 7 + source/common/network/connection_impl.h | 1 + source/common/network/raw_buffer_socket.h | 1 + source/common/tcp_proxy/tcp_proxy.cc | 2 +- source/extensions/extensions_build_config.bzl | 1 + .../quic_filter_manager_connection_impl.h | 1 + .../transport_sockets/alts/tsi_socket.h | 1 + .../transport_sockets/common/passthrough.h | 2 + .../transport_sockets/starttls/BUILD | 53 +++ .../transport_sockets/starttls/config.cc | 53 +++ .../transport_sockets/starttls/config.h | 29 ++ .../starttls/starttls_socket.cc | 38 ++ .../starttls/starttls_socket.h | 93 +++++ .../transport_sockets/tls/ssl_socket.cc | 1 + .../transport_sockets/tls/ssl_socket.h | 1 + .../transport_sockets/well_known_names.h | 2 + source/server/api_listener_impl.h | 1 + test/config/utility.cc | 21 + test/config/utility.h | 2 + .../quiche/envoy_quic_server_session_test.cc | 4 + .../common/passthrough_test.cc | 7 +- .../transport_sockets/starttls/BUILD | 26 ++ .../starttls/starttls_socket_test.cc | 127 ++++++ test/integration/BUILD | 24 ++ test/integration/starttls_integration_test.cc | 386 ++++++++++++++++++ .../starttls_integration_test.proto | 6 + test/integration/utility.h | 4 + test/mocks/network/connection.h | 2 + test/mocks/network/transport_socket.h | 1 + 42 files changed, 1120 insertions(+), 2 deletions(-) create mode 100644 api/envoy/extensions/transport_sockets/starttls/v3/BUILD create mode 100644 api/envoy/extensions/transport_sockets/starttls/v3/starttls.proto create mode 100644 api/envoy/extensions/transport_sockets/starttls/v4alpha/BUILD create mode 100644 api/envoy/extensions/transport_sockets/starttls/v4alpha/starttls.proto create mode 100644 generated_api_shadow/envoy/extensions/transport_sockets/starttls/v3/BUILD create mode 100644 generated_api_shadow/envoy/extensions/transport_sockets/starttls/v3/starttls.proto create mode 100644 generated_api_shadow/envoy/extensions/transport_sockets/starttls/v4alpha/BUILD create mode 100644 generated_api_shadow/envoy/extensions/transport_sockets/starttls/v4alpha/starttls.proto create mode 100644 source/extensions/transport_sockets/starttls/BUILD create mode 100644 source/extensions/transport_sockets/starttls/config.cc create mode 100644 source/extensions/transport_sockets/starttls/config.h create mode 100644 source/extensions/transport_sockets/starttls/starttls_socket.cc create mode 100644 source/extensions/transport_sockets/starttls/starttls_socket.h create mode 100644 test/extensions/transport_sockets/starttls/BUILD create mode 100644 test/extensions/transport_sockets/starttls/starttls_socket_test.cc create mode 100644 test/integration/starttls_integration_test.cc create mode 100644 test/integration/starttls_integration_test.proto diff --git a/CODEOWNERS b/CODEOWNERS index ee9e7f111b6e..4b3f453b6ccc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -44,6 +44,8 @@ extensions/filters/common/original_src @snowp @klarose /*/extensions/transport_sockets/proxy_protocol @alyssawilk @wez470 # common transport socket /*/extensions/transport_sockets/common @alyssawilk @wez470 +# starttls transport socket +/*/extensions/transport_sockets/starttls @cpakulski @lizan # sni_cluster extension /*/extensions/filters/network/sni_cluster @rshriram @lizan # sni_dynamic_forward_proxy extension diff --git a/api/BUILD b/api/BUILD index cc41b7c0d8aa..82a1592921ad 100644 --- a/api/BUILD +++ b/api/BUILD @@ -244,6 +244,7 @@ proto_library( "//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg", "//envoy/extensions/transport_sockets/quic/v3:pkg", "//envoy/extensions/transport_sockets/raw_buffer/v3:pkg", + "//envoy/extensions/transport_sockets/starttls/v3:pkg", "//envoy/extensions/transport_sockets/tap/v3:pkg", "//envoy/extensions/transport_sockets/tls/v3:pkg", "//envoy/extensions/upstreams/http/generic/v3:pkg", diff --git a/api/envoy/extensions/transport_sockets/starttls/v3/BUILD b/api/envoy/extensions/transport_sockets/starttls/v3/BUILD new file mode 100644 index 000000000000..7ae3c01a9947 --- /dev/null +++ b/api/envoy/extensions/transport_sockets/starttls/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/extensions/transport_sockets/raw_buffer/v3:pkg", + "//envoy/extensions/transport_sockets/tls/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/transport_sockets/starttls/v3/starttls.proto b/api/envoy/extensions/transport_sockets/starttls/v3/starttls.proto new file mode 100644 index 000000000000..d9da31e7c460 --- /dev/null +++ b/api/envoy/extensions/transport_sockets/starttls/v3/starttls.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.starttls.v3; + +import "envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.proto"; +import "envoy/extensions/transport_sockets/tls/v3/tls.proto"; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.starttls.v3"; +option java_outer_classname = "StarttlsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: StartTls] +// [#extension: envoy.transport_sockets.starttls] + +// StartTls transport socket addresses situations when a protocol starts in clear-text and +// negotiates an in-band switch to TLS. StartTls transport socket is protocol agnostic and requires +// a network filter which understands protocol exchange and a state machine to signal to the StartTls +// transport socket when a switch to TLS is required. + +// Configuration for StartTls transport socket. +// StartTls transport socket wraps two sockets: +// - raw_buffer socket which is used at the beginning of the session +// - TLS socket used when a protocol negotiates a switch to encrypted traffic. +message StartTlsConfig { + // (optional) Configuration for clear-text socket used at the beginning of the session. + raw_buffer.v3.RawBuffer cleartext_socket_config = 1; + + // Configuration for TLS socket. + transport_sockets.tls.v3.DownstreamTlsContext tls_socket_config = 2 + [(validate.rules).message = {required: true}]; +} diff --git a/api/envoy/extensions/transport_sockets/starttls/v4alpha/BUILD b/api/envoy/extensions/transport_sockets/starttls/v4alpha/BUILD new file mode 100644 index 000000000000..b160d85ddb5b --- /dev/null +++ b/api/envoy/extensions/transport_sockets/starttls/v4alpha/BUILD @@ -0,0 +1,14 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/extensions/transport_sockets/raw_buffer/v3:pkg", + "//envoy/extensions/transport_sockets/starttls/v3:pkg", + "//envoy/extensions/transport_sockets/tls/v4alpha:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/transport_sockets/starttls/v4alpha/starttls.proto b/api/envoy/extensions/transport_sockets/starttls/v4alpha/starttls.proto new file mode 100644 index 000000000000..32350cf7e1b6 --- /dev/null +++ b/api/envoy/extensions/transport_sockets/starttls/v4alpha/starttls.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.starttls.v4alpha; + +import "envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.proto"; +import "envoy/extensions/transport_sockets/tls/v4alpha/tls.proto"; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.starttls.v4alpha"; +option java_outer_classname = "StarttlsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: StartTls] +// [#extension: envoy.transport_sockets.starttls] + +// StartTls transport socket addresses situations when a protocol starts in clear-text and +// negotiates an in-band switch to TLS. StartTls transport socket is protocol agnostic and requires +// a network filter which understands protocol exchange and a state machine to signal to the StartTls +// transport socket when a switch to TLS is required. + +// Configuration for StartTls transport socket. +// StartTls transport socket wraps two sockets: +// - raw_buffer socket which is used at the beginning of the session +// - TLS socket used when a protocol negotiates a switch to encrypted traffic. +message StartTlsConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.transport_sockets.starttls.v3.StartTlsConfig"; + + // (optional) Configuration for clear-text socket used at the beginning of the session. + raw_buffer.v3.RawBuffer cleartext_socket_config = 1; + + // Configuration for TLS socket. + transport_sockets.tls.v4alpha.DownstreamTlsContext tls_socket_config = 2 + [(validate.rules).message = {required: true}]; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index d5b6098680bd..9b9ea97e97aa 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -127,6 +127,7 @@ proto_library( "//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg", "//envoy/extensions/transport_sockets/quic/v3:pkg", "//envoy/extensions/transport_sockets/raw_buffer/v3:pkg", + "//envoy/extensions/transport_sockets/starttls/v3:pkg", "//envoy/extensions/transport_sockets/tap/v3:pkg", "//envoy/extensions/transport_sockets/tls/v3:pkg", "//envoy/extensions/upstreams/http/generic/v3:pkg", diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 40b38e1c62ec..d29d29648df2 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -80,6 +80,7 @@ New Features :ref:`TlsCertificate ` and :ref:`CertificateValidationContext `. * signal: added an extension point for custom actions to run on the thread that has encountered a fatal error. Actions are configurable via :ref:`fatal_actions `. +* start_tls: :ref:`transport socket` which starts in clear-text but may programatically be converted to use tls. * tcp: added a new :ref:`envoy.overload_actions.reject_incoming_connections ` action to reject incoming TCP connections. * thrift_proxy: added a new :ref: `payload_passthrough ` option to skip decoding body in the Thrift message. * tls: added support for RSA certificates with 4096-bit keys in FIPS mode. diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v3/BUILD b/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v3/BUILD new file mode 100644 index 000000000000..7ae3c01a9947 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/extensions/transport_sockets/raw_buffer/v3:pkg", + "//envoy/extensions/transport_sockets/tls/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v3/starttls.proto b/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v3/starttls.proto new file mode 100644 index 000000000000..d9da31e7c460 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v3/starttls.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.starttls.v3; + +import "envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.proto"; +import "envoy/extensions/transport_sockets/tls/v3/tls.proto"; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.starttls.v3"; +option java_outer_classname = "StarttlsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: StartTls] +// [#extension: envoy.transport_sockets.starttls] + +// StartTls transport socket addresses situations when a protocol starts in clear-text and +// negotiates an in-band switch to TLS. StartTls transport socket is protocol agnostic and requires +// a network filter which understands protocol exchange and a state machine to signal to the StartTls +// transport socket when a switch to TLS is required. + +// Configuration for StartTls transport socket. +// StartTls transport socket wraps two sockets: +// - raw_buffer socket which is used at the beginning of the session +// - TLS socket used when a protocol negotiates a switch to encrypted traffic. +message StartTlsConfig { + // (optional) Configuration for clear-text socket used at the beginning of the session. + raw_buffer.v3.RawBuffer cleartext_socket_config = 1; + + // Configuration for TLS socket. + transport_sockets.tls.v3.DownstreamTlsContext tls_socket_config = 2 + [(validate.rules).message = {required: true}]; +} diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v4alpha/BUILD b/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v4alpha/BUILD new file mode 100644 index 000000000000..b160d85ddb5b --- /dev/null +++ b/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v4alpha/BUILD @@ -0,0 +1,14 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/extensions/transport_sockets/raw_buffer/v3:pkg", + "//envoy/extensions/transport_sockets/starttls/v3:pkg", + "//envoy/extensions/transport_sockets/tls/v4alpha:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v4alpha/starttls.proto b/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v4alpha/starttls.proto new file mode 100644 index 000000000000..32350cf7e1b6 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/transport_sockets/starttls/v4alpha/starttls.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.starttls.v4alpha; + +import "envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.proto"; +import "envoy/extensions/transport_sockets/tls/v4alpha/tls.proto"; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.starttls.v4alpha"; +option java_outer_classname = "StarttlsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: StartTls] +// [#extension: envoy.transport_sockets.starttls] + +// StartTls transport socket addresses situations when a protocol starts in clear-text and +// negotiates an in-band switch to TLS. StartTls transport socket is protocol agnostic and requires +// a network filter which understands protocol exchange and a state machine to signal to the StartTls +// transport socket when a switch to TLS is required. + +// Configuration for StartTls transport socket. +// StartTls transport socket wraps two sockets: +// - raw_buffer socket which is used at the beginning of the session +// - TLS socket used when a protocol negotiates a switch to encrypted traffic. +message StartTlsConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.transport_sockets.starttls.v3.StartTlsConfig"; + + // (optional) Configuration for clear-text socket used at the beginning of the session. + raw_buffer.v3.RawBuffer cleartext_socket_config = 1; + + // Configuration for TLS socket. + transport_sockets.tls.v4alpha.DownstreamTlsContext tls_socket_config = 2 + [(validate.rules).message = {required: true}]; +} diff --git a/include/envoy/network/connection.h b/include/envoy/network/connection.h index 01d20bfc1fca..d3a1c649cda9 100644 --- a/include/envoy/network/connection.h +++ b/include/envoy/network/connection.h @@ -328,6 +328,14 @@ class Connection : public Event::DeferredDeletable, public FilterManager { */ virtual absl::string_view transportFailureReason() const PURE; + /** + * Instructs the connection to start using secure transport. + * Note: Not all underlying transport sockets support such operation. + * @return boolean telling if underlying transport socket was able to + start secure transport. + */ + virtual bool startSecureTransport() PURE; + /** * @return absl::optional An optional of the most recent round-trip * time of the connection. If the platform does not support this, then an empty optional is diff --git a/include/envoy/network/transport_socket.h b/include/envoy/network/transport_socket.h index c820397b59cd..d90661beb7d6 100644 --- a/include/envoy/network/transport_socket.h +++ b/include/envoy/network/transport_socket.h @@ -146,6 +146,13 @@ class TransportSocket { * @return the const SSL connection data if this is an SSL connection, or nullptr if it is not. */ virtual Ssl::ConnectionInfoConstSharedPtr ssl() const PURE; + + /** + * Instructs a transport socket to start using secure transport. + * Note: Not all transport sockets support such operation. + * @return boolean indicating if the transport socket was able to start secure transport. + */ + virtual bool startSecureTransport() PURE; }; using TransportSocketPtr = std::unique_ptr; diff --git a/source/common/network/connection_impl.h b/source/common/network/connection_impl.h index bde423cff1ec..c7b630c4dec7 100644 --- a/source/common/network/connection_impl.h +++ b/source/common/network/connection_impl.h @@ -93,6 +93,7 @@ class ConnectionImpl : public ConnectionImplBase, public TransportSocketCallback StreamInfo::StreamInfo& streamInfo() override { return stream_info_; } const StreamInfo::StreamInfo& streamInfo() const override { return stream_info_; } absl::string_view transportFailureReason() const override; + bool startSecureTransport() override { return transport_socket_->startSecureTransport(); } absl::optional lastRoundTripTime() const override; // Network::FilterManagerConnection diff --git a/source/common/network/raw_buffer_socket.h b/source/common/network/raw_buffer_socket.h index 9a17fe35516b..24e498ebf59a 100644 --- a/source/common/network/raw_buffer_socket.h +++ b/source/common/network/raw_buffer_socket.h @@ -21,6 +21,7 @@ class RawBufferSocket : public TransportSocket, protected Logger::LoggableaddBytesSentCallback([upstream_callbacks = upstream_callbacks_](uint64_t) { + upstream_->addBytesSentCallback([upstream_callbacks = upstream_callbacks_](uint64_t) -> bool { upstream_callbacks->onBytesSent(); return true; }); diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 6ef0d25ca7cc..52f127950c64 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -180,6 +180,7 @@ EXTENSIONS = { "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", "envoy.transport_sockets.quic": "//source/extensions/quic_listeners/quiche:quic_factory_lib", + "envoy.transport_sockets.starttls": "//source/extensions/transport_sockets/starttls:config", # # Retry host predicates diff --git a/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h b/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h index 06ef6a9cb98b..53535c961423 100644 --- a/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h +++ b/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h @@ -90,6 +90,7 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase { StreamInfo::StreamInfo& streamInfo() override { return stream_info_; } const StreamInfo::StreamInfo& streamInfo() const override { return stream_info_; } absl::string_view transportFailureReason() const override { return transport_failure_reason_; } + bool startSecureTransport() override { return false; } absl::optional lastRoundTripTime() const override { return {}; } // Network::FilterManagerConnection diff --git a/source/extensions/transport_sockets/alts/tsi_socket.h b/source/extensions/transport_sockets/alts/tsi_socket.h index a4a7423bd927..76828a9c2b38 100644 --- a/source/extensions/transport_sockets/alts/tsi_socket.h +++ b/source/extensions/transport_sockets/alts/tsi_socket.h @@ -59,6 +59,7 @@ class TsiSocket : public Network::TransportSocket, absl::string_view failureReason() const override; bool canFlushClose() override { return handshake_complete_; } Envoy::Ssl::ConnectionInfoConstSharedPtr ssl() const override { return nullptr; } + bool startSecureTransport() override { return false; } Network::IoResult doWrite(Buffer::Instance& buffer, bool end_stream) override; void closeSocket(Network::ConnectionEvent event) override; Network::IoResult doRead(Buffer::Instance& buffer) override; diff --git a/source/extensions/transport_sockets/common/passthrough.h b/source/extensions/transport_sockets/common/passthrough.h index 5084d973a865..5c338af425a0 100644 --- a/source/extensions/transport_sockets/common/passthrough.h +++ b/source/extensions/transport_sockets/common/passthrough.h @@ -22,6 +22,8 @@ class PassthroughSocket : public Network::TransportSocket { Network::IoResult doWrite(Buffer::Instance& buffer, bool end_stream) override; void onConnected() override; Ssl::ConnectionInfoConstSharedPtr ssl() const override; + // startSecureTransport method should not be called for this transport socket. + bool startSecureTransport() override { return false; } protected: Network::TransportSocketPtr transport_socket_; diff --git a/source/extensions/transport_sockets/starttls/BUILD b/source/extensions/transport_sockets/starttls/BUILD new file mode 100644 index 000000000000..6269f165e5f3 --- /dev/null +++ b/source/extensions/transport_sockets/starttls/BUILD @@ -0,0 +1,53 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +# StartTls transport socket. + +envoy_extension_package() + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + security_posture = "robust_to_untrusted_downstream_and_upstream", + visibility = ["//visibility:public"], + deps = [ + ":starttls_socket_lib", + "//include/envoy/network:transport_socket_interface", + "//include/envoy/registry", + "//include/envoy/server:transport_socket_config_interface", + "//source/common/config:utility_lib", + "//source/extensions/transport_sockets:well_known_names", + "@envoy_api//envoy/extensions/transport_sockets/starttls/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", + ], +) + +envoy_cc_library( + name = "starttls_socket_lib", + srcs = ["starttls_socket.cc"], + hdrs = ["starttls_socket.h"], + external_deps = [ + "abseil_optional", + "abseil_synchronization", + "ssl", + ], + deps = [ + "//include/envoy/network:connection_interface", + "//include/envoy/network:transport_socket_interface", + "//include/envoy/stats:stats_macros", + "//source/common/buffer:buffer_lib", + "//source/common/common:assert_lib", + "//source/common/common:empty_string", + "//source/common/common:minimal_logger_lib", + "//source/common/common:thread_annotations", + "//source/extensions/transport_sockets:well_known_names", + "@envoy_api//envoy/extensions/transport_sockets/starttls/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/transport_sockets/starttls/config.cc b/source/extensions/transport_sockets/starttls/config.cc new file mode 100644 index 000000000000..6f5cd15f847a --- /dev/null +++ b/source/extensions/transport_sockets/starttls/config.cc @@ -0,0 +1,53 @@ +#include "extensions/transport_sockets/starttls/config.h" + +#include "envoy/extensions/transport_sockets/starttls/v3/starttls.pb.h" +#include "envoy/extensions/transport_sockets/starttls/v3/starttls.pb.validate.h" +#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" + +#include "common/config/utility.h" + +#include "extensions/transport_sockets/starttls/starttls_socket.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace StartTls { + +Network::TransportSocketFactoryPtr DownstreamStartTlsSocketFactory::createTransportSocketFactory( + const Protobuf::Message& message, Server::Configuration::TransportSocketFactoryContext& context, + const std::vector& server_names) { + const auto& outer_config = MessageUtil::downcastAndValidate< + const envoy::extensions::transport_sockets::starttls::v3::StartTlsConfig&>( + message, context.messageValidationVisitor()); + + auto& raw_socket_config_factory = Config::Utility::getAndCheckFactoryByName< + Server::Configuration::DownstreamTransportSocketConfigFactory>( + TransportSocketNames::get().RawBuffer); + + Network::TransportSocketFactoryPtr raw_socket_factory = + raw_socket_config_factory.createTransportSocketFactory(outer_config.cleartext_socket_config(), + context, server_names); + + auto& tls_socket_config_factory = Config::Utility::getAndCheckFactoryByName< + Server::Configuration::DownstreamTransportSocketConfigFactory>( + TransportSocketNames::get().Tls); + + Network::TransportSocketFactoryPtr tls_socket_factory = + tls_socket_config_factory.createTransportSocketFactory(outer_config.tls_socket_config(), + context, server_names); + + return std::make_unique(outer_config, std::move(raw_socket_factory), + std::move(tls_socket_factory)); +} + +ProtobufTypes::MessagePtr DownstreamStartTlsSocketFactory::createEmptyConfigProto() { + return std::make_unique(); +} + +REGISTER_FACTORY(DownstreamStartTlsSocketFactory, + Server::Configuration::DownstreamTransportSocketConfigFactory){"starttls"}; + +} // namespace StartTls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/starttls/config.h b/source/extensions/transport_sockets/starttls/config.h new file mode 100644 index 000000000000..eb1508ec7f47 --- /dev/null +++ b/source/extensions/transport_sockets/starttls/config.h @@ -0,0 +1,29 @@ +#pragma once + +#include "envoy/registry/registry.h" +#include "envoy/server/transport_socket_config.h" + +#include "extensions/transport_sockets/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace StartTls { + +class DownstreamStartTlsSocketFactory + : public Server::Configuration::DownstreamTransportSocketConfigFactory { +public: + Network::TransportSocketFactoryPtr + createTransportSocketFactory(const Protobuf::Message& config, + Server::Configuration::TransportSocketFactoryContext& context, + const std::vector& server_names) override; + ProtobufTypes::MessagePtr createEmptyConfigProto() override; + std::string name() const override { return TransportSocketNames::get().StartTls; } +}; + +DECLARE_FACTORY(DownstreamStartTlsSocketFactory); + +} // namespace StartTls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/starttls/starttls_socket.cc b/source/extensions/transport_sockets/starttls/starttls_socket.cc new file mode 100644 index 000000000000..84b4d5cde3dd --- /dev/null +++ b/source/extensions/transport_sockets/starttls/starttls_socket.cc @@ -0,0 +1,38 @@ +#include "extensions/transport_sockets/starttls/starttls_socket.h" + +#include + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace StartTls { + +// Switch clear-text to secure transport. +bool StartTlsSocket::startSecureTransport() { + if (!using_tls_) { + tls_socket_->setTransportSocketCallbacks(*callbacks_); + tls_socket_->onConnected(); + // TODO(cpakulski): deleting active_socket_ assumes + // that active_socket_ does not contain any buffered data. + // Currently, active_socket_ is initialized to raw_buffer, which does not + // buffer. If active_socket_ is initialized to a transport socket which + // does buffering, it should be flushed before destroying or + // flush should be called from destructor. + active_socket_ = std::move(tls_socket_); + using_tls_ = true; + } + return true; +} + +Network::TransportSocketPtr ServerStartTlsSocketFactory::createTransportSocket( + Network::TransportSocketOptionsSharedPtr transport_socket_options) const { + return std::make_unique( + config_, raw_socket_factory_->createTransportSocket(transport_socket_options), + tls_socket_factory_->createTransportSocket(transport_socket_options), + transport_socket_options); +} + +} // namespace StartTls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/starttls/starttls_socket.h b/source/extensions/transport_sockets/starttls/starttls_socket.h new file mode 100644 index 000000000000..782630f28ad1 --- /dev/null +++ b/source/extensions/transport_sockets/starttls/starttls_socket.h @@ -0,0 +1,93 @@ +#include "envoy/extensions/transport_sockets/starttls/v3/starttls.pb.h" +#include "envoy/extensions/transport_sockets/starttls/v3/starttls.pb.validate.h" +#include "envoy/network/transport_socket.h" +#include "envoy/stats/scope.h" +#include "envoy/stats/stats_macros.h" + +#include "common/buffer/buffer_impl.h" +#include "common/common/logger.h" + +#include "extensions/transport_sockets/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace StartTls { + +class StartTlsSocket : public Network::TransportSocket, Logger::Loggable { +public: + StartTlsSocket(const envoy::extensions::transport_sockets::starttls::v3::StartTlsConfig&, + Network::TransportSocketPtr raw_socket, // RawBufferSocket + Network::TransportSocketPtr tls_socket, // TlsSocket + const Network::TransportSocketOptionsSharedPtr&) + : active_socket_(std::move(raw_socket)), tls_socket_(std::move(tls_socket)) {} + + void setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) override { + active_socket_->setTransportSocketCallbacks(callbacks); + callbacks_ = &callbacks; + } + + std::string protocol() const override { return TransportProtocolNames::get().StartTls; } + + absl::string_view failureReason() const override { return active_socket_->failureReason(); } + + void onConnected() override { active_socket_->onConnected(); } + bool canFlushClose() override { return active_socket_->canFlushClose(); } + Ssl::ConnectionInfoConstSharedPtr ssl() const override { return active_socket_->ssl(); } + + void closeSocket(Network::ConnectionEvent event) override { + return active_socket_->closeSocket(event); + } + + Network::IoResult doRead(Buffer::Instance& buffer) override { + return active_socket_->doRead(buffer); + } + + Network::IoResult doWrite(Buffer::Instance& buffer, bool end_stream) override { + return active_socket_->doWrite(buffer, end_stream); + } + + // Method to enable TLS. + bool startSecureTransport() override; + +private: + // Socket used in all transport socket operations. + // initially it is set to use raw buffer socket but + // can be converted to use tls. + Network::TransportSocketPtr active_socket_; + // Secure transport socket. It will replace raw buffer socket + // when startSecureTransport is called. + Network::TransportSocketPtr tls_socket_; + + Network::TransportSocketCallbacks* callbacks_{}; + + bool using_tls_{false}; +}; + +class ServerStartTlsSocketFactory : public Network::TransportSocketFactory, + Logger::Loggable { +public: + ~ServerStartTlsSocketFactory() override = default; + + ServerStartTlsSocketFactory( + const envoy::extensions::transport_sockets::starttls::v3::StartTlsConfig& config, + Network::TransportSocketFactoryPtr raw_socket_factory, + Network::TransportSocketFactoryPtr tls_socket_factory) + : raw_socket_factory_(std::move(raw_socket_factory)), + tls_socket_factory_(std::move(tls_socket_factory)), config_(config) {} + + Network::TransportSocketPtr + createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override; + bool implementsSecureTransport() const override { return false; } + bool usesProxyProtocolOptions() const override { return false; } + +private: + Network::TransportSocketFactoryPtr raw_socket_factory_; + Network::TransportSocketFactoryPtr tls_socket_factory_; + envoy::extensions::transport_sockets::starttls::v3::StartTlsConfig config_; +}; + +} // namespace StartTls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index e9a516cdf121..ba408c2d417f 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -42,6 +42,7 @@ class NotReadySslSocket : public Network::TransportSocket { } void onConnected() override {} Ssl::ConnectionInfoConstSharedPtr ssl() const override { return nullptr; } + bool startSecureTransport() override { return false; } }; } // namespace diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index 81b4712979fd..08c78a0ec8fe 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -62,6 +62,7 @@ class SslSocket : public Network::TransportSocket, Network::IoResult doWrite(Buffer::Instance& write_buffer, bool end_stream) override; void onConnected() override; Ssl::ConnectionInfoConstSharedPtr ssl() const override; + bool startSecureTransport() override { return false; } // Ssl::PrivateKeyConnectionCallbacks void onPrivateKeyMethodComplete() override; // Ssl::HandshakeCallbacks diff --git a/source/extensions/transport_sockets/well_known_names.h b/source/extensions/transport_sockets/well_known_names.h index 471e1e8b60cf..ef6e85361fee 100644 --- a/source/extensions/transport_sockets/well_known_names.h +++ b/source/extensions/transport_sockets/well_known_names.h @@ -20,6 +20,7 @@ class TransportSocketNameValues { const std::string Tap = "envoy.transport_sockets.tap"; const std::string Tls = "envoy.transport_sockets.tls"; const std::string UpstreamProxyProtocol = "envoy.transport_sockets.upstream_proxy_protocol"; + const std::string StartTls = "envoy.transport_sockets.starttls"; }; using TransportSocketNames = ConstSingleton; @@ -32,6 +33,7 @@ class TransportProtocolNameValues { const std::string Tls = "tls"; const std::string RawBuffer = "raw_buffer"; const std::string Quic = "quic"; + const std::string StartTls = "starttls"; }; // TODO(lizan): Find a better place to have this singleton. diff --git a/source/server/api_listener_impl.h b/source/server/api_listener_impl.h index e56fec57d73b..bbda83bb6559 100644 --- a/source/server/api_listener_impl.h +++ b/source/server/api_listener_impl.h @@ -141,6 +141,7 @@ class ApiListenerImplBase : public ApiListener, const StreamInfo::StreamInfo& streamInfo() const override { return stream_info_; } void setDelayedCloseTimeout(std::chrono::milliseconds) override {} absl::string_view transportFailureReason() const override { return EMPTY_STRING; } + bool startSecureTransport() override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } absl::optional lastRoundTripTime() const override { return {}; }; SyntheticReadCallbacks& parent_; diff --git a/test/config/utility.cc b/test/config/utility.cc index a13793bf1222..115201d53095 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -116,6 +116,27 @@ std::string ConfigHelper::tcpProxyConfig() { )EOF"); } +std::string ConfigHelper::startTlsConfig() { + return absl::StrCat( + tcpProxyConfig(), + fmt::format(R"EOF( + transport_socket: + name: "starttls" + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.starttls.v3.StartTlsConfig + cleartext_socket_config: + tls_socket_config: + common_tls_context: + tls_certificates: + certificate_chain: + filename: {} + private_key: + filename: {} +)EOF", + TestEnvironment::runfilesPath("test/config/integration/certs/servercert.pem"), + TestEnvironment::runfilesPath("test/config/integration/certs/serverkey.pem"))); +} + std::string ConfigHelper::tlsInspectorFilter() { return R"EOF( name: "envoy.filters.listener.tls_inspector" diff --git a/test/config/utility.h b/test/config/utility.h index 2cc176498495..ade1ce1b2d87 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -115,6 +115,8 @@ class ConfigHelper { static std::string defaultHealthCheckFilter(); // A string for a squash filter which can be used with addFilter() static std::string defaultSquashFilter(); + // A string for startTls transport socket config. + static std::string startTlsConfig(); // Configuration for L7 proxying, with clusters cluster_1 and cluster_2 meant to be added via CDS. // api_type should be REST, GRPC, or DELTA_GRPC. diff --git a/test/extensions/quic_listeners/quiche/envoy_quic_server_session_test.cc b/test/extensions/quic_listeners/quiche/envoy_quic_server_session_test.cc index 4fc37685788a..afd6adb3ff42 100644 --- a/test/extensions/quic_listeners/quiche/envoy_quic_server_session_test.cc +++ b/test/extensions/quic_listeners/quiche/envoy_quic_server_session_test.cc @@ -294,6 +294,10 @@ TEST_P(EnvoyQuicServerSessionTest, NewStream) { quic::VersionUsesHttp3(quic_version_[0].transport_version) ? 4u : 5u; auto stream = reinterpret_cast(envoy_quic_session_.GetOrCreateStream(stream_id)); + + // Basic checks. + ASSERT_FALSE(envoy_quic_session_.startSecureTransport()); + // Receive a GET request on created stream. quic::QuicHeaderList headers; headers.OnHeaderBlockStart(); diff --git a/test/extensions/transport_sockets/common/passthrough_test.cc b/test/extensions/transport_sockets/common/passthrough_test.cc index 3f3164a4df84..cc983e0900e5 100644 --- a/test/extensions/transport_sockets/common/passthrough_test.cc +++ b/test/extensions/transport_sockets/common/passthrough_test.cc @@ -83,7 +83,12 @@ TEST_F(PassthroughTest, SslDefersToInnerSocket) { passthrough_socket_->ssl(); } +// Test invoking startSecureTransport. +TEST_F(PassthroughTest, FailOnStartSecureTransport) { + EXPECT_FALSE(passthrough_socket_->startSecureTransport()); +} + } // namespace } // namespace TransportSockets } // namespace Extensions -} // namespace Envoy \ No newline at end of file +} // namespace Envoy diff --git a/test/extensions/transport_sockets/starttls/BUILD b/test/extensions/transport_sockets/starttls/BUILD new file mode 100644 index 000000000000..6f114205648a --- /dev/null +++ b/test/extensions/transport_sockets/starttls/BUILD @@ -0,0 +1,26 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_extension_cc_test( + name = "starttls_socket_tests", + srcs = [ + "starttls_socket_test.cc", + ], + extension_name = "envoy.transport_sockets.starttls", + deps = [ + "//source/common/network:transport_socket_options_lib", + "//source/extensions/transport_sockets/starttls:config", + "//test/mocks/network:network_mocks", + "@envoy_api//envoy/extensions/transport_sockets/starttls/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/transport_sockets/starttls/starttls_socket_test.cc b/test/extensions/transport_sockets/starttls/starttls_socket_test.cc new file mode 100644 index 000000000000..bac41cb5f149 --- /dev/null +++ b/test/extensions/transport_sockets/starttls/starttls_socket_test.cc @@ -0,0 +1,127 @@ +#include +#include + +#include "envoy/extensions/transport_sockets/starttls/v3/starttls.pb.h" +#include "envoy/extensions/transport_sockets/starttls/v3/starttls.pb.validate.h" +#include "envoy/network/connection.h" + +#include "common/network/transport_socket_options_impl.h" + +#include "extensions/transport_sockets/starttls/starttls_socket.h" + +#include "test/mocks/network/mocks.h" +#include "test/mocks/network/transport_socket.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace StartTls { + +using testing::_; + +class StartTlsTransportSocketMock : public Network::MockTransportSocket { +public: + MOCK_METHOD(void, Die, ()); + ~StartTlsTransportSocketMock() override { Die(); } +}; + +TEST(StartTlsTest, BasicSwitch) { + const envoy::extensions::transport_sockets::starttls::v3::StartTlsConfig config; + Network::TransportSocketOptionsSharedPtr options = + std::make_shared(); + NiceMock transport_callbacks; + NiceMock* raw_socket = new NiceMock; + Network::MockTransportSocket* ssl_socket = new Network::MockTransportSocket; + Buffer::OwnedImpl buf; + + std::unique_ptr socket = + std::make_unique(config, Network::TransportSocketPtr(raw_socket), + Network::TransportSocketPtr(ssl_socket), options); + socket->setTransportSocketCallbacks(transport_callbacks); + + // StartTls socket is initial clear-text state. All calls should be forwarded to raw socket. + ASSERT_THAT(socket->protocol(), TransportProtocolNames::get().StartTls); + EXPECT_CALL(*raw_socket, onConnected()); + EXPECT_CALL(*ssl_socket, onConnected()).Times(0); + socket->onConnected(); + + EXPECT_CALL(*raw_socket, failureReason()); + EXPECT_CALL(*ssl_socket, failureReason()).Times(0); + socket->failureReason(); + + EXPECT_CALL(*raw_socket, canFlushClose()); + EXPECT_CALL(*ssl_socket, canFlushClose()).Times(0); + socket->canFlushClose(); + + EXPECT_CALL(*raw_socket, ssl()); + EXPECT_CALL(*ssl_socket, ssl()).Times(0); + socket->ssl(); + + EXPECT_CALL(*raw_socket, closeSocket(Network::ConnectionEvent::RemoteClose)); + EXPECT_CALL(*ssl_socket, closeSocket(Network::ConnectionEvent::RemoteClose)).Times(0); + socket->closeSocket(Network::ConnectionEvent::RemoteClose); + + EXPECT_CALL(*raw_socket, doRead(_)); + EXPECT_CALL(*ssl_socket, doRead(_)).Times(0); + socket->doRead(buf); + + EXPECT_CALL(*raw_socket, doWrite(_, true)); + EXPECT_CALL(*ssl_socket, doWrite(_, true)).Times(0); + socket->doWrite(buf, true); + + // Now switch to Tls. During the switch, the new socket should register for callbacks + // and connect. + EXPECT_CALL(*ssl_socket, setTransportSocketCallbacks(_)); + EXPECT_CALL(*ssl_socket, onConnected); + // Make sure that raw socket is destructed. + EXPECT_CALL(*raw_socket, Die); + socket->startSecureTransport(); + + // Calling again should do nothing: No subsequent registration for callbacks + // and no onConnected. + socket->startSecureTransport(); + + // Now calls to all methods should be forwarded to ssl_socket. + // raw_socket has been destructed when switch to tls happened. + ASSERT_THAT(socket->protocol(), TransportProtocolNames::get().StartTls); + EXPECT_CALL(*ssl_socket, onConnected()); + socket->onConnected(); + + EXPECT_CALL(*ssl_socket, failureReason()); + socket->failureReason(); + + EXPECT_CALL(*ssl_socket, canFlushClose()); + socket->canFlushClose(); + + EXPECT_CALL(*ssl_socket, ssl()); + socket->ssl(); + + EXPECT_CALL(*ssl_socket, closeSocket(Network::ConnectionEvent::RemoteClose)); + socket->closeSocket(Network::ConnectionEvent::RemoteClose); + + EXPECT_CALL(*ssl_socket, doRead(_)); + socket->doRead(buf); + + EXPECT_CALL(*ssl_socket, doWrite(_, true)); + socket->doWrite(buf, true); +} + +// Factory test. +TEST(StartTls, BasicFactoryTest) { + const envoy::extensions::transport_sockets::starttls::v3::StartTlsConfig config; + NiceMock* raw_buffer_factory = + new NiceMock; + NiceMock* ssl_factory = + new NiceMock; + std::unique_ptr factory = + std::make_unique( + config, Network::TransportSocketFactoryPtr(raw_buffer_factory), + Network::TransportSocketFactoryPtr(ssl_factory)); + ASSERT_FALSE(factory->implementsSecureTransport()); + ASSERT_FALSE(factory->usesProxyProtocolOptions()); +} + +} // namespace StartTls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/test/integration/BUILD b/test/integration/BUILD index 94e78b9c5e4a..d433a40a9d30 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1216,6 +1216,30 @@ envoy_cc_test( ], ) +envoy_proto_library( + name = "starttls_integration_proto", + srcs = [":starttls_integration_test.proto"], +) + +envoy_cc_test( + name = "starttls_integration_test", + srcs = [ + "starttls_integration_test.cc", + ], + data = [ + "//test/config/integration/certs", + ], + deps = [ + ":integration_lib", + ":starttls_integration_proto_cc_proto", + "//source/extensions/filters/network/tcp_proxy:config", + "//source/extensions/transport_sockets/raw_buffer:config", + "//source/extensions/transport_sockets/starttls:config", + "//test/test_common:registry_lib", + "@envoy_api//envoy/extensions/transport_sockets/raw_buffer/v3:pkg_cc_proto", + ], +) + envoy_cc_test( name = "tcp_tunneling_integration_test", srcs = [ diff --git a/test/integration/starttls_integration_test.cc b/test/integration/starttls_integration_test.cc new file mode 100644 index 000000000000..b4468726bbb7 --- /dev/null +++ b/test/integration/starttls_integration_test.cc @@ -0,0 +1,386 @@ +#include "envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.pb.h" +#include "envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.pb.validate.h" +#include "envoy/network/filter.h" +#include "envoy/server/filter_config.h" + +#include "common/network/connection_impl.h" + +#include "extensions/filters/network/common/factory_base.h" +#include "extensions/transport_sockets/raw_buffer/config.h" + +#include "test/config/utility.h" +#include "test/integration/integration.h" +#include "test/integration/ssl_utility.h" +#include "test/integration/starttls_integration_test.pb.h" +#include "test/integration/starttls_integration_test.pb.validate.h" +#include "test/test_common/registry.h" + +#include "gtest/gtest.h" + +namespace Envoy { + +// Simple filter for test purposes. This filter will be injected into the filter chain during +// tests. The filter reacts only to few keywords. If received payload does not contain +// allowed keyword, filter will stop iteration. +// The filter will be configured to sit on top of tcp_proxy and use start-tls transport socket. +// If it receives a data which is not known keyword it means that transport socket has not been +// successfully converted to use TLS and filter receives either encrypted data or TLS handshake +// messages. +class StartTlsSwitchFilter : public Network::Filter { +public: + // Network::ReadFilter + Network::FilterStatus onData(Buffer::Instance& data, bool end_stream) override; + Network::FilterStatus onNewConnection() override; + void initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) override { + read_callbacks_ = &callbacks; + } + + // Network::WriteFilter + Network::FilterStatus onWrite(Buffer::Instance& data, bool end_stream) override; + void initializeWriteFilterCallbacks(Network::WriteFilterCallbacks& callbacks) override { + write_callbacks_ = &callbacks; + } + +private: + Network::FilterStatus onCommand(Buffer::Instance& buf, bool write_back); + + Network::ReadFilterCallbacks* read_callbacks_{}; + Network::WriteFilterCallbacks* write_callbacks_{}; + // Filter will allow only the following messages to pass. + std::set allowed_messages_{"hello", "switch", "hola", "usetls", "bye"}; +}; + +Network::FilterStatus StartTlsSwitchFilter::onNewConnection() { + return Network::FilterStatus::Continue; +} + +Network::FilterStatus StartTlsSwitchFilter::onCommand(Buffer::Instance& buf, bool write_back) { + const std::string message = buf.toString(); + bool stopIteration = false; + + // Skip empty messages. + if (message.empty()) { + return Network::FilterStatus::Continue; + } + + // Stop processing if unrecognized message has been received. + if (allowed_messages_.find(message) == allowed_messages_.end()) { + return Network::FilterStatus::StopIteration; + } + + if (message == "switch") { + buf.drain(buf.length()); + buf.add("usetls"); + read_callbacks_->connection().addBytesSentCallback([=](uint64_t bytes) -> bool { + // Wait until 6 bytes long "usetls" has been sent. + if (bytes >= 6) { + read_callbacks_->connection().startSecureTransport(); + // Unsubscribe the callback. + // Switch to tls has been completed. + return false; + } + return true; + }); + if (write_back) { + read_callbacks_->connection().write(buf, false); + stopIteration = true; + } + } + return stopIteration ? Network::FilterStatus::StopIteration : Network::FilterStatus::Continue; +} + +// onData method processes payloads sent by downstream client. +// Allowed messages are passed upstream. +// When "switch" keyword is received, the filter stops filter iteration +// and sends to the client "usetls" message. +// +Network::FilterStatus StartTlsSwitchFilter::onData(Buffer::Instance& buf, bool) { + return onCommand(buf, true); +} + +// onWrite method processes payloads sent by upstream to the client. +// The logic here reacts to keyword "switch". It replaces the payload +// with keyword "usetls" and adds a callback to be called when sending payload +// to the client completes. +Network::FilterStatus StartTlsSwitchFilter::onWrite(Buffer::Instance& buf, bool) { + return onCommand(buf, false); +} + +// Config factory for StartTlsSwitchFilter. +class StartTlsSwitchFilterConfigFactory : public Extensions::NetworkFilters::Common::FactoryBase< + test::integration::starttls::StartTlsFilterConfig> { +public: + explicit StartTlsSwitchFilterConfigFactory(const std::string& name) : FactoryBase(name) {} + + Network::FilterFactoryCb + createFilterFactoryFromProtoTyped(const test::integration::starttls::StartTlsFilterConfig&, + Server::Configuration::FactoryContext&) override { + return [](Network::FilterManager& filter_manager) -> void { + filter_manager.addFilter(std::make_shared()); + }; + } + + std::string name() const override { return name_; } + +private: + const std::string name_; +}; + +// ClientTestConnection is used for simulating a client +// which initiates a connection to Envoy in clear-text and then switches to TLS +// without closing the socket. +class ClientTestConnection : public Network::ClientConnectionImpl { +public: + ClientTestConnection(Event::Dispatcher& dispatcher, + const Network::Address::InstanceConstSharedPtr& remote_address, + const Network::Address::InstanceConstSharedPtr& source_address, + Network::TransportSocketPtr&& transport_socket, + const Network::ConnectionSocket::OptionsSharedPtr& options) + : ClientConnectionImpl(dispatcher, remote_address, source_address, + std::move(transport_socket), options) {} + + void setTransportSocket(Network::TransportSocketPtr&& transport_socket) { + transport_socket_ = std::move(transport_socket); + transport_socket_->setTransportSocketCallbacks(*this); + + // Reset connection's state machine. + connecting_ = true; + + // Issue event which will trigger TLS handshake. + ioHandle().activateFileEvents(Event::FileReadyType::Write); + } +}; + +// Fixture class for integration tests. +class StartTlsIntegrationTest : public testing::TestWithParam, + public BaseIntegrationTest { +public: + StartTlsIntegrationTest() : BaseIntegrationTest(GetParam(), ConfigHelper::startTlsConfig()) {} + void initialize() override; + void addStartTlsSwitchFilter(ConfigHelper& config_helper); + + // Contexts needed by raw buffer and tls transport sockets. + std::unique_ptr tls_context_manager_; + Network::TransportSocketFactoryPtr tls_context_; + Network::TransportSocketFactoryPtr cleartext_context_; + + MockWatermarkBuffer* client_write_buffer_{nullptr}; + ConnectionStatusCallbacks connect_callbacks_; + + // Config factory for StartTlsSwitchFilter. + StartTlsSwitchFilterConfigFactory config_factory_{"startTls"}; + Registry::InjectFactory + registered_config_factory_{config_factory_}; + + std::unique_ptr conn_; + std::shared_ptr payload_reader_; +}; + +void StartTlsIntegrationTest::initialize() { + EXPECT_CALL(*mock_buffer_factory_, create_(_, _, _)) + // Connection constructor will first create write buffer. + // Test tracks how many bytes are sent. + .WillOnce(Invoke([&](std::function below_low, std::function above_high, + std::function above_overflow) -> Buffer::Instance* { + client_write_buffer_ = + new NiceMock(below_low, above_high, above_overflow); + ON_CALL(*client_write_buffer_, move(_)) + .WillByDefault(Invoke(client_write_buffer_, &MockWatermarkBuffer::baseMove)); + ON_CALL(*client_write_buffer_, drain(_)) + .WillByDefault(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackDrains)); + return client_write_buffer_; + })) + // Connection constructor will also create read buffer, but the test does + // not track received bytes. + .WillOnce(Invoke([&](std::function below_low, std::function above_high, + std::function above_overflow) -> Buffer::Instance* { + return new Buffer::WatermarkBuffer(below_low, above_high, above_overflow); + })); + config_helper_.renameListener("tcp_proxy"); + addStartTlsSwitchFilter(config_helper_); + + // Setup factories and contexts for upstream clear-text raw buffer transport socket. + auto config = std::make_unique(); + + auto factory = + std::make_unique(); + cleartext_context_ = Network::TransportSocketFactoryPtr{ + factory->createTransportSocketFactory(*config, factory_context_)}; + + // Setup factories and contexts for tls transport socket. + tls_context_manager_ = + std::make_unique(timeSystem()); + tls_context_ = Ssl::createClientSslTransportSocketFactory({}, *tls_context_manager_, *api_); + payload_reader_ = std::make_shared(*dispatcher_); + + BaseIntegrationTest::initialize(); + + Network::Address::InstanceConstSharedPtr address = + Ssl::getSslAddress(version_, lookupPort("tcp_proxy")); + conn_ = std::make_unique( + *dispatcher_, address, Network::Address::InstanceConstSharedPtr(), + cleartext_context_->createTransportSocket( + std::make_shared( + absl::string_view(""), std::vector(), std::vector())), + nullptr); + + conn_->enableHalfClose(true); + conn_->addConnectionCallbacks(connect_callbacks_); + conn_->addReadFilter(payload_reader_); +} + +// Method adds StartTlsSwitchFilter into the filter chain. +// The filter is required to instruct StartTls transport +// socket to start using Tls. +void StartTlsIntegrationTest::addStartTlsSwitchFilter(ConfigHelper& config_helper) { + config_helper.addNetworkFilter(R"EOF( + name: startTls + typed_config: + "@type": type.googleapis.com/test.integration.starttls.StartTlsFilterConfig + )EOF"); + // double-check the filter was actually added + config_helper.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + ASSERT_EQ("startTls", + bootstrap.static_resources().listeners(0).filter_chains(0).filters(0).name()); + }); +} + +// Test creates a clear-text connection from a client to Envoy and sends several messages. +// Then a special message is sent, which causes StartTlsSwitchFilter to +// instruct StartTls transport socket to start using tls. +// The Client connection starts using tls, performs tls handshake and few messages +// are sent over tls. start-tls transport socket de-crypts the messages and forwards them +// upstream in clear-text.. +TEST_P(StartTlsIntegrationTest, SwitchToTlsFromClient) { + initialize(); + + // Open clear-text connection. + conn_->connect(); + + FakeRawConnectionPtr fake_upstream_connection; + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + ASSERT_THAT(test_server_->server().listenerManager().numConnections(), 1); + + Buffer::OwnedImpl buffer; + buffer.add("hello"); + conn_->write(buffer, false); + while (client_write_buffer_->bytesDrained() != 5) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + // Make sure the data makes it upstream. + ASSERT_TRUE(fake_upstream_connection->waitForData(5)); + + // Send a message to switch to tls on the receiver side. + // StartTlsSwitchFilter will switch transport socket on the + // receiver side upon receiving "switch" message. + buffer.add("switch"); + conn_->write(buffer, false); + while (client_write_buffer_->bytesDrained() != 11) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + + // Wait for confirmation + payload_reader_->set_data_to_wait_for("usetls"); + dispatcher_->run(Event::Dispatcher::RunType::Block); + + // Without closing the connection, switch to tls. + conn_->setTransportSocket( + tls_context_->createTransportSocket(std::make_shared( + absl::string_view(""), std::vector(), + std::vector{"envoyalpn"}))); + connect_callbacks_.reset(); + while (!connect_callbacks_.connected() && !connect_callbacks_.closed()) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + + // Send few messages over encrypted connection. + buffer.add("hola"); + conn_->write(buffer, false); + while (client_write_buffer_->bytesDrained() != 15) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + // Make sure the data makes it upstream. + ASSERT_TRUE(fake_upstream_connection->waitForData(9)); + + buffer.add("bye"); + conn_->write(buffer, false); + while (client_write_buffer_->bytesDrained() != 18) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + // Make sure the data makes it upstream. + ASSERT_TRUE(fake_upstream_connection->waitForData(12)); + + conn_->close(Network::ConnectionCloseType::FlushWrite); +} + +// The test creates a clear-text connection from the client to Envoy. +// The client sends some messages to upstream. +// Then upstream sends a special message called "switch". +// The filter reacts to that message and instructs an underlying +// start-tls transport socket to switch to tls. +// The client starts tls handshake and few additional messages are passed. +// The messages are decrypt-ed at start-tls transport socket and forwarded +// in clear-text upstream. +TEST_P(StartTlsIntegrationTest, SwitchToTlsFromUpstream) { + initialize(); + + // Open clear-text connection. + conn_->connect(); + + FakeRawConnectionPtr fake_upstream_connection; + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + ASSERT_THAT(test_server_->server().listenerManager().numConnections(), 1); + + Buffer::OwnedImpl buffer; + buffer.add("hello"); + conn_->write(buffer, false); + while (client_write_buffer_->bytesDrained() != 5) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + // Make sure the data makes it upstream. + ASSERT_TRUE(fake_upstream_connection->waitForData(5)); + + // Send a command to switch to tls from upstream. + // As it passes through filter, the message will cause + // the filter to signal to start-tls transport socket + // to use tls. + const std::string data("switch"); + ASSERT_TRUE(fake_upstream_connection->write(data, false)); + + // Wait for confirmation + payload_reader_->set_data_to_wait_for("usetls"); + dispatcher_->run(Event::Dispatcher::RunType::Block); + + // Without closing the connection, switch to tls. + conn_->setTransportSocket( + tls_context_->createTransportSocket(std::make_shared( + absl::string_view(""), std::vector(), + std::vector{"envoyalpn"}))); + connect_callbacks_.reset(); + while (!connect_callbacks_.connected() && !connect_callbacks_.closed()) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + + // Send few messages over encrypted connection. + buffer.add("hola"); + conn_->write(buffer, false); + while (client_write_buffer_->bytesDrained() != 9) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + // Make sure the data makes it upstream. + ASSERT_TRUE(fake_upstream_connection->waitForData(9)); + + buffer.add("bye"); + conn_->write(buffer, false); + while (client_write_buffer_->bytesDrained() != 12) { + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } + // Make sure the data makes it upstream. + ASSERT_TRUE(fake_upstream_connection->waitForData(12)); + conn_->close(Network::ConnectionCloseType::FlushWrite); +} + +INSTANTIATE_TEST_SUITE_P(StartTlsIntegrationTestSuite, StartTlsIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); + +} // namespace Envoy diff --git a/test/integration/starttls_integration_test.proto b/test/integration/starttls_integration_test.proto new file mode 100644 index 000000000000..9c0164002e02 --- /dev/null +++ b/test/integration/starttls_integration_test.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; + +package test.integration.starttls; + +message StartTlsFilterConfig { +} diff --git a/test/integration/utility.h b/test/integration/utility.h index cad8b153363d..afe3df9a3de2 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -191,6 +191,10 @@ class ConnectionStatusCallbacks : public Network::ConnectionCallbacks { public: bool connected() const { return connected_; } bool closed() const { return closed_; } + void reset() { + connected_ = false; + closed_ = false; + } // Network::ConnectionCallbacks void onEvent(Network::ConnectionEvent event) override { diff --git a/test/mocks/network/connection.h b/test/mocks/network/connection.h index 2bf02ccfdc6a..45cac5fcd780 100644 --- a/test/mocks/network/connection.h +++ b/test/mocks/network/connection.h @@ -86,6 +86,7 @@ class MockConnectionBase { MOCK_METHOD(const StreamInfo::StreamInfo&, streamInfo, (), (const)); \ MOCK_METHOD(void, setDelayedCloseTimeout, (std::chrono::milliseconds)); \ MOCK_METHOD(absl::string_view, transportFailureReason, (), (const)); \ + MOCK_METHOD(bool, startSecureTransport, ()); \ MOCK_METHOD(absl::optional, lastRoundTripTime, (), (const)) class MockConnection : public Connection, public MockConnectionBase { @@ -170,6 +171,7 @@ class MockFilterManagerConnection : public FilterManagerConnection, public MockC MOCK_METHOD(const StreamInfo::StreamInfo&, streamInfo, (), (const)); MOCK_METHOD(void, setDelayedCloseTimeout, (std::chrono::milliseconds)); MOCK_METHOD(absl::string_view, transportFailureReason, (), (const)); + MOCK_METHOD(bool, startSecureTransport, ()); MOCK_METHOD(absl::optional, lastRoundTripTime, (), (const)); // Network::FilterManagerConnection diff --git a/test/mocks/network/transport_socket.h b/test/mocks/network/transport_socket.h index ed8fa15b7be3..7fdf69d98781 100644 --- a/test/mocks/network/transport_socket.h +++ b/test/mocks/network/transport_socket.h @@ -26,6 +26,7 @@ class MockTransportSocket : public TransportSocket { MOCK_METHOD(IoResult, doWrite, (Buffer::Instance & buffer, bool end_stream)); MOCK_METHOD(void, onConnected, ()); MOCK_METHOD(Ssl::ConnectionInfoConstSharedPtr, ssl, (), (const)); + MOCK_METHOD(bool, startSecureTransport, ()); TransportSocketCallbacks* callbacks_{}; }; From ac344a7d1b6ee10561ad1b4de86fc36176c46a6e Mon Sep 17 00:00:00 2001 From: asraa Date: Sun, 13 Dec 2020 17:05:20 -0500 Subject: [PATCH 17/49] [http] Fix H/1 test issue in codec fuzzer (#14379) Signed-off-by: Asra Ali --- test/common/http/BUILD | 1 + ...ized-codec_impl_fuzz_test-5686080614694912 | 94 +++++++++++++++++++ test/common/http/codec_impl_fuzz_test.cc | 19 +++- test/common/http/conn_manager_utility_test.cc | 73 -------------- test/integration/integration_test.cc | 32 +++++++ test/mocks/http/BUILD | 1 + test/mocks/http/mocks.h | 76 +++++++++++++++ 7 files changed, 218 insertions(+), 78 deletions(-) create mode 100644 test/common/http/codec_impl_corpus/clusterfuzz-testcase-minimized-codec_impl_fuzz_test-5686080614694912 diff --git a/test/common/http/BUILD b/test/common/http/BUILD index c821897bb1e1..4f415a518ba3 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -86,6 +86,7 @@ envoy_cc_fuzz_test( corpus = "codec_impl_corpus", deps = [ ":codec_impl_fuzz_proto_cc_proto", + "//source/common/http:conn_manager_lib", "//source/common/http:header_map_lib", "//source/common/http/http1:codec_lib", "//source/common/http/http2:codec_lib", diff --git a/test/common/http/codec_impl_corpus/clusterfuzz-testcase-minimized-codec_impl_fuzz_test-5686080614694912 b/test/common/http/codec_impl_corpus/clusterfuzz-testcase-minimized-codec_impl_fuzz_test-5686080614694912 new file mode 100644 index 000000000000..bb32d1da4c1a --- /dev/null +++ b/test/common/http/codec_impl_corpus/clusterfuzz-testcase-minimized-codec_impl_fuzz_test-5686080614694912 @@ -0,0 +1,94 @@ +actions { + new_stream { + request_headers { + headers { + key: ":method" + value: "GET" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":scheme" + value: ",ttp" + } + headers { + key: ":authority" + value: "foo.com" + } + headers { + key: "connection" + value: "upgrade" + } + } + } +} +actions { + client_drain { + } +} +actions { + quiesce_drain { + } +} +actions { + stream_action { + request { + data: 54 + } + dispatching_action { + data: 1 + } + } +} +actions { + stream_action { + response { + headers { + headers { + key: "connection" + value: "upgrade" + } + headers { + key: "upgrade" + value: "WebSocket" + } + } + } + } +} +actions { + stream_action { + response { + trailers { + headers { + key: ":method" + value: "GET" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":scheme" + value: "http" + } + } + } + } +} +actions { + stream_action { + request { + trailers { + } + } + } +} +actions { + new_stream { + request_headers { + } + } +} \ No newline at end of file diff --git a/test/common/http/codec_impl_fuzz_test.cc b/test/common/http/codec_impl_fuzz_test.cc index 6f9ee8c57a4b..d0fbdca11a4f 100644 --- a/test/common/http/codec_impl_fuzz_test.cc +++ b/test/common/http/codec_impl_fuzz_test.cc @@ -15,6 +15,7 @@ #include "common/http/header_map_impl.h" #include "common/http/http1/codec_impl.h" #include "common/http/http2/codec_impl.h" +#include "common/http/conn_manager_utility.h" #include "test/common/http/codec_impl_fuzz.pb.validate.h" #include "test/common/http/http2/codec_impl_test_util.h" @@ -110,6 +111,7 @@ class HttpStream : public LinkedObject { // TODO(mattklein123): Split this more clearly into request and response directional state. RequestEncoder* request_encoder_; ResponseEncoder* response_encoder_; + TestRequestHeaderMapImpl request_headers_; NiceMock response_decoder_; NiceMock request_decoder_; NiceMock stream_callbacks_; @@ -136,8 +138,9 @@ class HttpStream : public LinkedObject { } request_, response_; HttpStream(ClientConnection& client, const TestRequestHeaderMapImpl& request_headers, - bool end_stream, StreamResetCallbackFn stream_reset_callback) - : stream_reset_callback_(stream_reset_callback) { + bool end_stream, StreamResetCallbackFn stream_reset_callback, + MockConnectionManagerConfig* config) + : stream_reset_callback_(stream_reset_callback), conn_manager_config_(config) { request_.request_encoder_ = &client.newStream(response_.response_decoder_); ON_CALL(request_.stream_callbacks_, onResetStream(_, _)) .WillByDefault(InvokeWithoutArgs([this] { @@ -181,7 +184,7 @@ class HttpStream : public LinkedObject { if (!end_stream) { request_.request_encoder_->getStream().addCallbacks(request_.stream_callbacks_); } - + request_.request_headers_ = request_headers; request_.request_encoder_->encodeHeaders(request_headers, end_stream).IgnoreError(); request_.stream_state_ = end_stream ? StreamState::Closed : StreamState::PendingDataOrTrailers; response_.stream_state_ = StreamState::PendingHeaders; @@ -214,6 +217,8 @@ class HttpStream : public LinkedObject { if (response) { auto headers = fromSanitizedHeaders(directional_action.headers()); + ConnectionManagerUtility::mutateResponseHeaders(headers, &request_.request_headers_, + *conn_manager_config_, ""); if (headers.Status() == nullptr) { headers.setReferenceKey(Headers::get().Status, "200"); } @@ -380,6 +385,7 @@ class HttpStream : public LinkedObject { int32_t stream_index_{-1}; StreamResetCallbackFn stream_reset_callback_; + MockConnectionManagerConfig* conn_manager_config_; }; // Buffer between client and server H1/H2 codecs. This models each write operation @@ -467,6 +473,7 @@ void codecFuzz(const test::common::http::CodecImplFuzzTestCase& input, HttpVersi NiceMock server_connection; NiceMock server_callbacks; NiceMock random; + NiceMock conn_manager_config; uint32_t max_request_headers_kb = Http::DEFAULT_MAX_REQUEST_HEADERS_KB; uint32_t max_request_headers_count = Http::DEFAULT_MAX_HEADERS_COUNT; uint32_t max_response_headers_count = Http::DEFAULT_MAX_HEADERS_COUNT; @@ -587,12 +594,14 @@ void codecFuzz(const test::common::http::CodecImplFuzzTestCase& input, HttpVersi HttpStreamPtr stream = std::make_unique( *client, fromSanitizedHeaders(action.new_stream().request_headers()), - action.new_stream().end_stream(), [&should_close_connection, http2]() { + action.new_stream().end_stream(), + [&should_close_connection, http2]() { // HTTP/1 codec has stream reset implying connection close. if (!http2) { should_close_connection = true; } - }); + }, + &conn_manager_config); LinkedList::moveIntoListBack(std::move(stream), pending_streams); break; } diff --git a/test/common/http/conn_manager_utility_test.cc b/test/common/http/conn_manager_utility_test.cc index f83e84346ed9..e22c8feccff2 100644 --- a/test/common/http/conn_manager_utility_test.cc +++ b/test/common/http/conn_manager_utility_test.cc @@ -76,79 +76,6 @@ class MockInternalAddressConfig : public Http::InternalAddressConfig { MOCK_METHOD(bool, isInternalAddress, (const Network::Address::Instance&), (const)); }; -class MockConnectionManagerConfig : public ConnectionManagerConfig { -public: - MockConnectionManagerConfig() { - ON_CALL(*this, generateRequestId()).WillByDefault(Return(true)); - ON_CALL(*this, isRoutable()).WillByDefault(Return(true)); - ON_CALL(*this, preserveExternalRequestId()).WillByDefault(Return(false)); - ON_CALL(*this, alwaysSetRequestIdInResponse()).WillByDefault(Return(false)); - } - - // Http::ConnectionManagerConfig - ServerConnectionPtr createCodec(Network::Connection& connection, const Buffer::Instance& instance, - ServerConnectionCallbacks& callbacks) override { - return ServerConnectionPtr{createCodec_(connection, instance, callbacks)}; - } - - MOCK_METHOD(RequestIDExtensionSharedPtr, requestIDExtension, ()); - MOCK_METHOD(const std::list&, accessLogs, ()); - MOCK_METHOD(ServerConnection*, createCodec_, - (Network::Connection&, const Buffer::Instance&, ServerConnectionCallbacks&)); - MOCK_METHOD(DateProvider&, dateProvider, ()); - MOCK_METHOD(std::chrono::milliseconds, drainTimeout, (), (const)); - MOCK_METHOD(FilterChainFactory&, filterFactory, ()); - MOCK_METHOD(bool, generateRequestId, (), (const)); - MOCK_METHOD(bool, preserveExternalRequestId, (), (const)); - MOCK_METHOD(bool, alwaysSetRequestIdInResponse, (), (const)); - MOCK_METHOD(uint32_t, maxRequestHeadersKb, (), (const)); - MOCK_METHOD(uint32_t, maxRequestHeadersCount, (), (const)); - MOCK_METHOD(absl::optional, idleTimeout, (), (const)); - MOCK_METHOD(bool, isRoutable, (), (const)); - MOCK_METHOD(absl::optional, maxConnectionDuration, (), (const)); - MOCK_METHOD(absl::optional, maxStreamDuration, (), (const)); - MOCK_METHOD(std::chrono::milliseconds, streamIdleTimeout, (), (const)); - MOCK_METHOD(std::chrono::milliseconds, requestTimeout, (), (const)); - MOCK_METHOD(std::chrono::milliseconds, requestHeadersTimeout, (), (const)); - MOCK_METHOD(std::chrono::milliseconds, delayedCloseTimeout, (), (const)); - MOCK_METHOD(Router::RouteConfigProvider*, routeConfigProvider, ()); - MOCK_METHOD(Config::ConfigProvider*, scopedRouteConfigProvider, ()); - MOCK_METHOD(const std::string&, serverName, (), (const)); - MOCK_METHOD(HttpConnectionManagerProto::ServerHeaderTransformation, serverHeaderTransformation, - (), (const)); - MOCK_METHOD(ConnectionManagerStats&, stats, ()); - MOCK_METHOD(ConnectionManagerTracingStats&, tracingStats, ()); - MOCK_METHOD(bool, useRemoteAddress, (), (const)); - const Http::InternalAddressConfig& internalAddressConfig() const override { - return *internal_address_config_; - } - - MOCK_METHOD(bool, unixSocketInternal, ()); - MOCK_METHOD(uint32_t, xffNumTrustedHops, (), (const)); - MOCK_METHOD(bool, skipXffAppend, (), (const)); - MOCK_METHOD(const std::string&, via, (), (const)); - MOCK_METHOD(Http::ForwardClientCertType, forwardClientCert, (), (const)); - MOCK_METHOD(const std::vector&, setCurrentClientCertDetails, (), - (const)); - MOCK_METHOD(const Network::Address::Instance&, localAddress, ()); - MOCK_METHOD(const absl::optional&, userAgent, ()); - MOCK_METHOD(const Http::TracingConnectionManagerConfig*, tracingConfig, ()); - MOCK_METHOD(Tracing::HttpTracerSharedPtr, tracer, ()); - MOCK_METHOD(ConnectionManagerListenerStats&, listenerStats, ()); - MOCK_METHOD(bool, proxy100Continue, (), (const)); - MOCK_METHOD(bool, streamErrorOnInvalidHttpMessaging, (), (const)); - MOCK_METHOD(const Http::Http1Settings&, http1Settings, (), (const)); - MOCK_METHOD(bool, shouldNormalizePath, (), (const)); - MOCK_METHOD(bool, shouldMergeSlashes, (), (const)); - MOCK_METHOD(bool, shouldStripMatchingPort, (), (const)); - MOCK_METHOD(envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction, - headersWithUnderscoresAction, (), (const)); - MOCK_METHOD(const LocalReply::LocalReply&, localReply, (), (const)); - - std::unique_ptr internal_address_config_ = - std::make_unique(); -}; - const Http::LowerCaseString& traceStatusHeader() { static Http::LowerCaseString header("x-trace-status"); return header; diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index 6be5851728c9..15f7587a2d34 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -1550,6 +1550,38 @@ TEST_P(IntegrationTest, TestUpgradeHeaderInResponse) { EXPECT_EQ("Hello World", response->body()); } +// Expect that if an upgrade was not expected, the HCM correctly removes upgrade headers from the +// response and the response encoder does not drop trailers. +TEST_P(IntegrationTest, TestUpgradeHeaderInResponseWithTrailers) { + config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); + config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + FakeRawConnectionPtr fake_upstream_connection; + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + ASSERT(fake_upstream_connection != nullptr); + ASSERT_TRUE(fake_upstream_connection->write("HTTP/1.1 200 OK\r\n" + "connection: upgrade\r\n" + "upgrade: websocket\r\n" + "Transfer-encoding: chunked\r\n\r\n" + "b\r\nHello World\r\n0\r\n" + "trailer1:t2\r\n" + "\r\n", + false)); + + // Expect that upgrade headers are dropped and trailers are sent. + response->waitForHeaders(); + EXPECT_EQ(nullptr, response->headers().Upgrade()); + EXPECT_EQ(nullptr, response->headers().Connection()); + response->waitForEndStream(); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("Hello World", response->body()); + EXPECT_NE(response->trailers(), nullptr); +} + TEST_P(IntegrationTest, ConnectWithNoBody) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& diff --git a/test/mocks/http/BUILD b/test/mocks/http/BUILD index e5593a908c55..8a8313f51e05 100644 --- a/test/mocks/http/BUILD +++ b/test/mocks/http/BUILD @@ -50,6 +50,7 @@ envoy_cc_mock( "//include/envoy/http:filter_interface", "//include/envoy/ssl:connection_interface", "//include/envoy/tracing:http_tracer_interface", + "//source/common/http:conn_manager_config_interface", "//source/common/http:filter_manager_lib", "//source/common/http:header_map_lib", "//test/mocks/event:event_mocks", diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 407956f8aa93..0d38fdddf5c0 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -14,6 +14,7 @@ #include "envoy/http/filter.h" #include "envoy/ssl/connection.h" +#include "common/http/conn_manager_config.h" #include "common/http/filter_manager.h" #include "common/http/header_map_impl.h" #include "common/http/utility.h" @@ -36,6 +37,8 @@ #include "absl/strings/str_join.h" #include "gmock/gmock.h" +using testing::Return; + namespace Envoy { namespace Http { @@ -477,6 +480,79 @@ class MockDownstreamWatermarkCallbacks : public DownstreamWatermarkCallbacks { MOCK_METHOD(void, onBelowWriteBufferLowWatermark, ()); }; +class MockConnectionManagerConfig : public ConnectionManagerConfig { +public: + MockConnectionManagerConfig() { + ON_CALL(*this, generateRequestId()).WillByDefault(testing::Return(true)); + ON_CALL(*this, isRoutable()).WillByDefault(testing::Return(true)); + ON_CALL(*this, preserveExternalRequestId()).WillByDefault(testing::Return(false)); + ON_CALL(*this, alwaysSetRequestIdInResponse()).WillByDefault(testing::Return(false)); + } + + // Http::ConnectionManagerConfig + ServerConnectionPtr createCodec(Network::Connection& connection, const Buffer::Instance& instance, + ServerConnectionCallbacks& callbacks) override { + return ServerConnectionPtr{createCodec_(connection, instance, callbacks)}; + } + + MOCK_METHOD(RequestIDExtensionSharedPtr, requestIDExtension, ()); + MOCK_METHOD(const std::list&, accessLogs, ()); + MOCK_METHOD(ServerConnection*, createCodec_, + (Network::Connection&, const Buffer::Instance&, ServerConnectionCallbacks&)); + MOCK_METHOD(DateProvider&, dateProvider, ()); + MOCK_METHOD(std::chrono::milliseconds, drainTimeout, (), (const)); + MOCK_METHOD(FilterChainFactory&, filterFactory, ()); + MOCK_METHOD(bool, generateRequestId, (), (const)); + MOCK_METHOD(bool, preserveExternalRequestId, (), (const)); + MOCK_METHOD(bool, alwaysSetRequestIdInResponse, (), (const)); + MOCK_METHOD(uint32_t, maxRequestHeadersKb, (), (const)); + MOCK_METHOD(uint32_t, maxRequestHeadersCount, (), (const)); + MOCK_METHOD(absl::optional, idleTimeout, (), (const)); + MOCK_METHOD(bool, isRoutable, (), (const)); + MOCK_METHOD(absl::optional, maxConnectionDuration, (), (const)); + MOCK_METHOD(absl::optional, maxStreamDuration, (), (const)); + MOCK_METHOD(std::chrono::milliseconds, streamIdleTimeout, (), (const)); + MOCK_METHOD(std::chrono::milliseconds, requestTimeout, (), (const)); + MOCK_METHOD(std::chrono::milliseconds, requestHeadersTimeout, (), (const)); + MOCK_METHOD(std::chrono::milliseconds, delayedCloseTimeout, (), (const)); + MOCK_METHOD(Router::RouteConfigProvider*, routeConfigProvider, ()); + MOCK_METHOD(Config::ConfigProvider*, scopedRouteConfigProvider, ()); + MOCK_METHOD(const std::string&, serverName, (), (const)); + MOCK_METHOD(HttpConnectionManagerProto::ServerHeaderTransformation, serverHeaderTransformation, + (), (const)); + MOCK_METHOD(ConnectionManagerStats&, stats, ()); + MOCK_METHOD(ConnectionManagerTracingStats&, tracingStats, ()); + MOCK_METHOD(bool, useRemoteAddress, (), (const)); + const Http::InternalAddressConfig& internalAddressConfig() const override { + return *internal_address_config_; + } + + MOCK_METHOD(bool, unixSocketInternal, ()); + MOCK_METHOD(uint32_t, xffNumTrustedHops, (), (const)); + MOCK_METHOD(bool, skipXffAppend, (), (const)); + MOCK_METHOD(const std::string&, via, (), (const)); + MOCK_METHOD(Http::ForwardClientCertType, forwardClientCert, (), (const)); + MOCK_METHOD(const std::vector&, setCurrentClientCertDetails, (), + (const)); + MOCK_METHOD(const Network::Address::Instance&, localAddress, ()); + MOCK_METHOD(const absl::optional&, userAgent, ()); + MOCK_METHOD(const Http::TracingConnectionManagerConfig*, tracingConfig, ()); + MOCK_METHOD(Tracing::HttpTracerSharedPtr, tracer, ()); + MOCK_METHOD(ConnectionManagerListenerStats&, listenerStats, ()); + MOCK_METHOD(bool, proxy100Continue, (), (const)); + MOCK_METHOD(bool, streamErrorOnInvalidHttpMessaging, (), (const)); + MOCK_METHOD(const Http::Http1Settings&, http1Settings, (), (const)); + MOCK_METHOD(bool, shouldNormalizePath, (), (const)); + MOCK_METHOD(bool, shouldMergeSlashes, (), (const)); + MOCK_METHOD(bool, shouldStripMatchingPort, (), (const)); + MOCK_METHOD(envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction, + headersWithUnderscoresAction, (), (const)); + MOCK_METHOD(const LocalReply::LocalReply&, localReply, (), (const)); + + std::unique_ptr internal_address_config_ = + std::make_unique(); +}; + } // namespace Http namespace Http { From 76bcbd7d31615196d6d7295a741a5329689fdb9e Mon Sep 17 00:00:00 2001 From: Matt Klein Date: Mon, 14 Dec 2020 09:38:59 -0800 Subject: [PATCH 18/49] squash: check for cluster that does not exist (#14386) Signed-off-by: Matt Klein --- .../filters/http/squash/squash_filter.cc | 16 ++++++++-------- .../filters/http/squash/squash_filter_test.cc | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/source/extensions/filters/http/squash/squash_filter.cc b/source/extensions/filters/http/squash/squash_filter.cc index 084ec4308569..bca43dceb5a3 100644 --- a/source/extensions/filters/http/squash/squash_filter.cc +++ b/source/extensions/filters/http/squash/squash_filter.cc @@ -276,14 +276,14 @@ void SquashFilter::pollForAttachment() { request->headers().setReferencePath(debug_attachment_path_); request->headers().setReferenceHost(SERVER_AUTHORITY); - // TODO(mattklein123): The following code should check to see if the cluster has been removed - // by CDS. This is a preexisting bug so was not fixed in my recent refactor because testing is - // non-trivial. - in_flight_request_ = - cm_.getThreadLocalCluster(config_->clusterName()) - ->httpAsyncClient() - .send(std::move(request), check_attachment_callback_, - Http::AsyncClient::RequestOptions().setTimeout(config_->requestTimeout())); + const auto thread_local_cluster = cm_.getThreadLocalCluster(config_->clusterName()); + if (thread_local_cluster != nullptr) { + in_flight_request_ = thread_local_cluster->httpAsyncClient().send( + std::move(request), check_attachment_callback_, + Http::AsyncClient::RequestOptions().setTimeout(config_->requestTimeout())); + } else { + scheduleRetry(); + } // No need to check if in_flight_request_ is null as onFailure will take care of // cleanup. } diff --git a/test/extensions/filters/http/squash/squash_filter_test.cc b/test/extensions/filters/http/squash/squash_filter_test.cc index 6bfade15ad44..5cb81564c4be 100644 --- a/test/extensions/filters/http/squash/squash_filter_test.cc +++ b/test/extensions/filters/http/squash/squash_filter_test.cc @@ -363,6 +363,25 @@ TEST_F(SquashFilterTest, CheckRetryPollingAttachment) { completeGetStatusRequest("attached"); } +TEST_F(SquashFilterTest, PollingAttachmentNoCluster) { + doDownstreamRequest(); + // Expect the get attachment request + expectAsyncClientSend(); + completeCreateRequest(); + + auto retry_timer = new NiceMock(&filter_callbacks_.dispatcher_); + + EXPECT_CALL(*retry_timer, enableTimer(config_->attachmentPollPeriod(), _)); + completeGetStatusRequest("attaching"); + + // Expect the second get attachment request + ON_CALL(factory_context_.cluster_manager_, getThreadLocalCluster("squash")) + .WillByDefault(Return(nullptr)); + EXPECT_CALL(filter_callbacks_.dispatcher_, setTrackedObject(_)).Times(2); + EXPECT_CALL(*retry_timer, enableTimer(config_->attachmentPollPeriod(), _)); + retry_timer->invokeCallback(); +} + TEST_F(SquashFilterTest, CheckRetryPollingAttachmentOnFailure) { doDownstreamRequest(); // Expect the first get attachment request From 0e6047ba91dc14c27ac69829743ef33197a74304 Mon Sep 17 00:00:00 2001 From: Matt Klein Date: Mon, 14 Dec 2020 09:39:54 -0800 Subject: [PATCH 19/49] cluster manager: initialization cleanups (#14382) Final follow up from #13906. This PR does: 1) Simplify the logic during startup by making thread local clusters only appear after a cluster has been initialized. This is now uniform both for bootstrap clusters as well as CDS clusters, making the logic simpler to follow. 2) Aggregate cluster needed fixes due to assumptions on startup existence of the thread local cluster. This change also fixes https://github.com/envoyproxy/envoy/issues/14119 3) Make TLS mocks verify that set() is called before other functions. Signed-off-by: Matt Klein --- .../common/upstream/cluster_manager_impl.cc | 105 +++++++----------- source/common/upstream/cluster_manager_impl.h | 14 ++- .../extensions/clusters/aggregate/cluster.cc | 19 ++-- .../extensions/clusters/aggregate/cluster.h | 42 +++++-- .../upstream/cluster_manager_impl_test.cc | 64 +++++------ .../http/jwt_authn/filter_config_test.cc | 2 +- .../redis_proxy/conn_pool_impl_test.cc | 2 +- .../lightstep/lightstep_tracer_impl_test.cc | 2 +- test/mocks/thread_local/mocks.h | 20 +++- 9 files changed, 139 insertions(+), 131 deletions(-) diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 7c9246d88b61..af026fe66b23 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -371,37 +371,25 @@ ClusterManagerImpl::ClusterManagerImpl( cm_stats_.cluster_added_.add(bootstrap.static_resources().clusters().size()); updateClusterCounts(); - if (local_cluster_name_ && - (active_clusters_.find(local_cluster_name_.value()) == active_clusters_.end())) { - throw EnvoyException( - fmt::format("local cluster '{}' must be defined", local_cluster_name_.value())); + absl::optional local_cluster_params; + if (local_cluster_name_) { + auto local_cluster = active_clusters_.find(local_cluster_name_.value()); + if (local_cluster == active_clusters_.end()) { + throw EnvoyException( + fmt::format("local cluster '{}' must be defined", local_cluster_name_.value())); + } + local_cluster_params.emplace(); + local_cluster_params->info_ = local_cluster->second->cluster().info(); + local_cluster_params->load_balancer_factory_ = local_cluster->second->loadBalancerFactory(); + local_cluster->second->setAddedOrUpdated(); } // Once the initial set of static bootstrap clusters are created (including the local cluster), // we can instantiate the thread local cluster manager. - tls_.set([this](Event::Dispatcher& dispatcher) { - return std::make_shared(*this, dispatcher); + tls_.set([this, local_cluster_params](Event::Dispatcher& dispatcher) { + return std::make_shared(*this, dispatcher, local_cluster_params); }); - // For active clusters that exist in bootstrap, post an empty thread local cluster update to - // populate them. - // TODO(mattklein123): It would be nice if we did not do this and instead all thread local cluster - // creation happened as part of the cluster init flow, however there are certain cases that depend - // on this behavior including route checking. It may be possible to fix static route checking to - // not depend on this behavior, but for now this is consistent with the way we have always done - // this so in the interest of minimal change it is not being done now. - for (auto& cluster : active_clusters_) { - // Skip posting the thread local cluster which is created as part of the thread local cluster - // manager constructor. See the TODO in that code for eventually cleaning this up. - if (local_cluster_name_ && local_cluster_name_.value() == cluster.first) { - continue; - } - - // Avoid virtual call in the constructor. This only impacts tests. Remove this when fixing - // the above TODO. - postThreadLocalClusterUpdateNonVirtual(*cluster.second, ThreadLocalClusterUpdateParams()); - } - // We can now potentially create the CDS API once the backing cluster exists. if (dyn_resources.has_cds_config()) { cds_api_ = factory_.createCds(dyn_resources.cds_config(), *this); @@ -536,14 +524,11 @@ void ClusterManagerImpl::onClusterInit(ClusterManagerCluster& cm_cluster) { params.per_priority_update_params_.emplace_back(host_set->priority(), host_set->hosts(), HostVector{}); } - // At this point the update is posted if either there are actual updates or the cluster has - // not been added yet. The latter can only happen with dynamic cluster as static clusters are - // added immediately. - // TODO(mattklein123): Per related TODOs we will see if we can centralize all logic so that - // clusters only get added in this path and all of the special casing can be removed. - if (!params.per_priority_update_params_.empty() || !cm_cluster.addedOrUpdated()) { - postThreadLocalClusterUpdate(cm_cluster, std::move(params)); - } + // NOTE: In all cases *other* than the local cluster, this is when a cluster is added/updated + // The local cluster must currently be statically defined and must exist prior to other + // clusters being added/updated. We could gate the below update on hosts being available on + // the cluster or the cluster not already existing, but the special logic is not worth it. + postThreadLocalClusterUpdate(cm_cluster, std::move(params)); } bool ClusterManagerImpl::scheduleUpdate(ClusterManagerCluster& cluster, uint32_t priority, @@ -929,21 +914,13 @@ void ClusterManagerImpl::postThreadLocalDrainConnections(const Cluster& cluster, }); } -void ClusterManagerImpl::postThreadLocalClusterUpdateNonVirtual( - ClusterManagerCluster& cm_cluster, ThreadLocalClusterUpdateParams&& params) { - const bool is_local_cluster = local_cluster_name_.has_value() && - local_cluster_name_.value() == cm_cluster.cluster().info()->name(); +void ClusterManagerImpl::postThreadLocalClusterUpdate(ClusterManagerCluster& cm_cluster, + ThreadLocalClusterUpdateParams&& params) { bool add_or_update_cluster = false; if (!cm_cluster.addedOrUpdated()) { add_or_update_cluster = true; cm_cluster.setAddedOrUpdated(); } - if (is_local_cluster) { - // TODO(mattklein123): This is needed because of the special case of how local cluster is - // initialized in the thread local cluster manager constructor. This will all be cleaned up - // in a follow up. - add_or_update_cluster = false; - } LoadBalancerFactorySharedPtr load_balancer_factory; if (add_or_update_cluster) { @@ -961,6 +938,7 @@ void ClusterManagerImpl::postThreadLocalClusterUpdateNonVirtual( tls_.runOnAllThreads( [info = cm_cluster.cluster().info(), params = std::move(params), add_or_update_cluster, load_balancer_factory](OptRef cluster_manager) { + ThreadLocalClusterManagerImpl::ClusterEntry* new_cluster = nullptr; if (add_or_update_cluster) { if (cluster_manager->thread_local_clusters_.count(info->name()) > 0) { ENVOY_LOG(debug, "updating TLS cluster {}", info->name()); @@ -968,15 +946,9 @@ void ClusterManagerImpl::postThreadLocalClusterUpdateNonVirtual( ENVOY_LOG(debug, "adding TLS cluster {}", info->name()); } - auto thread_local_cluster = new ThreadLocalClusterManagerImpl::ClusterEntry( - *cluster_manager, info, load_balancer_factory); - cluster_manager->thread_local_clusters_[info->name()].reset(thread_local_cluster); - // TODO(mattklein123): It would be better if update callbacks were done after the initial - // cluster member is seeded, assuming it is. In the interest of minimal change this is - // deferred for a future change. - for (auto& cb : cluster_manager->update_callbacks_) { - cb->onClusterAddOrUpdate(*thread_local_cluster); - } + new_cluster = new ThreadLocalClusterManagerImpl::ClusterEntry(*cluster_manager, info, + load_balancer_factory); + cluster_manager->thread_local_clusters_[info->name()].reset(new_cluster); } for (const auto& per_priority : params.per_priority_update_params_) { @@ -985,6 +957,12 @@ void ClusterManagerImpl::postThreadLocalClusterUpdateNonVirtual( per_priority.locality_weights_, per_priority.hosts_added_, per_priority.hosts_removed_, per_priority.overprovisioning_factor_); } + + if (new_cluster != nullptr) { + for (auto& cb : cluster_manager->update_callbacks_) { + cb->onClusterAddOrUpdate(*new_cluster); + } + } }); } @@ -1059,22 +1037,17 @@ ProtobufTypes::MessagePtr ClusterManagerImpl::dumpClusterConfigs() { } ClusterManagerImpl::ThreadLocalClusterManagerImpl::ThreadLocalClusterManagerImpl( - ClusterManagerImpl& parent, Event::Dispatcher& dispatcher) + ClusterManagerImpl& parent, Event::Dispatcher& dispatcher, + const absl::optional& local_cluster_params) : parent_(parent), thread_local_dispatcher_(dispatcher) { // If local cluster is defined then we need to initialize it first. - // TODO(mattklein123): Technically accessing active_clusters_ here is a race condition. This has - // been this way "forever" but should be fixed in a follow up. - if (parent.localClusterName()) { - ENVOY_LOG(debug, "adding TLS local cluster {}", parent.localClusterName().value()); - auto& local_cluster = parent.active_clusters_.at(parent.localClusterName().value()); - thread_local_clusters_[parent.localClusterName().value()] = std::make_unique( - *this, local_cluster->cluster_->info(), local_cluster->loadBalancerFactory()); - } - - local_priority_set_ = - parent.localClusterName() - ? &thread_local_clusters_[parent.localClusterName().value()]->priority_set_ - : nullptr; + if (local_cluster_params.has_value()) { + const auto& local_cluster_name = local_cluster_params->info_->name(); + ENVOY_LOG(debug, "adding TLS local cluster {}", local_cluster_name); + thread_local_clusters_[local_cluster_name] = std::make_unique( + *this, local_cluster_params->info_, local_cluster_params->load_balancer_factory_); + local_priority_set_ = &thread_local_clusters_[local_cluster_name]->priority_set_; + } } ClusterManagerImpl::ThreadLocalClusterManagerImpl::~ThreadLocalClusterManagerImpl() { diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index ebbd98c55b17..e75cc5f7ebc9 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -327,9 +327,7 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable; - ThreadLocalClusterManagerImpl(ClusterManagerImpl& parent, Event::Dispatcher& dispatcher); + struct LocalClusterParams { + LoadBalancerFactorySharedPtr load_balancer_factory_; + ClusterInfoConstSharedPtr info_; + }; + + ThreadLocalClusterManagerImpl(ClusterManagerImpl& parent, Event::Dispatcher& dispatcher, + const absl::optional& local_cluster_params); ~ThreadLocalClusterManagerImpl() override; void drainConnPools(const HostVector& hosts); void drainConnPools(HostSharedPtr old_host, ConnPoolsContainer& container); @@ -556,8 +560,6 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable(); + per_thread_load_balancer->lb_ = std::make_unique( + info->stats(), runtime, random, info->lbConfig()); + return per_thread_load_balancer; + }); +} PriorityContextPtr Cluster::linearizePrioritySet(const std::function& skip_predicate) { @@ -39,7 +47,7 @@ Cluster::linearizePrioritySet(const std::function& ski continue; } auto tlc = cluster_manager_.getThreadLocalCluster(cluster); - // It is possible that the cluster doesn't exist, e.g., the cluster cloud be deleted or the + // It is possible that the cluster doesn't exist, e.g., the cluster could be deleted or the // cluster hasn't been added by xDS. if (tlc == nullptr) { continue; @@ -92,12 +100,9 @@ void Cluster::refresh(const std::function& skip_predic // Post the priority set to worker threads. // TODO(mattklein123): Remove "this" capture. tls_.runOnAllThreads([this, skip_predicate, cluster_name = this->info()->name()]( - OptRef) { + OptRef per_thread_load_balancer) { PriorityContextPtr priority_context = linearizePrioritySet(skip_predicate); - Upstream::ThreadLocalCluster* cluster = cluster_manager_.getThreadLocalCluster(cluster_name); - ASSERT(cluster != nullptr); - dynamic_cast(cluster->loadBalancer()) - .refresh(std::move(priority_context)); + per_thread_load_balancer->get().refresh(std::move(priority_context)); }); } diff --git a/source/extensions/clusters/aggregate/cluster.h b/source/extensions/clusters/aggregate/cluster.h index e5beeb46ef5a..59f74b08d876 100644 --- a/source/extensions/clusters/aggregate/cluster.h +++ b/source/extensions/clusters/aggregate/cluster.h @@ -3,6 +3,7 @@ #include "envoy/config/cluster/v3/cluster.pb.h" #include "envoy/extensions/clusters/aggregate/v3/cluster.pb.h" #include "envoy/extensions/clusters/aggregate/v3/cluster.pb.validate.h" +#include "envoy/thread_local/thread_local_object.h" #include "common/upstream/cluster_factory_impl.h" #include "common/upstream/upstream_impl.h" @@ -28,6 +29,8 @@ struct PriorityContext { using PriorityContextPtr = std::unique_ptr; +class AggregateClusterLoadBalancer; + class Cluster : public Upstream::ClusterImplBase, Upstream::ClusterUpdateCallbacks { public: Cluster(const envoy::config::cluster::v3::Cluster& cluster, @@ -37,6 +40,24 @@ class Cluster : public Upstream::ClusterImplBase, Upstream::ClusterUpdateCallbac Server::Configuration::TransportSocketFactoryContextImpl& factory_context, Stats::ScopePtr&& stats_scope, ThreadLocal::SlotAllocator& tls, bool added_via_api); + struct PerThreadLoadBalancer : public ThreadLocal::ThreadLocalObject { + AggregateClusterLoadBalancer& get() { + // We can refresh before the per-worker LB is created. One of these variants should hold + // a non-null value. + if (absl::holds_alternative>(lb_)) { + ASSERT(absl::get>(lb_) != nullptr); + return *absl::get>(lb_); + } else { + ASSERT(absl::get(lb_) != nullptr); + return *absl::get(lb_); + } + } + + // For aggregate cluster the per-thread LB is only created once. We need to own it so we + // can pre-populate it before the LB is created and handed to the cluster. + absl::variant, AggregateClusterLoadBalancer*> lb_; + }; + // Upstream::Cluster Upstream::Cluster::InitializePhase initializePhase() const override { return Upstream::Cluster::InitializePhase::Secondary; @@ -54,7 +75,7 @@ class Cluster : public Upstream::ClusterImplBase, Upstream::ClusterUpdateCallbac Upstream::ClusterManager& cluster_manager_; Runtime::Loader& runtime_; Random::RandomGenerator& random_; - ThreadLocal::TypedSlot<> tls_; + ThreadLocal::TypedSlot tls_; const std::vector clusters_; private: @@ -139,8 +160,14 @@ struct AggregateLoadBalancerFactory : public Upstream::LoadBalancerFactory { AggregateLoadBalancerFactory(const Cluster& cluster) : cluster_(cluster) {} // Upstream::LoadBalancerFactory Upstream::LoadBalancerPtr create() override { - return std::make_unique( - cluster_.info()->stats(), cluster_.runtime_, cluster_.random_, cluster_.info()->lbConfig()); + // See comments in PerThreadLoadBalancer above for why the follow is done. + auto per_thread_local_balancer = cluster_.tls_.get(); + ASSERT(absl::get>( + per_thread_local_balancer->lb_) != nullptr); + auto to_return = std::move( + absl::get>(per_thread_local_balancer->lb_)); + per_thread_local_balancer->lb_ = to_return.get(); + return to_return; } const Cluster& cluster_; @@ -148,15 +175,14 @@ struct AggregateLoadBalancerFactory : public Upstream::LoadBalancerFactory { // Thread aware load balancer created by the main thread. struct AggregateThreadAwareLoadBalancer : public Upstream::ThreadAwareLoadBalancer { - AggregateThreadAwareLoadBalancer(const Cluster& cluster) : cluster_(cluster) {} + AggregateThreadAwareLoadBalancer(const Cluster& cluster) + : factory_(std::make_shared(cluster)) {} // Upstream::ThreadAwareLoadBalancer - Upstream::LoadBalancerFactorySharedPtr factory() override { - return std::make_shared(cluster_); - } + Upstream::LoadBalancerFactorySharedPtr factory() override { return factory_; } void initialize() override {} - const Cluster& cluster_; + std::shared_ptr factory_; }; class ClusterFactory : public Upstream::ConfigurableClusterFactoryBase< diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index d8f745f68fb5..a52eb1cfbb25 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -749,9 +749,7 @@ class ClusterManagerImplThreadAwareLbTest : public ClusterManagerImplTest { ON_CALL(*cluster1, initializePhase()).WillByDefault(Return(Cluster::InitializePhase::Primary)); create(parseBootstrapFromV3Json(json)); - EXPECT_EQ( - nullptr, - cluster_manager_->getThreadLocalCluster("cluster_0")->loadBalancer().chooseHost(nullptr)); + EXPECT_EQ(nullptr, cluster_manager_->getThreadLocalCluster("cluster_0")); cluster1->prioritySet().getMockHostSet(0)->hosts_ = { makeTestHost(cluster1->info_, "tcp://127.0.0.1:80", time_system_)}; @@ -1902,15 +1900,11 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemove) { EXPECT_CALL(*dns_resolver, resolve(_, _, _)) .WillRepeatedly(DoAll(SaveArg<2>(&dns_callback), Return(&active_dns_query))); create(parseBootstrapFromV3Yaml(yaml)); - EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); - - // Test for no hosts returning the correct values before we have hosts. - const auto thread_local_cluster = cluster_manager_->getThreadLocalCluster("cluster_1"); - EXPECT_EQ(nullptr, thread_local_cluster->httpConnPool(ResourcePriority::Default, - Http::Protocol::Http11, nullptr)); - EXPECT_EQ(nullptr, thread_local_cluster->tcpConnPool(ResourcePriority::Default, nullptr)); - EXPECT_EQ(nullptr, thread_local_cluster->tcpConn(nullptr).connection_); - EXPECT_EQ(3UL, factory_.stats_.counter("cluster.cluster_1.upstream_cx_none_healthy").value()); + const auto all_clusters = cluster_manager_->clusters(); + EXPECT_TRUE(all_clusters.warming_clusters_.empty()); + EXPECT_EQ(all_clusters.active_clusters_.size(), 1); + EXPECT_FALSE(all_clusters.active_clusters_.at("cluster_1").get().info()->addedViaApi()); + EXPECT_EQ(nullptr, cluster_manager_->getThreadLocalCluster("cluster_1")); // Set up for an initialize callback. ReadyWatcher initialized; @@ -2052,7 +2046,11 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveWithTls) { EXPECT_CALL(*dns_resolver, resolve(_, _, _)) .WillRepeatedly(DoAll(SaveArg<2>(&dns_callback), Return(&active_dns_query))); create(parseBootstrapFromV3Yaml(yaml)); - EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); + const auto all_clusters = cluster_manager_->clusters(); + EXPECT_TRUE(all_clusters.warming_clusters_.empty()); + EXPECT_EQ(all_clusters.active_clusters_.size(), 1); + EXPECT_FALSE(all_clusters.active_clusters_.at("cluster_1").get().info()->addedViaApi()); + EXPECT_EQ(nullptr, cluster_manager_->getThreadLocalCluster("cluster_1")); NiceMock example_com_context; ON_CALL(example_com_context, upstreamTransportSocketOptions()) @@ -2072,23 +2070,6 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveWithTls) { ON_CALL(ibm_com_context, upstreamTransportSocketOptions()) .WillByDefault(Return(std::make_shared("ibm.com"))); - // Test for no hosts returning the correct values before we have hosts. - const auto thread_local_cluster = cluster_manager_->getThreadLocalCluster("cluster_1"); - EXPECT_EQ(nullptr, thread_local_cluster->httpConnPool(ResourcePriority::Default, - Http::Protocol::Http11, nullptr)); - EXPECT_EQ(nullptr, thread_local_cluster->tcpConnPool(ResourcePriority::Default, nullptr)); - EXPECT_EQ(nullptr, thread_local_cluster->tcpConn(nullptr).connection_); - - EXPECT_EQ(nullptr, - thread_local_cluster->tcpConnPool(ResourcePriority::Default, &example_com_context)); - EXPECT_EQ(nullptr, thread_local_cluster->tcpConn(&ibm_com_context).connection_); - - EXPECT_EQ(nullptr, - thread_local_cluster->tcpConnPool(ResourcePriority::Default, &ibm_com_context)); - EXPECT_EQ(nullptr, thread_local_cluster->tcpConn(&ibm_com_context).connection_); - - EXPECT_EQ(7UL, factory_.stats_.counter("cluster.cluster_1.upstream_cx_none_healthy").value()); - // Set up for an initialize callback. ReadyWatcher initialized; cluster_manager_->setInitializedCb([&]() -> void { initialized.ready(); }); @@ -2393,7 +2374,11 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveDefaultPriority) { EXPECT_CALL(*dns_resolver, resolve(_, _, _)) .WillRepeatedly(DoAll(SaveArg<2>(&dns_callback), Return(&active_dns_query))); create(parseBootstrapFromV3Yaml(yaml)); - EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); + const auto all_clusters = cluster_manager_->clusters(); + EXPECT_TRUE(all_clusters.warming_clusters_.empty()); + EXPECT_EQ(all_clusters.active_clusters_.size(), 1); + EXPECT_FALSE(all_clusters.active_clusters_.at("cluster_1").get().info()->addedViaApi()); + EXPECT_EQ(nullptr, cluster_manager_->getThreadLocalCluster("cluster_1")); dns_callback(Network::DnsResolver::ResolutionStatus::Success, TestUtility::makeDnsResponse({"127.0.0.2"})); @@ -2475,7 +2460,11 @@ TEST_F(ClusterManagerImplTest, ConnPoolDestroyWithDraining) { EXPECT_CALL(*dns_resolver, resolve(_, _, _)) .WillRepeatedly(DoAll(SaveArg<2>(&dns_callback), Return(&active_dns_query))); create(parseBootstrapFromV3Yaml(yaml)); - EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); + const auto all_clusters = cluster_manager_->clusters(); + EXPECT_TRUE(all_clusters.warming_clusters_.empty()); + EXPECT_EQ(all_clusters.active_clusters_.size(), 1); + EXPECT_FALSE(all_clusters.active_clusters_.at("cluster_1").get().info()->addedViaApi()); + EXPECT_EQ(nullptr, cluster_manager_->getThreadLocalCluster("cluster_1")); dns_callback(Network::DnsResolver::ResolutionStatus::Success, TestUtility::makeDnsResponse({"127.0.0.2"})); @@ -2531,8 +2520,10 @@ TEST_F(ClusterManagerImplTest, OriginalDstInitialization) { // Set up for an initialize callback. cluster_manager_->setInitializedCb([&]() -> void { initialized.ready(); }); - - EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); + const auto all_clusters = cluster_manager_->clusters(); + EXPECT_TRUE(all_clusters.warming_clusters_.empty()); + EXPECT_EQ(all_clusters.active_clusters_.size(), 1); + EXPECT_FALSE(all_clusters.active_clusters_.at("cluster_1").get().info()->addedViaApi()); // Test for no hosts returning the correct values before we have hosts. EXPECT_EQ(nullptr, @@ -3867,7 +3858,10 @@ TEST_F(ClusterManagerImplTest, ConnPoolsDrainedOnHostSetChange) { ClusterUpdateCallbacksHandlePtr cb = cluster_manager_->addThreadLocalClusterUpdateCallbacks(*callbacks); - EXPECT_FALSE(cluster_manager_->getThreadLocalCluster("cluster_1")->info()->addedViaApi()); + const auto all_clusters = cluster_manager_->clusters(); + EXPECT_TRUE(all_clusters.warming_clusters_.empty()); + EXPECT_EQ(all_clusters.active_clusters_.size(), 1); + EXPECT_FALSE(all_clusters.active_clusters_.at("cluster_1").get().info()->addedViaApi()); // Verify that we get no hosts when the HostSet is empty. EXPECT_EQ(nullptr, diff --git a/test/extensions/filters/http/jwt_authn/filter_config_test.cc b/test/extensions/filters/http/jwt_authn/filter_config_test.cc index 6aae1a8e1b39..8406afd8c50d 100644 --- a/test/extensions/filters/http/jwt_authn/filter_config_test.cc +++ b/test/extensions/filters/http/jwt_authn/filter_config_test.cc @@ -168,7 +168,7 @@ TEST(HttpJwtAuthnFilterConfigTest, VerifyTLSLifetime) { NiceMock server_context; // Make sure that the thread callbacks are not invoked inline. - server_context.thread_local_.defer_data = true; + server_context.thread_local_.defer_data_ = true; { // Scope in all the things that the filter depends on, so they are destroyed as we leave the // scope. diff --git a/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc b/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc index cec880ba9289..1211390c625d 100644 --- a/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc +++ b/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc @@ -1174,7 +1174,7 @@ TEST_F(RedisConnPoolImplTest, AskRedirectionFailure) { TEST_F(RedisConnPoolImplTest, MakeRequestAndRedirectFollowedByDelete) { cm_.initializeThreadLocalClusters({"fake_cluster"}); - tls_.defer_delete = true; + tls_.defer_delete_ = true; std::unique_ptr> store = std::make_unique>(); cluster_refresh_manager_ = diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index 239e30a85624..6513314c0ba0 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -201,7 +201,7 @@ TEST_F(LightStepDriverTest, DeferredTlsInitialization) { auto propagation_mode = Common::Ot::OpenTracingDriver::PropagationMode::TracerNative; - tls_.defer_data = true; + tls_.defer_data_ = true; cm_.initializeClusters({"fake_cluster"}, {}); ON_CALL(*cm_.active_clusters_["fake_cluster"]->info_, features()) .WillByDefault(Return(Upstream::ClusterInfo::Features::HTTP2)); diff --git a/test/mocks/thread_local/mocks.h b/test/mocks/thread_local/mocks.h index 7b3097f5e0fa..e735c543b0e1 100644 --- a/test/mocks/thread_local/mocks.h +++ b/test/mocks/thread_local/mocks.h @@ -50,25 +50,32 @@ class MockInstance : public Instance { ~SlotImpl() override { // Do not actually clear slot data during shutdown. This mimics the production code. - // The defer_delete mimics the recycle() code with Bookkeeper. - if (!parent_.shutdown_ && !parent_.defer_delete) { + // The defer_delete mimics the slot being deleted on the main thread but the update not yet + // getting to a worker. + if (!parent_.shutdown_ && !parent_.defer_delete_) { EXPECT_LT(index_, parent_.data_.size()); parent_.data_[index_].reset(); } } // ThreadLocal::Slot - ThreadLocalObjectSharedPtr get() override { return parent_.data_[index_]; } + ThreadLocalObjectSharedPtr get() override { + EXPECT_TRUE(was_set_); + return parent_.data_[index_]; + } bool currentThreadRegistered() override { return parent_.registered_; } void runOnAllThreads(const UpdateCb& cb) override { + EXPECT_TRUE(was_set_); parent_.runOnAllThreads([cb, this]() { cb(parent_.data_[index_]); }); } void runOnAllThreads(const UpdateCb& cb, const Event::PostCb& main_callback) override { + EXPECT_TRUE(was_set_); parent_.runOnAllThreads([cb, this]() { cb(parent_.data_[index_]); }, main_callback); } void set(InitializeCb cb) override { - if (parent_.defer_data) { + was_set_ = true; + if (parent_.defer_data_) { parent_.deferred_data_[index_] = cb; } else { parent_.data_[index_] = cb(parent_.dispatcher_); @@ -77,6 +84,7 @@ class MockInstance : public Instance { MockInstance& parent_; const uint32_t index_; + bool was_set_{}; // set() must be called before other functions. }; void call() { @@ -90,10 +98,10 @@ class MockInstance : public Instance { testing::NiceMock dispatcher_; std::vector data_; std::vector deferred_data_; - bool defer_data{}; + bool defer_data_{}; bool shutdown_{}; bool registered_{true}; - bool defer_delete{}; + bool defer_delete_{}; }; } // namespace ThreadLocal From 53201825f6c5739fde0284235789b8c62da2004b Mon Sep 17 00:00:00 2001 From: Hamdi Allam Date: Mon, 14 Dec 2020 10:53:15 -0800 Subject: [PATCH 20/49] config: stats flush via admin (#14156) Signed-off-by: Hamdi Allam --- api/envoy/config/bootstrap/v3/bootstrap.proto | 23 ++++-- .../config/bootstrap/v4alpha/bootstrap.proto | 28 ++++--- docs/root/operations/admin.rst | 12 +-- docs/root/version_history/current.rst | 1 + .../envoy/config/bootstrap/v3/bootstrap.proto | 23 ++++-- .../config/bootstrap/v4alpha/bootstrap.proto | 28 ++++--- include/envoy/server/BUILD | 2 + include/envoy/server/configuration.h | 31 +++++-- include/envoy/server/factory_context.h | 5 +- include/envoy/server/instance.h | 11 +-- .../extensions/stat_sinks/hystrix/hystrix.cc | 2 +- source/server/BUILD | 1 + source/server/admin/stats_handler.cc | 4 + source/server/config_validation/server.h | 5 +- source/server/configuration_impl.cc | 52 ++++++++---- source/server/configuration_impl.h | 30 +++++-- source/server/server.cc | 19 +++-- source/server/server.h | 11 +-- test/integration/integration_admin_test.cc | 4 +- test/mocks/server/instance.cc | 6 ++ test/mocks/server/instance.h | 18 +++- test/mocks/server/main.h | 3 +- test/server/configuration_impl_test.cc | 82 ++++++++++++++++++- test/server/server_test.cc | 27 ++++++ .../stats_sink_manual_flush_bootstrap.yaml | 15 ++++ 25 files changed, 339 insertions(+), 104 deletions(-) create mode 100644 test/server/test_data/server/stats_sink_manual_flush_bootstrap.yaml diff --git a/api/envoy/config/bootstrap/v3/bootstrap.proto b/api/envoy/config/bootstrap/v3/bootstrap.proto index 8d0a42c94227..c3fee5e2dfeb 100644 --- a/api/envoy/config/bootstrap/v3/bootstrap.proto +++ b/api/envoy/config/bootstrap/v3/bootstrap.proto @@ -38,7 +38,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // ` for more detail. // Bootstrap :ref:`configuration overview `. -// [#next-free-field: 29] +// [#next-free-field: 30] message Bootstrap { option (udpa.annotations.versioning).previous_message_type = "envoy.config.bootstrap.v2.Bootstrap"; @@ -166,12 +166,23 @@ message Bootstrap { // Optional duration between flushes to configured stats sinks. For // performance reasons Envoy latches counters and only flushes counters and // gauges at a periodic interval. If not specified the default is 5000ms (5 - // seconds). + // seconds). Only one of `stats_flush_interval` or `stats_flush_on_admin` + // can be set. // Duration must be at least 1ms and at most 5 min. - google.protobuf.Duration stats_flush_interval = 7 [(validate.rules).duration = { - lt {seconds: 300} - gte {nanos: 1000000} - }]; + google.protobuf.Duration stats_flush_interval = 7 [ + (validate.rules).duration = { + lt {seconds: 300} + gte {nanos: 1000000} + }, + (udpa.annotations.field_migrate).oneof_promotion = "stats_flush" + ]; + + oneof stats_flush { + // Flush stats to sinks only when queried for on the admin interface. If set, + // a flush timer is not created. Only one of `stats_flush_on_admin` or + // `stats_flush_interval` can be set. + bool stats_flush_on_admin = 29 [(validate.rules).bool = {const: true}]; + } // Optional watchdog configuration. // This is for a single watchdog configuration for the entire system. diff --git a/api/envoy/config/bootstrap/v4alpha/bootstrap.proto b/api/envoy/config/bootstrap/v4alpha/bootstrap.proto index e9b53ccd3187..b4aceffc91b1 100644 --- a/api/envoy/config/bootstrap/v4alpha/bootstrap.proto +++ b/api/envoy/config/bootstrap/v4alpha/bootstrap.proto @@ -36,7 +36,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // ` for more detail. // Bootstrap :ref:`configuration overview `. -// [#next-free-field: 29] +// [#next-free-field: 30] message Bootstrap { option (udpa.annotations.versioning).previous_message_type = "envoy.config.bootstrap.v3.Bootstrap"; @@ -161,15 +161,23 @@ message Bootstrap { // Configuration for internal processing of stats. metrics.v4alpha.StatsConfig stats_config = 13; - // Optional duration between flushes to configured stats sinks. For - // performance reasons Envoy latches counters and only flushes counters and - // gauges at a periodic interval. If not specified the default is 5000ms (5 - // seconds). - // Duration must be at least 1ms and at most 5 min. - google.protobuf.Duration stats_flush_interval = 7 [(validate.rules).duration = { - lt {seconds: 300} - gte {nanos: 1000000} - }]; + oneof stats_flush { + // Optional duration between flushes to configured stats sinks. For + // performance reasons Envoy latches counters and only flushes counters and + // gauges at a periodic interval. If not specified the default is 5000ms (5 + // seconds). Only one of `stats_flush_interval` or `stats_flush_on_admin` + // can be set. + // Duration must be at least 1ms and at most 5 min. + google.protobuf.Duration stats_flush_interval = 7 [(validate.rules).duration = { + lt {seconds: 300} + gte {nanos: 1000000} + }]; + + // Flush stats to sinks only when queried for on the admin interface. If set, + // a flush timer is not created. Only one of `stats_flush_on_admin` or + // `stats_flush_interval` can be set. + bool stats_flush_on_admin = 29 [(validate.rules).bool = {const: true}]; + } // Optional watchdogs configuration. // This is used for specifying different watchdogs for the different subsystems. diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index 514c25b51e09..07df8d02a946 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -405,11 +405,13 @@ modify different aspects of the server: Outputs all statistics on demand. This command is very useful for local debugging. Histograms will output the computed quantiles i.e P0,P25,P50,P75,P90,P99,P99.9 and P100. - The output for each quantile will be in the form of (interval,cumulative) where interval value - represents the summary since last flush interval and cumulative value represents the - summary since the start of Envoy instance. "No recorded values" in the histogram output indicates - that it has not been updated with a value. - See :ref:`here ` for more information. + The output for each quantile will be in the form of (interval,cumulative) where the interval value + represents the summary since last flush. By default, a timer is setup to flush in intervals + defined by :ref:`stats_flush_interval `, + defaulting to 5 seconds. If :ref:`stats_flush_on_admin ` + is specified, stats are flushed when this endpoint is queried and a timer will not be used. The cumulative + value represents the summary since the start of Envoy instance. "No recorded values" in the histogram + output indicates that it has not been updated with a value. See :ref:`here ` for more information. .. http:get:: /stats?usedonly diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index d29d29648df2..94e90b463b72 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -58,6 +58,7 @@ Removed Config or Runtime New Features ------------ * compression: the :ref:`compressor ` filter adds support for compressing request payloads. Its configuration is unified with the :ref:`decompressor ` filter with two new fields for different directions - :ref:`requests ` and :ref:`responses `. The latter deprecates the old response-specific fields and, if used, roots the response-specific stats in `.compressor...response.*` instead of `.compressor...*`. +* config: added ability to flush stats when the admin's :ref:`/stats endpoint ` is hit instead of on a timer via :ref:`stats_flush_on_admin `. * config: added new runtime feature `envoy.features.enable_all_deprecated_features` that allows the use of all deprecated features. * grpc: implemented header value syntax support when defining :ref:`initial metadata ` for gRPC-based `ext_authz` :ref:`HTTP ` and :ref:`network ` filters, and :ref:`ratelimit ` filters. * grpc-json: added support for configuring :ref:`unescaping behavior ` for path components. diff --git a/generated_api_shadow/envoy/config/bootstrap/v3/bootstrap.proto b/generated_api_shadow/envoy/config/bootstrap/v3/bootstrap.proto index fdd3eea8d7c2..9ac8927e1984 100644 --- a/generated_api_shadow/envoy/config/bootstrap/v3/bootstrap.proto +++ b/generated_api_shadow/envoy/config/bootstrap/v3/bootstrap.proto @@ -38,7 +38,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // ` for more detail. // Bootstrap :ref:`configuration overview `. -// [#next-free-field: 29] +// [#next-free-field: 30] message Bootstrap { option (udpa.annotations.versioning).previous_message_type = "envoy.config.bootstrap.v2.Bootstrap"; @@ -164,12 +164,23 @@ message Bootstrap { // Optional duration between flushes to configured stats sinks. For // performance reasons Envoy latches counters and only flushes counters and // gauges at a periodic interval. If not specified the default is 5000ms (5 - // seconds). + // seconds). Only one of `stats_flush_interval` or `stats_flush_on_admin` + // can be set. // Duration must be at least 1ms and at most 5 min. - google.protobuf.Duration stats_flush_interval = 7 [(validate.rules).duration = { - lt {seconds: 300} - gte {nanos: 1000000} - }]; + google.protobuf.Duration stats_flush_interval = 7 [ + (validate.rules).duration = { + lt {seconds: 300} + gte {nanos: 1000000} + }, + (udpa.annotations.field_migrate).oneof_promotion = "stats_flush" + ]; + + oneof stats_flush { + // Flush stats to sinks only when queried for on the admin interface. If set, + // a flush timer is not created. Only one of `stats_flush_on_admin` or + // `stats_flush_interval` can be set. + bool stats_flush_on_admin = 29 [(validate.rules).bool = {const: true}]; + } // Optional watchdog configuration. // This is for a single watchdog configuration for the entire system. diff --git a/generated_api_shadow/envoy/config/bootstrap/v4alpha/bootstrap.proto b/generated_api_shadow/envoy/config/bootstrap/v4alpha/bootstrap.proto index b9a44d8dce48..0ddcdb389d87 100644 --- a/generated_api_shadow/envoy/config/bootstrap/v4alpha/bootstrap.proto +++ b/generated_api_shadow/envoy/config/bootstrap/v4alpha/bootstrap.proto @@ -37,7 +37,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // ` for more detail. // Bootstrap :ref:`configuration overview `. -// [#next-free-field: 29] +// [#next-free-field: 30] message Bootstrap { option (udpa.annotations.versioning).previous_message_type = "envoy.config.bootstrap.v3.Bootstrap"; @@ -162,15 +162,23 @@ message Bootstrap { // Configuration for internal processing of stats. metrics.v4alpha.StatsConfig stats_config = 13; - // Optional duration between flushes to configured stats sinks. For - // performance reasons Envoy latches counters and only flushes counters and - // gauges at a periodic interval. If not specified the default is 5000ms (5 - // seconds). - // Duration must be at least 1ms and at most 5 min. - google.protobuf.Duration stats_flush_interval = 7 [(validate.rules).duration = { - lt {seconds: 300} - gte {nanos: 1000000} - }]; + oneof stats_flush { + // Optional duration between flushes to configured stats sinks. For + // performance reasons Envoy latches counters and only flushes counters and + // gauges at a periodic interval. If not specified the default is 5000ms (5 + // seconds). Only one of `stats_flush_interval` or `stats_flush_on_admin` + // can be set. + // Duration must be at least 1ms and at most 5 min. + google.protobuf.Duration stats_flush_interval = 7 [(validate.rules).duration = { + lt {seconds: 300} + gte {nanos: 1000000} + }]; + + // Flush stats to sinks only when queried for on the admin interface. If set, + // a flush timer is not created. Only one of `stats_flush_on_admin` or + // `stats_flush_interval` can be set. + bool stats_flush_on_admin = 29 [(validate.rules).bool = {const: true}]; + } // Optional watchdog configuration. // This is for a single watchdog configuration for the entire system. diff --git a/include/envoy/server/BUILD b/include/envoy/server/BUILD index 1af7669c5cae..a8afb8a8ddc6 100644 --- a/include/envoy/server/BUILD +++ b/include/envoy/server/BUILD @@ -112,6 +112,7 @@ envoy_cc_library( hdrs = ["instance.h"], deps = [ ":admin_interface", + ":configuration_interface", ":drain_manager_interface", ":hot_restart_interface", ":lifecycle_notifier_interface", @@ -169,6 +170,7 @@ envoy_cc_library( hdrs = ["factory_context.h"], deps = [ ":admin_interface", + ":configuration_interface", ":drain_manager_interface", ":lifecycle_notifier_interface", ":process_context_interface", diff --git a/include/envoy/server/configuration.h b/include/envoy/server/configuration.h index d10e27e72c4d..1b280e56541d 100644 --- a/include/envoy/server/configuration.h +++ b/include/envoy/server/configuration.h @@ -65,6 +65,27 @@ class Watchdog { actions() const PURE; }; +class StatsConfig { +public: + virtual ~StatsConfig() = default; + + /** + * @return std::list& the list of stats sinks initialized from the configuration. + */ + virtual const std::list& sinks() const PURE; + + /** + * @return std::chrono::milliseconds the time interval between flushing to configured stat sinks. + * The server latches counters. + */ + virtual std::chrono::milliseconds flushInterval() const PURE; + + /** + * @return bool indicator to flush stats on-demand via the admin interface instead of on a timer. + */ + virtual bool flushOnAdmin() const PURE; +}; + /** * The main server configuration. */ @@ -79,15 +100,9 @@ class Main { virtual Upstream::ClusterManager* clusterManager() PURE; /** - * @return std::list& the list of stats sinks initialized from the configuration. - */ - virtual std::list& statsSinks() PURE; - - /** - * @return std::chrono::milliseconds the time interval between flushing to configured stat sinks. - * The server latches counters. + * @return const StatsConfig& the configuration of server stats. */ - virtual std::chrono::milliseconds statsFlushInterval() const PURE; + virtual StatsConfig& statsConfig() PURE; /** * @return const Watchdog& the configuration of the main thread watchdog. diff --git a/include/envoy/server/factory_context.h b/include/envoy/server/factory_context.h index 1847d7104289..6842920944c3 100644 --- a/include/envoy/server/factory_context.h +++ b/include/envoy/server/factory_context.h @@ -18,6 +18,7 @@ #include "envoy/router/context.h" #include "envoy/runtime/runtime.h" #include "envoy/server/admin.h" +#include "envoy/server/configuration.h" #include "envoy/server/drain_manager.h" #include "envoy/server/lifecycle_notifier.h" #include "envoy/server/overload/overload_manager.h" @@ -142,9 +143,9 @@ class ServerFactoryContext : public virtual CommonFactoryContext { virtual ServerLifecycleNotifier& lifecycleNotifier() PURE; /** - * @return std::chrono::milliseconds the flush interval of stats sinks. + * @return StatsConfig& the servers stats configuration. */ - virtual std::chrono::milliseconds statsFlushInterval() const PURE; + virtual StatsConfig& statsConfig() PURE; }; /** diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 23852100438f..25a08d25dc08 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -18,6 +18,7 @@ #include "envoy/runtime/runtime.h" #include "envoy/secret/secret_manager.h" #include "envoy/server/admin.h" +#include "envoy/server/configuration.h" #include "envoy/server/drain_manager.h" #include "envoy/server/hot_restart.h" #include "envoy/server/lifecycle_notifier.h" @@ -216,11 +217,6 @@ class Instance { */ virtual TimeSource& timeSource() PURE; - /** - * @return the flush interval of stats sinks. - */ - virtual std::chrono::milliseconds statsFlushInterval() const PURE; - /** * Flush the stats sinks outside of a flushing interval. * Note: stats flushing may not be synchronous. @@ -234,6 +230,11 @@ class Instance { */ virtual ProtobufMessage::ValidationContext& messageValidationContext() PURE; + /** + * @return const StatsConfig& the configuration of server stats. + */ + virtual Configuration::StatsConfig& statsConfig() PURE; + /** * @return Configuration::ServerFactoryContext& factory context for filters. */ diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 6f4a1808accf..0eed2bbe2a7d 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -389,7 +389,7 @@ void HystrixSink::flush(Stats::MetricSnapshot& snapshot) { cluster_info->statsScope() .gaugeFromStatName(membership_total_, Stats::Gauge::ImportMode::NeverImport) .value(), - server_.statsFlushInterval(), time_histograms[cluster_info->name()], ss); + server_.statsConfig().flushInterval(), time_histograms[cluster_info->name()], ss); } Buffer::OwnedImpl data; diff --git a/source/server/BUILD b/source/server/BUILD index 62649f3f7ff1..e889b84d0bea 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -417,6 +417,7 @@ envoy_cc_library( "//include/envoy/event:timer_interface", "//include/envoy/network:dns_interface", "//include/envoy/server:bootstrap_extension_config_interface", + "//include/envoy/server:configuration_interface", "//include/envoy/server:drain_manager_interface", "//include/envoy/server:fatal_action_interface", "//include/envoy/server:instance_interface", diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index e64fd878a8fb..a211ef82f812 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -71,6 +71,10 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, Http::Code StatsHandler::handlerStats(absl::string_view url, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream& admin_stream) { + if (server_.statsConfig().flushOnAdmin()) { + server_.flushStats(); + } + Http::Code rc = Http::Code::OK; const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(url); diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 0c90a2b048e3..e735b1ecd183 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -105,13 +105,12 @@ class ValidationInstance final : Logger::Loggable, const LocalInfo::LocalInfo& localInfo() const override { return *local_info_; } TimeSource& timeSource() override { return api_->timeSource(); } Envoy::MutexTracer* mutexTracer() override { return mutex_tracer_; } - std::chrono::milliseconds statsFlushInterval() const override { - return config_.statsFlushInterval(); - } void flushStats() override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } ProtobufMessage::ValidationContext& messageValidationContext() override { return validation_context_; } + + Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { return server_contexts_; diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index 5ab9061a81ae..4e3bbc4c2f8b 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -54,6 +54,21 @@ void FilterChainUtility::buildUdpFilterChain( } } +StatsConfigImpl::StatsConfigImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + if (bootstrap.has_stats_flush_interval() && + bootstrap.stats_flush_case() != + envoy::config::bootstrap::v3::Bootstrap::STATS_FLUSH_NOT_SET) { + throw EnvoyException("Only one of stats_flush_interval or stats_flush_on_admin should be set!"); + } + + flush_interval_ = + std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(bootstrap, stats_flush_interval, 5000)); + + if (bootstrap.stats_flush_case() == envoy::config::bootstrap::v3::Bootstrap::kStatsFlushOnAdmin) { + flush_on_admin_ = bootstrap.stats_flush_on_admin(); + } +} + void MainImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, Instance& server, Upstream::ClusterManagerFactory& cluster_manager_factory) { @@ -83,11 +98,26 @@ void MainImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bootstr server.listenerManager().addOrUpdateListener(listeners[i], "", false); } - stats_flush_interval_ = - std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(bootstrap, stats_flush_interval, 5000)); - initializeWatchdogs(bootstrap, server); - initializeStatsSinks(bootstrap, server); + initializeStatsConfig(bootstrap, server); +} + +void MainImpl::initializeStatsConfig(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, + Instance& server) { + ENVOY_LOG(info, "loading stats configuration"); + + // stats_config_ should be set before populating the sinks so that it is available + // from the ServerFactoryContext when creating the stats sinks. + stats_config_ = std::make_unique(bootstrap); + + for (const envoy::config::metrics::v3::StatsSink& sink_object : bootstrap.stats_sinks()) { + // Generate factory and translate stats sink custom config. + auto& factory = Config::Utility::getAndCheckFactory(sink_object); + ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig( + sink_object, server.messageValidationContext().staticValidationVisitor(), factory); + + stats_config_->addSink(factory.createStatsSink(*message, server.serverFactoryContext())); + } } void MainImpl::initializeTracers(const envoy::config::trace::v3::Tracing& configuration, @@ -116,20 +146,6 @@ void MainImpl::initializeTracers(const envoy::config::trace::v3::Tracing& config // is no longer validated in this step. } -void MainImpl::initializeStatsSinks(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - Instance& server) { - ENVOY_LOG(info, "loading stats sink configuration"); - - for (const envoy::config::metrics::v3::StatsSink& sink_object : bootstrap.stats_sinks()) { - // Generate factory and translate stats sink custom config - auto& factory = Config::Utility::getAndCheckFactory(sink_object); - ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig( - sink_object, server.messageValidationContext().staticValidationVisitor(), factory); - - stats_sinks_.emplace_back(factory.createStatsSink(*message, server.serverFactoryContext())); - } -} - void MainImpl::initializeWatchdogs(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, Instance& server) { if (bootstrap.has_watchdog() && bootstrap.has_watchdogs()) { diff --git a/source/server/configuration_impl.h b/source/server/configuration_impl.h index 41cb45b930ac..322696405e05 100644 --- a/source/server/configuration_impl.h +++ b/source/server/configuration_impl.h @@ -46,6 +46,22 @@ class StatsSinkFactory : public Config::TypedFactory { std::string category() const override { return "envoy.stats_sinks"; } }; +class StatsConfigImpl : public StatsConfig { +public: + StatsConfigImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap); + + const std::list& sinks() const override { return sinks_; } + std::chrono::milliseconds flushInterval() const override { return flush_interval_; } + bool flushOnAdmin() const override { return flush_on_admin_; } + + void addSink(Stats::SinkPtr sink) { sinks_.emplace_back(std::move(sink)); } + +private: + std::list sinks_; + std::chrono::milliseconds flush_interval_; + bool flush_on_admin_{false}; +}; + /** * Utilities for creating a filter chain for a network connection. */ @@ -98,8 +114,7 @@ class MainImpl : Logger::Loggable, public Main { // Server::Configuration::Main Upstream::ClusterManager* clusterManager() override { return cluster_manager_.get(); } - std::list& statsSinks() override { return stats_sinks_; } - std::chrono::milliseconds statsFlushInterval() const override { return stats_flush_interval_; } + StatsConfig& statsConfig() override { return *stats_config_; } const Watchdog& mainThreadWatchdogConfig() const override { return *main_thread_watchdog_; } const Watchdog& workerWatchdogConfig() const override { return *worker_watchdog_; } @@ -109,8 +124,12 @@ class MainImpl : Logger::Loggable, public Main { */ void initializeTracers(const envoy::config::trace::v3::Tracing& configuration, Instance& server); - void initializeStatsSinks(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - Instance& server); + /** + * Initialize stats configuration. + */ + void initializeStatsConfig(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, + Instance& server); + /** * Initialize watchdog(s). Call before accessing any watchdog configuration. */ @@ -118,8 +137,7 @@ class MainImpl : Logger::Loggable, public Main { Instance& server); std::unique_ptr cluster_manager_; - std::list stats_sinks_; - std::chrono::milliseconds stats_flush_interval_; + std::unique_ptr stats_config_; std::unique_ptr main_thread_watchdog_; std::unique_ptr worker_watchdog_; }; diff --git a/source/server/server.cc b/source/server/server.cc index ca18d62e4375..c3bfd8962101 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -238,10 +238,11 @@ void InstanceImpl::updateServerStats() { void InstanceImpl::flushStatsInternal() { updateServerStats(); - InstanceUtil::flushMetricsToSinks(config_.statsSinks(), stats_store_, timeSource()); + auto& stats_config = config_.statsConfig(); + InstanceUtil::flushMetricsToSinks(stats_config.sinks(), stats_store_, timeSource()); // TODO(ramaraochavali): consider adding different flush interval for histograms. if (stat_flush_timer_ != nullptr) { - stat_flush_timer_->enableTimer(config_.statsFlushInterval()); + stat_flush_timer_->enableTimer(stats_config.flushInterval()); } } @@ -554,14 +555,16 @@ void InstanceImpl::initialize(const Options& options, clusterManager().setPrimaryClustersInitializedCb( [this]() { onClusterManagerPrimaryInitializationComplete(); }); - for (Stats::SinkPtr& sink : config_.statsSinks()) { + auto& stats_config = config_.statsConfig(); + for (const Stats::SinkPtr& sink : stats_config.sinks()) { stats_store_.addSink(*sink); } - - // Some of the stat sinks may need dispatcher support so don't flush until the main loop starts. - // Just setup the timer. - stat_flush_timer_ = dispatcher_->createTimer([this]() -> void { flushStats(); }); - stat_flush_timer_->enableTimer(config_.statsFlushInterval()); + if (!stats_config.flushOnAdmin()) { + // Some of the stat sinks may need dispatcher support so don't flush until the main loop starts. + // Just setup the timer. + stat_flush_timer_ = dispatcher_->createTimer([this]() -> void { flushStats(); }); + stat_flush_timer_->enableTimer(stats_config.flushInterval()); + } // GuardDog (deadlock detection) object and thread setup before workers are // started and before our own run() loop runs. diff --git a/source/server/server.h b/source/server/server.h index 15d950e5683d..47d6c6b0a18a 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -11,6 +11,7 @@ #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/event/timer.h" #include "envoy/server/bootstrap_extension_config.h" +#include "envoy/server/configuration.h" #include "envoy/server/drain_manager.h" #include "envoy/server/guarddog.h" #include "envoy/server/instance.h" @@ -176,9 +177,7 @@ class ServerFactoryContextImpl : public Configuration::ServerFactoryContext, Router::Context& routerContext() override { return server_.routerContext(); } Envoy::Server::DrainManager& drainManager() override { return server_.drainManager(); } ServerLifecycleNotifier& lifecycleNotifier() override { return server_.lifecycleNotifier(); } - std::chrono::milliseconds statsFlushInterval() const override { - return server_.statsFlushInterval(); - } + Configuration::StatsConfig& statsConfig() override { return server_.statsConfig(); } // Configuration::TransportSocketFactoryContext Ssl::ContextManager& sslContextManager() override { return server_.sslContextManager(); } @@ -260,16 +259,14 @@ class InstanceImpl final : Logger::Loggable, TimeSource& timeSource() override { return time_source_; } void flushStats() override; + Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } + Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { return server_contexts_; } - std::chrono::milliseconds statsFlushInterval() const override { - return config_.statsFlushInterval(); - } - ProtobufMessage::ValidationContext& messageValidationContext() override { return validation_context_; } diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 6e12c3cf8f2f..f5e00a9f4991 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -466,8 +466,8 @@ TEST_P(IntegrationAdminTest, AdminOnDestroyCallbacks) { // Check that the added callback was invoked. EXPECT_EQ(test, false); - // Small test to cover new statsFlushInterval() on Instance.h. - EXPECT_EQ(test_server_->server().statsFlushInterval(), std::chrono::milliseconds(5000)); + // Small test to cover new the flush interval on the statsConfig in Instance.h. + EXPECT_EQ(test_server_->server().statsConfig().flushInterval(), std::chrono::milliseconds(5000)); } TEST_P(IntegrationAdminTest, AdminCpuProfilerStart) { diff --git a/test/mocks/server/instance.cc b/test/mocks/server/instance.cc index 5679749d3f6a..f4a1458738f3 100644 --- a/test/mocks/server/instance.cc +++ b/test/mocks/server/instance.cc @@ -17,6 +17,7 @@ MockInstance::MockInstance() singleton_manager_(new Singleton::ManagerImpl(Thread::threadFactoryForTest())), grpc_context_(stats_store_.symbolTable()), http_context_(stats_store_.symbolTable()), router_context_(stats_store_.symbolTable()), + stats_config_(std::make_shared>()), server_factory_context_( std::make_shared>()), transport_socket_factory_context_( @@ -45,6 +46,7 @@ MockInstance::MockInstance() ON_CALL(*this, singletonManager()).WillByDefault(ReturnRef(*singleton_manager_)); ON_CALL(*this, overloadManager()).WillByDefault(ReturnRef(overload_manager_)); ON_CALL(*this, messageValidationContext()).WillByDefault(ReturnRef(validation_context_)); + ON_CALL(*this, statsConfig()).WillByDefault(ReturnRef(*stats_config_)); ON_CALL(*this, serverFactoryContext()).WillByDefault(ReturnRef(*server_factory_context_)); ON_CALL(*this, transportSocketFactoryContext()) .WillByDefault(ReturnRef(*transport_socket_factory_context_)); @@ -73,9 +75,13 @@ MockServerFactoryContext::MockServerFactoryContext() .WillByDefault(ReturnRef(ProtobufMessage::getStrictValidationVisitor())); ON_CALL(*this, api()).WillByDefault(ReturnRef(api_)); ON_CALL(*this, drainManager()).WillByDefault(ReturnRef(drain_manager_)); + ON_CALL(*this, statsConfig()).WillByDefault(ReturnRef(stats_config_)); } MockServerFactoryContext::~MockServerFactoryContext() = default; +MockStatsConfig::MockStatsConfig() = default; +MockStatsConfig::~MockStatsConfig() = default; + } // namespace Configuration } // namespace Server } // namespace Envoy diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index d5f2b5e67b84..47659ff9b50d 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -38,6 +38,7 @@ namespace Envoy { namespace Server { namespace Configuration { class MockServerFactoryContext; +class MockStatsConfig; } // namespace Configuration class MockInstance : public Instance { @@ -80,7 +81,7 @@ class MockInstance : public Instance { MOCK_METHOD(ProcessContextOptRef, processContext, ()); MOCK_METHOD(ThreadLocal::Instance&, threadLocal, ()); MOCK_METHOD(const LocalInfo::LocalInfo&, localInfo, (), (const)); - MOCK_METHOD(std::chrono::milliseconds, statsFlushInterval, (), (const)); + MOCK_METHOD(Configuration::StatsConfig&, statsConfig, (), ()); MOCK_METHOD(void, flushStats, ()); MOCK_METHOD(ProtobufMessage::ValidationContext&, messageValidationContext, ()); MOCK_METHOD(Configuration::ServerFactoryContext&, serverFactoryContext, ()); @@ -119,6 +120,7 @@ class MockInstance : public Instance { Http::ContextImpl http_context_; Router::ContextImpl router_context_; testing::NiceMock validation_context_; + std::shared_ptr> stats_config_; std::shared_ptr> server_factory_context_; std::shared_ptr> @@ -126,6 +128,16 @@ class MockInstance : public Instance { }; namespace Configuration { +class MockStatsConfig : public virtual StatsConfig { +public: + MockStatsConfig(); + ~MockStatsConfig() override; + + MOCK_METHOD(const std::list&, sinks, (), (const)); + MOCK_METHOD(std::chrono::milliseconds, flushInterval, (), (const)); + MOCK_METHOD(bool, flushOnAdmin, (), (const)); +}; + class MockServerFactoryContext : public virtual ServerFactoryContext { public: MockServerFactoryContext(); @@ -150,7 +162,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); - MOCK_METHOD(std::chrono::milliseconds, statsFlushInterval, (), (const)); + MOCK_METHOD(StatsConfig&, statsConfig, (), ()); testing::NiceMock cluster_manager_; testing::NiceMock dispatcher_; @@ -160,6 +172,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { testing::NiceMock scope_; testing::NiceMock thread_local_; testing::NiceMock validation_context_; + testing::NiceMock stats_config_; Singleton::ManagerPtr singleton_manager_; testing::NiceMock admin_; Event::GlobalTimeSystem time_system_; @@ -167,6 +180,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { Grpc::ContextImpl grpc_context_; Router::ContextImpl router_context_; }; + } // namespace Configuration } // namespace Server } // namespace Envoy diff --git a/test/mocks/server/main.h b/test/mocks/server/main.h index 17e00013c27d..427a8689c8f4 100644 --- a/test/mocks/server/main.h +++ b/test/mocks/server/main.h @@ -19,8 +19,7 @@ class MockMain : public Main { ~MockMain() override = default; MOCK_METHOD(Upstream::ClusterManager*, clusterManager, ()); - MOCK_METHOD(std::list&, statsSinks, ()); - MOCK_METHOD(std::chrono::milliseconds, statsFlushInterval, (), (const)); + MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(const Watchdog&, mainThreadWatchdogConfig, (), (const)); MOCK_METHOD(const Watchdog&, workerWatchdogConfig, (), (const)); }; diff --git a/test/server/configuration_impl_test.cc b/test/server/configuration_impl_test.cc index 3d993bdda820..83b502f2dec8 100644 --- a/test/server/configuration_impl_test.cc +++ b/test/server/configuration_impl_test.cc @@ -3,12 +3,14 @@ #include #include "envoy/config/bootstrap/v3/bootstrap.pb.h" +#include "envoy/config/bootstrap/v3/bootstrap.pb.validate.h" #include "envoy/config/core/v3/base.pb.h" #include "envoy/config/metrics/v3/stats.pb.h" #include "common/api/api_impl.h" #include "common/config/well_known_names.h" #include "common/json/json_loader.h" +#include "common/protobuf/utility.h" #include "common/upstream/cluster_manager_impl.h" #include "server/configuration_impl.h" @@ -85,7 +87,8 @@ TEST_F(ConfigurationImplTest, DefaultStatsFlushInterval) { MainImpl config; config.initialize(bootstrap, server_, cluster_manager_factory_); - EXPECT_EQ(std::chrono::milliseconds(5000), config.statsFlushInterval()); + EXPECT_EQ(std::chrono::milliseconds(5000), config.statsConfig().flushInterval()); + EXPECT_FALSE(config.statsConfig().flushOnAdmin()); } TEST_F(ConfigurationImplTest, CustomStatsFlushInterval) { @@ -110,7 +113,80 @@ TEST_F(ConfigurationImplTest, CustomStatsFlushInterval) { MainImpl config; config.initialize(bootstrap, server_, cluster_manager_factory_); - EXPECT_EQ(std::chrono::milliseconds(500), config.statsFlushInterval()); + EXPECT_EQ(std::chrono::milliseconds(500), config.statsConfig().flushInterval()); + EXPECT_FALSE(config.statsConfig().flushOnAdmin()); +} + +TEST_F(ConfigurationImplTest, StatsOnAdmin) { + std::string json = R"EOF( + { + "stats_flush_on_admin": true, + + "admin": { + "access_log_path": "/dev/null", + "address": { + "socket_address": { + "address": "1.2.3.4", + "port_value": 5678 + } + } + } + } + )EOF"; + + auto bootstrap = Upstream::parseBootstrapFromV3Json(json); + + MainImpl config; + config.initialize(bootstrap, server_, cluster_manager_factory_); + + EXPECT_TRUE(config.statsConfig().flushOnAdmin()); +} + +TEST_F(ConfigurationImplTest, NegativeStatsOnAdmin) { + std::string json = R"EOF( + { + "stats_flush_on_admin": false, + + "admin": { + "access_log_path": "/dev/null", + "address": { + "socket_address": { + "address": "1.2.3.4", + "port_value": 5678 + } + } + } + } + )EOF"; + + auto bootstrap = Upstream::parseBootstrapFromV3Json(json); + EXPECT_THROW(TestUtility::validate(bootstrap), Envoy::ProtoValidationException); +} + +// This should throw a proto validation exception in the v4 api with the oneof promotion. +TEST_F(ConfigurationImplTest, IntervalAndAdminFlush) { + std::string json = R"EOF( + { + "stats_flush_on_admin": true, + "stats_flush_interval": "0.500s", + + "admin": { + "access_log_path": "/dev/null", + "address": { + "socket_address": { + "address": "1.2.3.4", + "port_value": 5678 + } + } + } + } + )EOF"; + + auto bootstrap = Upstream::parseBootstrapFromV3Json(json); + MainImpl config; + EXPECT_THROW_WITH_MESSAGE( + config.initialize(bootstrap, server_, cluster_manager_factory_), EnvoyException, + "Only one of stats_flush_interval or stats_flush_on_admin should be set!"); } TEST_F(ConfigurationImplTest, SetUpstreamClusterPerConnectionBufferLimit) { @@ -330,7 +406,7 @@ TEST_F(ConfigurationImplTest, ProtoSpecifiedStatsSink) { MainImpl config; config.initialize(bootstrap, server_, cluster_manager_factory_); - EXPECT_EQ(1, config.statsSinks().size()); + EXPECT_EQ(1, config.statsConfig().sinks().size()); } TEST_F(ConfigurationImplTest, StatsSinkWithInvalidName) { diff --git a/test/server/server_test.cc b/test/server/server_test.cc index cff76b9d5daf..5669a566527e 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -570,6 +570,33 @@ TEST_P(ServerStatsTest, FlushStats) { EXPECT_EQ(recent_lookups.value(), strobed_recent_lookups); } +TEST_P(ServerInstanceImplTest, FlushStatsOnAdmin) { + CustomStatsSinkFactory factory; + Registry::InjectFactory registered(factory); + options_.bootstrap_version_ = 3; + auto server_thread = + startTestServer("test/server/test_data/server/stats_sink_manual_flush_bootstrap.yaml", true); + EXPECT_TRUE(server_->statsConfig().flushOnAdmin()); + EXPECT_EQ(std::chrono::seconds(5), server_->statsConfig().flushInterval()); + + auto counter = TestUtility::findCounter(stats_store_, "stats.flushed"); + + time_system_.advanceTimeWait(std::chrono::seconds(6)); + EXPECT_EQ(0L, counter->value()); + + // Flush via admin. + Http::TestResponseHeaderMapImpl response_headers; + std::string body; + EXPECT_EQ(Http::Code::OK, server_->admin().request("/stats", "GET", response_headers, body)); + EXPECT_EQ(1L, counter->value()); + + time_system_.advanceTimeWait(std::chrono::seconds(6)); + EXPECT_EQ(1L, counter->value()); + + server_->dispatcher().post([&] { server_->shutdown(); }); + server_thread->join(); +} + // Default validation mode TEST_P(ServerInstanceImplTest, ValidationDefault) { options_.service_cluster_name_ = "some_cluster_name"; diff --git a/test/server/test_data/server/stats_sink_manual_flush_bootstrap.yaml b/test/server/test_data/server/stats_sink_manual_flush_bootstrap.yaml new file mode 100644 index 000000000000..bf66e0a87176 --- /dev/null +++ b/test/server/test_data/server/stats_sink_manual_flush_bootstrap.yaml @@ -0,0 +1,15 @@ +node: + id: bootstrap_id + cluster: bootstrap_cluster + locality: + zone: bootstrap_zone + sub_zone: bootstrap_sub_zone +admin: + access_log_path: {{ null_device_path }} + address: + socket_address: + address: {{ ntop_ip_loopback_address }} + port_value: 0 +stats_sinks: +- name: envoy.custom_stats_sink +stats_flush_on_admin: true From 02357c65f2c6187dedefba8d9bfffcad9f57f2ed Mon Sep 17 00:00:00 2001 From: htuch Date: Mon, 14 Dec 2020 14:55:25 -0500 Subject: [PATCH 21/49] api: define an API breaking change grace period. (#14390) Also, update other cases for when breaking changes are permitted. Signed-off-by: Harvey Tuch --- api/API_VERSIONING.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/api/API_VERSIONING.md b/api/API_VERSIONING.md index 25e80aaa8407..1757b0f2efce 100644 --- a/api/API_VERSIONING.md +++ b/api/API_VERSIONING.md @@ -67,8 +67,11 @@ experience a backward compatible break on a change. Specifically: may be granted for scenarios in which these stricter conditions model behavior already implied structurally or by documentation. -The exception to the above policy is for API versions tagged `vNalpha`. Within an alpha major -version, arbitrary breaking changes are allowed. +An exception to the above policy exists for: +* Changes made within 14 days of the introduction of a new API field or message. +* API versions tagged `vNalpha`. Within an alpha major version, arbitrary breaking changes are allowed. +* Any field, message or enum with a `[#not-implemented-hide:..` comment. +* Any proto with a `(udpa.annotations.file_status).work_in_progress` option annotation. Note that changes to default values for wrapped types, e.g. `google.protobuf.UInt32Value` are not governed by the above policy. Any management server requiring stability across Envoy API or From 17447f3ed1e84ac8f9505c7b78ed4ac8c8cd9f2f Mon Sep 17 00:00:00 2001 From: Christoph Pakulski Date: Mon, 14 Dec 2020 15:16:29 -0500 Subject: [PATCH 22/49] docs: Updated version history with stable releases. (#14306) Risk Level: n/a Testing: n/a Docs Changes: inline Release Notes: indeed. Signed-off-by: Christoph Pakulski --- GOVERNANCE.md | 3 ++- docs/root/version_history/v1.13.7.rst | 9 +++++++++ docs/root/version_history/v1.14.6.rst | 9 +++++++++ docs/root/version_history/v1.15.3.rst | 11 +++++++++++ docs/root/version_history/v1.16.1.rst | 13 +++++++++++++ docs/root/version_history/v1.16.2.rst | 9 +++++++++ docs/root/version_history/version_history.rst | 5 +++++ 7 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 docs/root/version_history/v1.13.7.rst create mode 100644 docs/root/version_history/v1.14.6.rst create mode 100644 docs/root/version_history/v1.15.3.rst create mode 100644 docs/root/version_history/v1.16.1.rst create mode 100644 docs/root/version_history/v1.16.2.rst diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 72a172e6259f..1cb5157443ea 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -108,7 +108,8 @@ or you can subscribe to the iCal feed [here](webcal://kubernetes.app.opsgenie.co * Do a new PR to setup the next version * Update [VERSION](VERSION) to the next development release. E.g., "1.7.0-dev". * `git mv docs/root/version_history/current.rst docs/root/version_history/v1.6.0.rst`, filling in the previous - release version number in the filename, and add an entry for the new file in the `toctree` in + release version number in the filename and delete empty sections (like Incompatible Behavior Changes, Minor Bahavior Changes, etc). + Add an entry for the new file in the `toctree` in [version_history.rst](docs/root/version_history/version_history.rst). * Create a new "current" version history file at the [release notes](docs/root/version_history/current.rst) for the following version. E.g., "1.7.0 (pending)". Use diff --git a/docs/root/version_history/v1.13.7.rst b/docs/root/version_history/v1.13.7.rst new file mode 100644 index 000000000000..0aab885e7c4f --- /dev/null +++ b/docs/root/version_history/v1.13.7.rst @@ -0,0 +1,9 @@ +1.13.7 (December 7, 2020) +========================= + +Changes +------- + +* tls: fix read resumption after triggering buffer high-watermark and all remaining request/response bytes are stored in the SSL connection's internal buffers. +* udp: fixed issue in which receiving truncated UDP datagrams would cause Envoy to crash. + diff --git a/docs/root/version_history/v1.14.6.rst b/docs/root/version_history/v1.14.6.rst new file mode 100644 index 000000000000..056627ab87eb --- /dev/null +++ b/docs/root/version_history/v1.14.6.rst @@ -0,0 +1,9 @@ +1.14.6 (December 7, 2020) +========================= + +Changes +------- + +* listener: fix crash when disabling or re-enabling listeners due to overload while processing LDS updates. +* tls: fix read resumption after triggering buffer high-watermark and all remaining request/response bytes are stored in the SSL connection's internal buffers. +* udp: fixed issue in which receiving truncated UDP datagrams would cause Envoy to crash. diff --git a/docs/root/version_history/v1.15.3.rst b/docs/root/version_history/v1.15.3.rst new file mode 100644 index 000000000000..00871be14e5e --- /dev/null +++ b/docs/root/version_history/v1.15.3.rst @@ -0,0 +1,11 @@ +1.15.3 (December 7, 2020) +========================= + +Changes +------- + +* listener: fix crash when disabling or re-enabling listeners due to overload while processing LDS updates. +* proxy_proto: fixed a bug where network filters would not have the correct downstreamRemoteAddress() when accessed from the StreamInfo. This could result in incorrect enforcement of RBAC rules in the RBAC network filter (but not in the RBAC HTTP filter), or incorrect access log addresses from tcp_proxy. +* tls: fix read resumption after triggering buffer high-watermark and all remaining request/response bytes are stored in the SSL connection's internal buffers. +* udp: fixed issue in which receiving truncated UDP datagrams would cause Envoy to crash. +* vrp: allow supervisord to open its log file. diff --git a/docs/root/version_history/v1.16.1.rst b/docs/root/version_history/v1.16.1.rst new file mode 100644 index 000000000000..81c72a624fba --- /dev/null +++ b/docs/root/version_history/v1.16.1.rst @@ -0,0 +1,13 @@ +1.16.1 (November 20, 2020) +========================== + +Bug Fixes +--------- +*Changes expected to improve the state of the world and are unlikely to have negative effects* + +* examples: examples use v3 configs. +* listener: fix crash when disabling or re-enabling listeners due to overload while processing LDS updates. +* proxy_proto: fixed a bug where the wrong downstream address got sent to upstream connections. +* proxy_proto: fixed a bug where network filters would not have the correct downstreamRemoteAddress() when accessed from the StreamInfo. This could result in incorrect enforcement of RBAC rules in the RBAC network filter (but not in the RBAC HTTP filter), or incorrect access log addresses from tcp_proxy. +* tls: fix read resumption after triggering buffer high-watermark and all remaining request/response bytes are stored in the SSL connection's internal buffers. +* udp: fixed issue in which receiving truncated UDP datagrams would cause Envoy to crash. diff --git a/docs/root/version_history/v1.16.2.rst b/docs/root/version_history/v1.16.2.rst new file mode 100644 index 000000000000..a7714eb3cb51 --- /dev/null +++ b/docs/root/version_history/v1.16.2.rst @@ -0,0 +1,9 @@ +1.16.2 (December 7, 2020) +========================= + +Bug Fixes +--------- +*Changes expected to improve the state of the world and are unlikely to have negative effects* + +* http: fixed URL parsing for HTTP/1.1 fully qualified URLs and connect requests containing IPv6 addresses. +* vrp: allow supervisord to open its log file. diff --git a/docs/root/version_history/version_history.rst b/docs/root/version_history/version_history.rst index 453bda753f1f..b25ef731208a 100644 --- a/docs/root/version_history/version_history.rst +++ b/docs/root/version_history/version_history.rst @@ -7,16 +7,21 @@ Version history :titlesonly: current + v1.16.2 + v1.16.1 v1.16.0 + v1.15.3 v1.15.2 v1.15.1 v1.15.0 + v1.14.6 v1.14.5 v1.14.4 v1.14.3 v1.14.2 v1.14.1 v1.14.0 + v1.13.7 v1.13.6 v1.13.5 v1.13.4 From 1f22f32509e205d199833fe71f83119b4722793d Mon Sep 17 00:00:00 2001 From: antonio Date: Mon, 14 Dec 2020 12:17:54 -0800 Subject: [PATCH 23/49] benchmark: convert recent_lookups_speed_test and symbol_table_speed_test so they use the benchmark framework (#14331) Risk Level: n/a test only (adding benchmarks) Testing: Docs Changes: Release Notes: Platform Specific Features: Signed-off-by: Antonio Vicente --- test/common/stats/BUILD | 19 +++- .../common/stats/recent_lookups_speed_test.cc | 25 ++--- test/common/stats/symbol_table_speed_test.cc | 93 +++++++++---------- 3 files changed, 64 insertions(+), 73 deletions(-) diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index c2c66200b1b9..ab9daa0d6643 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -4,7 +4,6 @@ load( "envoy_cc_benchmark_binary", "envoy_cc_fuzz_test", "envoy_cc_test", - "envoy_cc_test_binary", "envoy_cc_test_library", "envoy_package", ) @@ -61,8 +60,8 @@ envoy_cc_test( ], ) -envoy_cc_test_binary( - name = "recent_lookups_speed_test", +envoy_cc_benchmark_binary( + name = "recent_lookups_benchmark", srcs = ["recent_lookups_speed_test.cc"], external_deps = [ "benchmark", @@ -75,6 +74,11 @@ envoy_cc_test_binary( ], ) +envoy_benchmark_test( + name = "recent_lookups_benchmark_test", + benchmark_binary = "recent_lookups_benchmark", +) + envoy_cc_test( name = "stat_merger_test", srcs = ["stat_merger_test.cc"], @@ -178,8 +182,8 @@ envoy_cc_fuzz_test( ], ) -envoy_cc_test_binary( - name = "symbol_table_speed_test", +envoy_cc_benchmark_binary( + name = "symbol_table_benchmark", srcs = [ "make_elements_helper.cc", "make_elements_helper.h", @@ -201,6 +205,11 @@ envoy_cc_test_binary( ], ) +envoy_benchmark_test( + name = "symbol_table_benchmark_test", + benchmark_binary = "symbol_table_benchmark", +) + envoy_cc_test( name = "tag_extractor_impl_test", srcs = ["tag_extractor_impl_test.cc"], diff --git a/test/common/stats/recent_lookups_speed_test.cc b/test/common/stats/recent_lookups_speed_test.cc index 9af07ad4ef93..2d3c0f5e4ef3 100644 --- a/test/common/stats/recent_lookups_speed_test.cc +++ b/test/common/stats/recent_lookups_speed_test.cc @@ -40,6 +40,7 @@ class RecentLookupsSpeedTest { void test(benchmark::State& state) { for (auto _ : state) { + UNREFERENCED_PARAMETER(_); Envoy::Random::RandomGeneratorImpl random; for (uint64_t i = 0; i < lookups_.size(); ++i) { recent_lookups_.lookup(lookups_[random.random() % lookups_.size()]); @@ -52,32 +53,20 @@ class RecentLookupsSpeedTest { Envoy::Stats::RecentLookups recent_lookups_; }; -static void BM_LookupsMixed(benchmark::State& state) { +static void bmLookupsMixed(benchmark::State& state) { RecentLookupsSpeedTest speed_test(1000, 500); speed_test.test(state); } -BENCHMARK(BM_LookupsMixed); +BENCHMARK(bmLookupsMixed)->Unit(::benchmark::kMillisecond); -static void BM_LookupsNoEvictions(benchmark::State& state) { +static void bmLookupsNoEvictions(benchmark::State& state) { RecentLookupsSpeedTest speed_test(1000, 1000); speed_test.test(state); } -BENCHMARK(BM_LookupsNoEvictions); +BENCHMARK(bmLookupsNoEvictions)->Unit(::benchmark::kMillisecond); -static void BM_LookupsAllEvictions(benchmark::State& state) { +static void bmLookupsAllEvictions(benchmark::State& state) { RecentLookupsSpeedTest speed_test(1000, 10); speed_test.test(state); } -BENCHMARK(BM_LookupsAllEvictions); - -int main(int argc, char** argv) { - Envoy::Thread::MutexBasicLockable lock; - Envoy::Logger::Context logger_context(spdlog::level::warn, - Envoy::Logger::Logger::DEFAULT_LOG_FORMAT, lock, false); - benchmark::Initialize(&argc, argv); - - if (benchmark::ReportUnrecognizedArguments(argc, argv)) { - return 1; - } - benchmark::RunSpecifiedBenchmarks(); -} +BENCHMARK(bmLookupsAllEvictions)->Unit(::benchmark::kMillisecond); diff --git a/test/common/stats/symbol_table_speed_test.cc b/test/common/stats/symbol_table_speed_test.cc index a32f0d8c05b1..f5806c69964e 100644 --- a/test/common/stats/symbol_table_speed_test.cc +++ b/test/common/stats/symbol_table_speed_test.cc @@ -16,50 +16,53 @@ #include "benchmark/benchmark.h" // NOLINTNEXTLINE(readability-identifier-naming) -static void BM_CreateRace(benchmark::State& state) { - Envoy::Thread::ThreadFactory& thread_factory = Envoy::Thread::threadFactoryForTest(); +static void bmCreateRace(benchmark::State& state) { + for (auto _ : state) { + UNREFERENCED_PARAMETER(_); + Envoy::Thread::ThreadFactory& thread_factory = Envoy::Thread::threadFactoryForTest(); - // Make 100 threads, each of which will race to encode an overlapping set of - // symbols, triggering corner-cases in SymbolTable::toSymbol. - constexpr int num_threads = 36; - std::vector threads; - threads.reserve(num_threads); - Envoy::ConditionalInitializer access, wait; - absl::BlockingCounter accesses(num_threads); - Envoy::Stats::SymbolTableImpl table; - const absl::string_view stat_name_string = "here.is.a.stat.name"; - Envoy::Stats::StatNameStorage initial(stat_name_string, table); + // Make 100 threads, each of which will race to encode an overlapping set of + // symbols, triggering corner-cases in SymbolTable::toSymbol. + constexpr int num_threads = 100; + std::vector threads; + threads.reserve(num_threads); + Envoy::ConditionalInitializer access, wait; + absl::BlockingCounter accesses(num_threads); + Envoy::Stats::SymbolTableImpl table; + const absl::string_view stat_name_string = "here.is.a.stat.name"; + Envoy::Stats::StatNameStorage initial(stat_name_string, table); - for (int i = 0; i < num_threads; ++i) { - threads.push_back( - thread_factory.createThread([&access, &accesses, &state, &table, &stat_name_string]() { - // Block each thread on waking up a common condition variable, - // so we make it likely to race on access. - access.wait(); + for (int i = 0; i < num_threads; ++i) { + threads.push_back( + thread_factory.createThread([&access, &accesses, &table, &stat_name_string]() { + // Block each thread on waking up a common condition variable, + // so we make it likely to race on access. + access.wait(); - for (auto _ : state) { - Envoy::Stats::StatNameStorage second(stat_name_string, table); - second.free(table); - } - accesses.DecrementCount(); - })); - } + for (int count = 0; count < 1000; ++count) { + Envoy::Stats::StatNameStorage second(stat_name_string, table); + second.free(table); + } + accesses.DecrementCount(); + })); + } - // But when we access the already-existing symbols, we guarantee that no - // further mutex contentions occur. - access.setReady(); - accesses.Wait(); + // But when we access the already-existing symbols, we guarantee that no + // further mutex contentions occur. + access.setReady(); + accesses.Wait(); - for (auto& thread : threads) { - thread->join(); - } + for (auto& thread : threads) { + thread->join(); + } - initial.free(table); + initial.free(table); + } } -BENCHMARK(BM_CreateRace); +BENCHMARK(bmCreateRace)->Unit(::benchmark::kMillisecond); // NOLINTNEXTLINE(readability-identifier-naming) -static void BM_JoinStatNames(benchmark::State& state) { +static void bmJoinStatNames(benchmark::State& state) { Envoy::Stats::SymbolTableImpl symbol_table; Envoy::Stats::IsolatedStoreImpl store(symbol_table); Envoy::Stats::StatNamePool pool(symbol_table); @@ -69,13 +72,14 @@ static void BM_JoinStatNames(benchmark::State& state) { Envoy::Stats::StatName d = pool.add("d"); Envoy::Stats::StatName e = pool.add("e"); for (auto _ : state) { + UNREFERENCED_PARAMETER(_); Envoy::Stats::Utility::counterFromStatNames(store, Envoy::Stats::makeStatNames(a, b, c, d, e)); } } -BENCHMARK(BM_JoinStatNames); +BENCHMARK(bmJoinStatNames); // NOLINTNEXTLINE(readability-identifier-naming) -static void BM_JoinElements(benchmark::State& state) { +static void bmJoinElements(benchmark::State& state) { Envoy::Stats::SymbolTableImpl symbol_table; Envoy::Stats::IsolatedStoreImpl store(symbol_table); Envoy::Stats::StatNamePool pool(symbol_table); @@ -84,20 +88,9 @@ static void BM_JoinElements(benchmark::State& state) { Envoy::Stats::StatName c = pool.add("c"); Envoy::Stats::StatName e = pool.add("e"); for (auto _ : state) { + UNREFERENCED_PARAMETER(_); Envoy::Stats::Utility::counterFromElements( store, Envoy::Stats::makeElements(a, b, c, Envoy::Stats::DynamicName("d"), e)); } } -BENCHMARK(BM_JoinElements); - -int main(int argc, char** argv) { - Envoy::Thread::MutexBasicLockable lock; - Envoy::Logger::Context logger_context(spdlog::level::warn, - Envoy::Logger::Logger::DEFAULT_LOG_FORMAT, lock, false); - benchmark::Initialize(&argc, argv); - - if (benchmark::ReportUnrecognizedArguments(argc, argv)) { - return 1; - } - benchmark::RunSpecifiedBenchmarks(); -} +BENCHMARK(bmJoinElements); From 935a6598cd01324f03608ca77ebffc9608f7af81 Mon Sep 17 00:00:00 2001 From: Snow Pettersen Date: Mon, 14 Dec 2020 16:03:14 -0500 Subject: [PATCH 24/49] http: add support for skip filter match action (#14275) Adds support for associating a match tree with a HTTP filter, supporting a single "Skip" operation that will have the FM ignore the filter for the duration of the stream once matched. Signed-off-by: Snow Pettersen --- api/BUILD | 2 + .../config/common/matcher/v3/matcher.proto | 1 - .../common/matcher/v4alpha/matcher.proto | 1 - api/envoy/extensions/common/matching/v3/BUILD | 13 ++ .../matching/v3/extension_matcher.proto | 29 +++ .../extensions/common/matching/v4alpha/BUILD | 14 ++ .../matching/v4alpha/extension_matcher.proto | 32 +++ .../filters/common/matcher/action/v3/BUILD | 9 + .../matcher/action/v3/skip_action.proto | 19 ++ api/versioning/BUILD | 2 + .../common_messages/common_messages.rst | 2 + .../config/common/matcher/v3/matcher.proto | 1 - .../common/matcher/v4alpha/matcher.proto | 1 - .../envoy/extensions/common/matching/v3/BUILD | 13 ++ .../matching/v3/extension_matcher.proto | 29 +++ .../extensions/common/matching/v4alpha/BUILD | 14 ++ .../matching/v4alpha/extension_matcher.proto | 32 +++ .../filters/common/matcher/action/v3/BUILD | 9 + .../matcher/action/v3/skip_action.proto | 19 ++ include/envoy/http/BUILD | 1 + include/envoy/http/filter.h | 35 ++++ include/envoy/http/header_map.h | 4 + include/envoy/matcher/matcher.h | 2 + source/common/http/BUILD | 3 + source/common/http/filter_manager.cc | 67 ++++++- source/common/http/filter_manager.h | 186 ++++++++++++++++-- source/common/http/header_utility.cc | 31 +-- source/common/http/header_utility.h | 2 + test/common/http/conn_manager_utility_test.cc | 3 +- test/common/http/filter_manager_test.cc | 109 ++++++++++ test/common/router/config_impl_test.cc | 31 +-- test/common/router/router_test.cc | 6 +- .../filters/http/cdn_loop/config_test.cc | 2 +- .../filters/http/fault/fault_filter_test.cc | 162 ++++++++------- .../filters/network/mongo_proxy/proxy_test.cc | 7 +- .../network/redis_proxy/router_impl_test.cc | 14 +- test/mocks/http/mocks.h | 10 + 37 files changed, 786 insertions(+), 131 deletions(-) create mode 100644 api/envoy/extensions/common/matching/v3/BUILD create mode 100644 api/envoy/extensions/common/matching/v3/extension_matcher.proto create mode 100644 api/envoy/extensions/common/matching/v4alpha/BUILD create mode 100644 api/envoy/extensions/common/matching/v4alpha/extension_matcher.proto create mode 100644 api/envoy/extensions/filters/common/matcher/action/v3/BUILD create mode 100644 api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto create mode 100644 generated_api_shadow/envoy/extensions/common/matching/v3/BUILD create mode 100644 generated_api_shadow/envoy/extensions/common/matching/v3/extension_matcher.proto create mode 100644 generated_api_shadow/envoy/extensions/common/matching/v4alpha/BUILD create mode 100644 generated_api_shadow/envoy/extensions/common/matching/v4alpha/extension_matcher.proto create mode 100644 generated_api_shadow/envoy/extensions/filters/common/matcher/action/v3/BUILD create mode 100644 generated_api_shadow/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto diff --git a/api/BUILD b/api/BUILD index 82a1592921ad..f7c75a2d7fae 100644 --- a/api/BUILD +++ b/api/BUILD @@ -160,11 +160,13 @@ proto_library( "//envoy/extensions/clusters/dynamic_forward_proxy/v3:pkg", "//envoy/extensions/clusters/redis/v3:pkg", "//envoy/extensions/common/dynamic_forward_proxy/v3:pkg", + "//envoy/extensions/common/matching/v3:pkg", "//envoy/extensions/common/ratelimit/v3:pkg", "//envoy/extensions/common/tap/v3:pkg", "//envoy/extensions/compression/gzip/compressor/v3:pkg", "//envoy/extensions/compression/gzip/decompressor/v3:pkg", "//envoy/extensions/filters/common/fault/v3:pkg", + "//envoy/extensions/filters/common/matcher/action/v3:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3:pkg", "//envoy/extensions/filters/http/admission_control/v3alpha:pkg", "//envoy/extensions/filters/http/aws_lambda/v3:pkg", diff --git a/api/envoy/config/common/matcher/v3/matcher.proto b/api/envoy/config/common/matcher/v3/matcher.proto index 24ed5dafbdc5..2760be1b8bde 100644 --- a/api/envoy/config/common/matcher/v3/matcher.proto +++ b/api/envoy/config/common/matcher/v3/matcher.proto @@ -25,7 +25,6 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // might repeat several times until the final OnMatch (or no match) is decided. // // This API is a work in progress. -// [#not-implemented-hide:] message Matcher { // What to do if a match is successful. message OnMatch { diff --git a/api/envoy/config/common/matcher/v4alpha/matcher.proto b/api/envoy/config/common/matcher/v4alpha/matcher.proto index ab1f4de6cccc..7c846e1e9375 100644 --- a/api/envoy/config/common/matcher/v4alpha/matcher.proto +++ b/api/envoy/config/common/matcher/v4alpha/matcher.proto @@ -24,7 +24,6 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // might repeat several times until the final OnMatch (or no match) is decided. // // This API is a work in progress. -// [#not-implemented-hide:] message Matcher { option (udpa.annotations.versioning).previous_message_type = "envoy.config.common.matcher.v3.Matcher"; diff --git a/api/envoy/extensions/common/matching/v3/BUILD b/api/envoy/extensions/common/matching/v3/BUILD new file mode 100644 index 000000000000..5fa93360e655 --- /dev/null +++ b/api/envoy/extensions/common/matching/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/common/matcher/v3:pkg", + "//envoy/config/core/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/common/matching/v3/extension_matcher.proto b/api/envoy/extensions/common/matching/v3/extension_matcher.proto new file mode 100644 index 000000000000..47a065be6488 --- /dev/null +++ b/api/envoy/extensions/common/matching/v3/extension_matcher.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package envoy.extensions.common.matching.v3; + +import "envoy/config/common/matcher/v3/matcher.proto"; +import "envoy/config/core/v3/extension.proto"; + +import "udpa/annotations/migrate.proto"; +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.common.matching.v3"; +option java_outer_classname = "ExtensionMatcherProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Extension Matcher] + +// Wrapper around an existing extension that provides an associated matcher. This allows +// decorating an existing extension with a matcher, which can be used to match against +// relevant protocol data. +message ExtensionWithMatcher { + // The associated matcher. + config.common.matcher.v3.Matcher matcher = 1 [(validate.rules).message = {required: true}]; + + // The underlying extension config. + config.core.v3.TypedExtensionConfig extension_config = 2 + [(validate.rules).message = {required: true}]; +} diff --git a/api/envoy/extensions/common/matching/v4alpha/BUILD b/api/envoy/extensions/common/matching/v4alpha/BUILD new file mode 100644 index 000000000000..95ccc22a554a --- /dev/null +++ b/api/envoy/extensions/common/matching/v4alpha/BUILD @@ -0,0 +1,14 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/common/matcher/v4alpha:pkg", + "//envoy/config/core/v4alpha:pkg", + "//envoy/extensions/common/matching/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/common/matching/v4alpha/extension_matcher.proto b/api/envoy/extensions/common/matching/v4alpha/extension_matcher.proto new file mode 100644 index 000000000000..bb71cc5a095f --- /dev/null +++ b/api/envoy/extensions/common/matching/v4alpha/extension_matcher.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package envoy.extensions.common.matching.v4alpha; + +import "envoy/config/common/matcher/v4alpha/matcher.proto"; +import "envoy/config/core/v4alpha/extension.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.common.matching.v4alpha"; +option java_outer_classname = "ExtensionMatcherProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Extension Matcher] + +// Wrapper around an existing extension that provides an associated matcher. This allows +// decorating an existing extension with a matcher, which can be used to match against +// relevant protocol data. +message ExtensionWithMatcher { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.common.matching.v3.ExtensionWithMatcher"; + + // The associated matcher. + config.common.matcher.v4alpha.Matcher matcher = 1 [(validate.rules).message = {required: true}]; + + // The underlying extension config. + config.core.v4alpha.TypedExtensionConfig extension_config = 2 + [(validate.rules).message = {required: true}]; +} diff --git a/api/envoy/extensions/filters/common/matcher/action/v3/BUILD b/api/envoy/extensions/filters/common/matcher/action/v3/BUILD new file mode 100644 index 000000000000..ee92fb652582 --- /dev/null +++ b/api/envoy/extensions/filters/common/matcher/action/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto b/api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto new file mode 100644 index 000000000000..79879699990a --- /dev/null +++ b/api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package envoy.extensions.filters.common.matcher.action.v3; + +import "udpa/annotations/migrate.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.common.matcher.action.v3"; +option java_outer_classname = "SkipActionProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Common Match Actions] + +// Indicates that the associated filter should be skipped. +message SkipFilter { +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index 9b9ea97e97aa..41addf767671 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -43,11 +43,13 @@ proto_library( "//envoy/extensions/clusters/dynamic_forward_proxy/v3:pkg", "//envoy/extensions/clusters/redis/v3:pkg", "//envoy/extensions/common/dynamic_forward_proxy/v3:pkg", + "//envoy/extensions/common/matching/v3:pkg", "//envoy/extensions/common/ratelimit/v3:pkg", "//envoy/extensions/common/tap/v3:pkg", "//envoy/extensions/compression/gzip/compressor/v3:pkg", "//envoy/extensions/compression/gzip/decompressor/v3:pkg", "//envoy/extensions/filters/common/fault/v3:pkg", + "//envoy/extensions/filters/common/matcher/action/v3:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3:pkg", "//envoy/extensions/filters/http/admission_control/v3alpha:pkg", "//envoy/extensions/filters/http/aws_lambda/v3:pkg", diff --git a/docs/root/api-v3/common_messages/common_messages.rst b/docs/root/api-v3/common_messages/common_messages.rst index ceff6d6681ee..24309a9d7815 100644 --- a/docs/root/api-v3/common_messages/common_messages.rst +++ b/docs/root/api-v3/common_messages/common_messages.rst @@ -21,3 +21,5 @@ Common messages ../extensions/common/ratelimit/v3/ratelimit.proto ../extensions/filters/common/fault/v3/fault.proto ../extensions/network/socket_interface/v3/default_socket_interface.proto + ../extensions/common/matching/v3/extension_matcher.proto + ../extensions/filters/common/matcher/action/v3/skip_action.proto diff --git a/generated_api_shadow/envoy/config/common/matcher/v3/matcher.proto b/generated_api_shadow/envoy/config/common/matcher/v3/matcher.proto index 24ed5dafbdc5..2760be1b8bde 100644 --- a/generated_api_shadow/envoy/config/common/matcher/v3/matcher.proto +++ b/generated_api_shadow/envoy/config/common/matcher/v3/matcher.proto @@ -25,7 +25,6 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // might repeat several times until the final OnMatch (or no match) is decided. // // This API is a work in progress. -// [#not-implemented-hide:] message Matcher { // What to do if a match is successful. message OnMatch { diff --git a/generated_api_shadow/envoy/config/common/matcher/v4alpha/matcher.proto b/generated_api_shadow/envoy/config/common/matcher/v4alpha/matcher.proto index ab1f4de6cccc..7c846e1e9375 100644 --- a/generated_api_shadow/envoy/config/common/matcher/v4alpha/matcher.proto +++ b/generated_api_shadow/envoy/config/common/matcher/v4alpha/matcher.proto @@ -24,7 +24,6 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // might repeat several times until the final OnMatch (or no match) is decided. // // This API is a work in progress. -// [#not-implemented-hide:] message Matcher { option (udpa.annotations.versioning).previous_message_type = "envoy.config.common.matcher.v3.Matcher"; diff --git a/generated_api_shadow/envoy/extensions/common/matching/v3/BUILD b/generated_api_shadow/envoy/extensions/common/matching/v3/BUILD new file mode 100644 index 000000000000..5fa93360e655 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/common/matching/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/common/matcher/v3:pkg", + "//envoy/config/core/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/common/matching/v3/extension_matcher.proto b/generated_api_shadow/envoy/extensions/common/matching/v3/extension_matcher.proto new file mode 100644 index 000000000000..47a065be6488 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/common/matching/v3/extension_matcher.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package envoy.extensions.common.matching.v3; + +import "envoy/config/common/matcher/v3/matcher.proto"; +import "envoy/config/core/v3/extension.proto"; + +import "udpa/annotations/migrate.proto"; +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.common.matching.v3"; +option java_outer_classname = "ExtensionMatcherProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Extension Matcher] + +// Wrapper around an existing extension that provides an associated matcher. This allows +// decorating an existing extension with a matcher, which can be used to match against +// relevant protocol data. +message ExtensionWithMatcher { + // The associated matcher. + config.common.matcher.v3.Matcher matcher = 1 [(validate.rules).message = {required: true}]; + + // The underlying extension config. + config.core.v3.TypedExtensionConfig extension_config = 2 + [(validate.rules).message = {required: true}]; +} diff --git a/generated_api_shadow/envoy/extensions/common/matching/v4alpha/BUILD b/generated_api_shadow/envoy/extensions/common/matching/v4alpha/BUILD new file mode 100644 index 000000000000..95ccc22a554a --- /dev/null +++ b/generated_api_shadow/envoy/extensions/common/matching/v4alpha/BUILD @@ -0,0 +1,14 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/common/matcher/v4alpha:pkg", + "//envoy/config/core/v4alpha:pkg", + "//envoy/extensions/common/matching/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/common/matching/v4alpha/extension_matcher.proto b/generated_api_shadow/envoy/extensions/common/matching/v4alpha/extension_matcher.proto new file mode 100644 index 000000000000..bb71cc5a095f --- /dev/null +++ b/generated_api_shadow/envoy/extensions/common/matching/v4alpha/extension_matcher.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package envoy.extensions.common.matching.v4alpha; + +import "envoy/config/common/matcher/v4alpha/matcher.proto"; +import "envoy/config/core/v4alpha/extension.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.common.matching.v4alpha"; +option java_outer_classname = "ExtensionMatcherProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Extension Matcher] + +// Wrapper around an existing extension that provides an associated matcher. This allows +// decorating an existing extension with a matcher, which can be used to match against +// relevant protocol data. +message ExtensionWithMatcher { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.common.matching.v3.ExtensionWithMatcher"; + + // The associated matcher. + config.common.matcher.v4alpha.Matcher matcher = 1 [(validate.rules).message = {required: true}]; + + // The underlying extension config. + config.core.v4alpha.TypedExtensionConfig extension_config = 2 + [(validate.rules).message = {required: true}]; +} diff --git a/generated_api_shadow/envoy/extensions/filters/common/matcher/action/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/common/matcher/action/v3/BUILD new file mode 100644 index 000000000000..ee92fb652582 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/common/matcher/action/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/generated_api_shadow/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto b/generated_api_shadow/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto new file mode 100644 index 000000000000..79879699990a --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package envoy.extensions.filters.common.matcher.action.v3; + +import "udpa/annotations/migrate.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.common.matcher.action.v3"; +option java_outer_classname = "SkipActionProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Common Match Actions] + +// Indicates that the associated filter should be skipped. +message SkipFilter { +} diff --git a/include/envoy/http/BUILD b/include/envoy/http/BUILD index f17ce1cb5e14..a350d27f3e61 100644 --- a/include/envoy/http/BUILD +++ b/include/envoy/http/BUILD @@ -78,6 +78,7 @@ envoy_cc_library( "//include/envoy/common:scope_tracker_interface", "//include/envoy/event:dispatcher_interface", "//include/envoy/grpc:status", + "//include/envoy/matcher:matcher_interface", "//include/envoy/router:router_interface", "//include/envoy/ssl:connection_interface", "//include/envoy/stream_info:stream_info_interface", diff --git a/include/envoy/http/filter.h b/include/envoy/http/filter.h index f9710a45eb0a..4157dfc6819f 100644 --- a/include/envoy/http/filter.h +++ b/include/envoy/http/filter.h @@ -11,6 +11,7 @@ #include "envoy/grpc/status.h" #include "envoy/http/codec.h" #include "envoy/http/header_map.h" +#include "envoy/matcher/matcher.h" #include "envoy/router/router.h" #include "envoy/ssl/connection.h" #include "envoy/tracing/http_tracer.h" @@ -864,6 +865,14 @@ class StreamFilter : public virtual StreamDecoderFilter, public virtual StreamEn using StreamFilterSharedPtr = std::shared_ptr; +class HttpMatchingData { +public: + virtual ~HttpMatchingData() = default; + + virtual RequestHeaderMapOptConstRef requestHeaders() const PURE; + virtual ResponseHeaderMapOptConstRef responseHeaders() const PURE; +}; + /** * These callbacks are provided by the connection manager to the factory so that the factory can * build the filter chain in an application specific way. @@ -878,18 +887,44 @@ class FilterChainFactoryCallbacks { */ virtual void addStreamDecoderFilter(Http::StreamDecoderFilterSharedPtr filter) PURE; + /** + * Add a decoder filter that is used when reading stream data. + * @param filter supplies the filter to add. + * @param match_tree the MatchTree to associated with this filter. + */ + virtual void + addStreamDecoderFilter(Http::StreamDecoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) PURE; + /** * Add an encoder filter that is used when writing stream data. * @param filter supplies the filter to add. */ virtual void addStreamEncoderFilter(Http::StreamEncoderFilterSharedPtr filter) PURE; + /** + * Add an encoder filter that is used when writing stream data. + * @param filter supplies the filter to add. + * @param match_tree the MatchTree to associated with this filter. + */ + virtual void + addStreamEncoderFilter(Http::StreamEncoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) PURE; + /** * Add a decoder/encoder filter that is used both when reading and writing stream data. * @param filter supplies the filter to add. */ virtual void addStreamFilter(Http::StreamFilterSharedPtr filter) PURE; + /** + * Add a decoder/encoder filter that is used both when reading and writing stream data. + * @param filter supplies the filter to add. + * @param match_tree the MatchTree to associated with this filter. + */ + virtual void addStreamFilter(Http::StreamFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) PURE; + /** * Add an access log handler that is called when the stream is destroyed. * @param handler supplies the handler to add. diff --git a/include/envoy/http/header_map.h b/include/envoy/http/header_map.h index 26b6bdf926b6..06bbe4f7a91b 100644 --- a/include/envoy/http/header_map.h +++ b/include/envoy/http/header_map.h @@ -749,6 +749,8 @@ class RequestHeaderMap }; using RequestHeaderMapPtr = std::unique_ptr; using RequestHeaderMapOptRef = absl::optional>; +using RequestHeaderMapOptConstRef = absl::optional>; +using RequestHeaderMapOptRef = absl::optional>; // Request trailers. class RequestTrailerMap @@ -775,6 +777,8 @@ class ResponseHeaderMap }; using ResponseHeaderMapPtr = std::unique_ptr; using ResponseHeaderMapOptRef = absl::optional>; +using ResponseHeaderMapOptConstRef = + absl::optional>; // Response trailers. class ResponseTrailerMap diff --git a/include/envoy/matcher/matcher.h b/include/envoy/matcher/matcher.h index dec9cf6ce76a..613274ce389a 100644 --- a/include/envoy/matcher/matcher.h +++ b/include/envoy/matcher/matcher.h @@ -120,6 +120,8 @@ template class MatchTree { virtual MatchResult match(const DataType& matching_data) PURE; }; +template using MatchTreeSharedPtr = std::shared_ptr>; + // InputMatcher provides the interface for determining whether an input value matches. class InputMatcher { public: diff --git a/source/common/http/BUILD b/source/common/http/BUILD index 93f846420100..b63f3ce45b80 100644 --- a/source/common/http/BUILD +++ b/source/common/http/BUILD @@ -169,11 +169,14 @@ envoy_cc_library( deps = [ ":headers_lib", "//include/envoy/http:filter_interface", + "//include/envoy/matcher:matcher_interface", "//source/common/buffer:watermark_buffer_lib", "//source/common/common:linked_object", "//source/common/common:scope_tracker", "//source/common/grpc:common_lib", "//source/common/local_reply:local_reply_lib", + "//source/common/matcher:matcher_lib", + "@envoy_api//envoy/extensions/filters/common/matcher/action/v3:pkg_cc_proto", ], ) diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index 78096043a6f1..f0b7618794f4 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -1,6 +1,7 @@ #include "common/http/filter_manager.h" #include "envoy/http/header_map.h" +#include "envoy/matcher/matcher.h" #include "common/common/enum_to_int.h" #include "common/common/scope_tracker.h" @@ -241,6 +242,25 @@ void ActiveStreamFilterBase::clearRouteCache() { parent_.filter_manager_callbacks_.clearRouteCache(); } +void ActiveStreamFilterBase::evaluateMatchTreeWithNewData( + std::function update_func) { + if (match_tree_evaluated_ || !matching_data_) { + return; + } + + update_func(*matching_data_); + + const auto match_result = Matcher::evaluateMatch(*match_tree_, *matching_data_); + + match_tree_evaluated_ = match_result.match_state_ == Matcher::MatchState::MatchComplete; + + if (match_tree_evaluated_ && match_result.result_) { + if (SkipAction().typeUrl() == match_result.result_->typeUrl()) { + skip_ = true; + } + } +} + bool ActiveStreamDecoderFilter::canContinue() { // It is possible for the connection manager to respond directly to a request even while // a filter is trying to continue. If a response has already happened, we should not @@ -384,9 +404,11 @@ void ActiveStreamDecoderFilter::requestDataTooLarge() { } } -void FilterManager::addStreamDecoderFilterWorker(StreamDecoderFilterSharedPtr filter, - bool dual_filter) { - ActiveStreamDecoderFilterPtr wrapper(new ActiveStreamDecoderFilter(*this, filter, dual_filter)); +void FilterManager::addStreamDecoderFilterWorker( + StreamDecoderFilterSharedPtr filter, Matcher::MatchTreeSharedPtr matcher, + HttpMatchingDataImplSharedPtr matching_data, bool dual_filter) { + ActiveStreamDecoderFilterPtr wrapper(new ActiveStreamDecoderFilter( + *this, filter, std::move(matcher), std::move(matching_data), dual_filter)); filter->setDecoderFilterCallbacks(*wrapper); // Note: configured decoder filters are appended to decoder_filters_. // This means that if filters are configured in the following order (assume all three filters are @@ -399,9 +421,11 @@ void FilterManager::addStreamDecoderFilterWorker(StreamDecoderFilterSharedPtr fi LinkedList::moveIntoListBack(std::move(wrapper), decoder_filters_); } -void FilterManager::addStreamEncoderFilterWorker(StreamEncoderFilterSharedPtr filter, - bool dual_filter) { - ActiveStreamEncoderFilterPtr wrapper(new ActiveStreamEncoderFilter(*this, filter, dual_filter)); +void FilterManager::addStreamEncoderFilterWorker( + StreamEncoderFilterSharedPtr filter, Matcher::MatchTreeSharedPtr match_tree, + HttpMatchingDataImplSharedPtr matching_data, bool dual_filter) { + ActiveStreamEncoderFilterPtr wrapper(new ActiveStreamEncoderFilter( + *this, filter, std::move(match_tree), std::move(matching_data), dual_filter)); filter->setEncoderFilterCallbacks(*wrapper); // Note: configured encoder filters are prepended to encoder_filters_. // This means that if filters are configured in the following order (assume all three filters are @@ -439,6 +463,12 @@ void FilterManager::decodeHeaders(ActiveStreamDecoderFilter* filter, RequestHead std::list::iterator continue_data_entry = decoder_filters_.end(); for (; entry != decoder_filters_.end(); entry++) { + (*entry)->evaluateMatchTreeWithNewData( + [&](auto& matching_data) { matching_data.onRequestHeaders(headers); }); + if ((*entry)->skip_) { + continue; + } + ASSERT(!(state_.filter_call_state_ & FilterCallState::DecodeHeaders)); state_.filter_call_state_ |= FilterCallState::DecodeHeaders; (*entry)->end_stream_ = (end_stream && continue_data_entry == decoder_filters_.end()); @@ -515,6 +545,9 @@ void FilterManager::decodeData(ActiveStreamDecoderFilter* filter, Buffer::Instan commonDecodePrefix(filter, filter_iteration_start_state); for (; entry != decoder_filters_.end(); entry++) { + if ((*entry)->skip_) { + continue; + } // If the filter pointed by entry has stopped for all frame types, return now. if (handleDataIfStopAll(**entry, data, state_.decoder_filters_streaming_)) { return; @@ -646,6 +679,10 @@ void FilterManager::decodeTrailers(ActiveStreamDecoderFilter* filter, RequestTra commonDecodePrefix(filter, FilterIterationStartState::CanStartFromCurrent); for (; entry != decoder_filters_.end(); entry++) { + if ((*entry)->skip_) { + continue; + } + // If the filter pointed by entry has stopped for all frame type, return now. if ((*entry)->stoppedAll()) { return; @@ -674,6 +711,9 @@ void FilterManager::decodeMetadata(ActiveStreamDecoderFilter* filter, MetadataMa commonDecodePrefix(filter, FilterIterationStartState::CanStartFromCurrent); for (; entry != decoder_filters_.end(); entry++) { + if ((*entry)->skip_) { + continue; + } // If the filter pointed by entry has stopped for all frame type, stores metadata and returns. // If the filter pointed by entry hasn't returned from decodeHeaders, stores newly added // metadata in case decodeHeaders returns StopAllIteration. The latter can happen when headers @@ -885,6 +925,10 @@ void FilterManager::encode100ContinueHeaders(ActiveStreamEncoderFilter* filter, std::list::iterator entry = commonEncodePrefix(filter, false, FilterIterationStartState::AlwaysStartFromNext); for (; entry != encoder_filters_.end(); entry++) { + if ((*entry)->skip_) { + continue; + } + ASSERT(!(state_.filter_call_state_ & FilterCallState::Encode100ContinueHeaders)); state_.filter_call_state_ |= FilterCallState::Encode100ContinueHeaders; FilterHeadersStatus status = (*entry)->handle_->encode100ContinueHeaders(headers); @@ -926,6 +970,11 @@ void FilterManager::encodeHeaders(ActiveStreamEncoderFilter* filter, ResponseHea std::list::iterator continue_data_entry = encoder_filters_.end(); for (; entry != encoder_filters_.end(); entry++) { + (*entry)->evaluateMatchTreeWithNewData( + [&headers](auto& matching_data) { matching_data.onResponseHeaders(headers); }); + if ((*entry)->skip_) { + continue; + } ASSERT(!(state_.filter_call_state_ & FilterCallState::EncodeHeaders)); state_.filter_call_state_ |= FilterCallState::EncodeHeaders; (*entry)->end_stream_ = (end_stream && continue_data_entry == encoder_filters_.end()); @@ -980,6 +1029,9 @@ void FilterManager::encodeMetadata(ActiveStreamEncoderFilter* filter, commonEncodePrefix(filter, false, FilterIterationStartState::CanStartFromCurrent); for (; entry != encoder_filters_.end(); entry++) { + if ((*entry)->skip_) { + continue; + } // If the filter pointed by entry has stopped for all frame type, stores metadata and returns. // If the filter pointed by entry hasn't returned from encodeHeaders, stores newly added // metadata in case encodeHeaders returns StopAllIteration. The latter can happen when headers @@ -1048,6 +1100,9 @@ void FilterManager::encodeData(ActiveStreamEncoderFilter* filter, Buffer::Instan const bool trailers_exists_at_start = filter_manager_callbacks_.responseTrailers().has_value(); for (; entry != encoder_filters_.end(); entry++) { + if ((*entry)->skip_) { + continue; + } // If the filter pointed by entry has stopped for all frame type, return now. if (handleDataIfStopAll(**entry, data, state_.encoder_filters_streaming_)) { return; diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 4920db67bc15..21109a82d774 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -1,31 +1,139 @@ #pragma once +#include + +#include "envoy/extensions/filters/common/matcher/action/v3/skip_action.pb.h" #include "envoy/http/filter.h" #include "envoy/http/header_map.h" +#include "envoy/matcher/matcher.h" #include "common/buffer/watermark_buffer.h" #include "common/common/dump_state_utils.h" #include "common/common/linked_object.h" #include "common/common/logger.h" #include "common/grpc/common.h" +#include "common/http/header_utility.h" #include "common/http/headers.h" #include "common/local_reply/local_reply.h" +#include "common/matcher/matcher.h" namespace Envoy { namespace Http { class FilterManager; +class HttpMatchingDataImpl : public HttpMatchingData { +public: + static absl::string_view name() { return "http"; } + + void onRequestHeaders(const Http::RequestHeaderMap& request_headers) { + request_headers_ = &request_headers; + } + + void onResponseHeaders(const Http::ResponseHeaderMap& response_headers) { + response_headers_ = &response_headers; + } + + Http::RequestHeaderMapOptConstRef requestHeaders() const override { + if (request_headers_) { + return absl::make_optional(std::cref(*request_headers_)); + } + + return absl::nullopt; + } + + Http::ResponseHeaderMapOptConstRef responseHeaders() const override { + if (response_headers_) { + return absl::make_optional(std::cref(*response_headers_)); + } + + return absl::nullopt; + } + +private: + const Http::RequestHeaderMap* request_headers_{}; + const Http::ResponseHeaderMap* response_headers_{}; +}; + +using HttpMatchingDataImplSharedPtr = std::shared_ptr; + +template +class HttpHeadersDataInputBase : public Matcher::DataInput { +public: + explicit HttpHeadersDataInputBase(const std::string& name) : name_(name) {} + + virtual absl::optional> + headerMap(const HttpMatchingData& data) const PURE; + + Matcher::DataInputGetResult get(const HttpMatchingData& data) override { + const auto maybe_headers = headerMap(data); + + if (!maybe_headers) { + return {Matcher::DataInputGetResult::DataAvailability::NotAvailable, absl::nullopt}; + } + + auto header = maybe_headers->get().get(name_); + if (header.empty()) { + return {Matcher::DataInputGetResult::DataAvailability::AllDataAvailable, absl::nullopt}; + } + + if (header_as_string_result_) { + return {Matcher::DataInputGetResult::DataAvailability::AllDataAvailable, + header_as_string_result_->result()}; + } + + header_as_string_result_ = HeaderUtility::getAllOfHeaderAsString(header, ","); + + return {Matcher::DataInputGetResult::DataAvailability::AllDataAvailable, + header_as_string_result_->result()}; + } + +private: + const LowerCaseString name_; + absl::optional header_as_string_result_; +}; + +class HttpRequestHeadersDataInput : public HttpHeadersDataInputBase { +public: + explicit HttpRequestHeadersDataInput(const std::string& name) : HttpHeadersDataInputBase(name) {} + + absl::optional> + headerMap(const HttpMatchingData& data) const override { + return data.requestHeaders(); + } +}; + +class HttpResponseHeadersDataInput : public HttpHeadersDataInputBase { +public: + explicit HttpResponseHeadersDataInput(const std::string& name) : HttpHeadersDataInputBase(name) {} + + absl::optional> + headerMap(const HttpMatchingData& data) const override { + return data.responseHeaders(); + } +}; + +class SkipAction : public Matcher::ActionBase< + envoy::extensions::filters::common::matcher::action::v3::SkipFilter> {}; + /** * Base class wrapper for both stream encoder and decoder filters. + * + * This class is responsible for performing matching and updating match data when a match tree is + * configured for the associated filter. When not using a match tree, only minimal overhead (i.e. + * memory overhead of unused fields) should apply. */ struct ActiveStreamFilterBase : public virtual StreamFilterCallbacks, Logger::Loggable { - ActiveStreamFilterBase(FilterManager& parent, bool dual_filter) + ActiveStreamFilterBase(FilterManager& parent, bool dual_filter, + Matcher::MatchTreeSharedPtr match_tree, + HttpMatchingDataImplSharedPtr matching_data) : parent_(parent), iteration_state_(IterationState::Continue), + match_tree_(std::move(match_tree)), matching_data_(std::move(matching_data)), iterate_from_current_filter_(false), headers_continued_(false), continue_headers_continued_(false), end_stream_(false), dual_filter_(dual_filter), - decode_headers_called_(false), encode_headers_called_(false) {} + decode_headers_called_(false), encode_headers_called_(false), match_tree_evaluated_(false), + skip_(false) {} // Functions in the following block are called after the filter finishes processing // corresponding data. Those functions handle state updates and data storage (if needed) @@ -95,6 +203,8 @@ struct ActiveStreamFilterBase : public virtual StreamFilterCallbacks, return saved_response_metadata_.get(); } + void evaluateMatchTreeWithNewData(std::function update_func); + // A vector to save metadata when the current filter's [de|en]codeMetadata() can not be called, // either because [de|en]codeHeaders() of the current filter returns StopAllIteration or because // [de|en]codeHeaders() adds new metadata to [de|en]code, but we don't know @@ -112,6 +222,10 @@ struct ActiveStreamFilterBase : public virtual StreamFilterCallbacks, }; FilterManager& parent_; IterationState iteration_state_; + + Matcher::MatchTreeSharedPtr match_tree_; + HttpMatchingDataImplSharedPtr matching_data_; + // If the filter resumes iteration from a StopAllBuffer/Watermark state, the current filter // hasn't parsed data and trailers. As a result, the filter iteration should start with the // current filter instead of the next one. If true, filter iteration starts with the current @@ -124,6 +238,8 @@ struct ActiveStreamFilterBase : public virtual StreamFilterCallbacks, const bool dual_filter_ : 1; bool decode_headers_called_ : 1; bool encode_headers_called_ : 1; + bool match_tree_evaluated_ : 1; + bool skip_ : 1; }; /** @@ -133,8 +249,11 @@ struct ActiveStreamDecoderFilter : public ActiveStreamFilterBase, public StreamDecoderFilterCallbacks, LinkedObject { ActiveStreamDecoderFilter(FilterManager& parent, StreamDecoderFilterSharedPtr filter, - bool dual_filter) - : ActiveStreamFilterBase(parent, dual_filter), handle_(filter) {} + Matcher::MatchTreeSharedPtr match_tree, + HttpMatchingDataImplSharedPtr matching_data, bool dual_filter) + : ActiveStreamFilterBase(parent, dual_filter, std::move(match_tree), + std::move(matching_data)), + handle_(filter) {} // ActiveStreamFilterBase bool canContinue() override; @@ -219,8 +338,11 @@ struct ActiveStreamEncoderFilter : public ActiveStreamFilterBase, public StreamEncoderFilterCallbacks, LinkedObject { ActiveStreamEncoderFilter(FilterManager& parent, StreamEncoderFilterSharedPtr filter, - bool dual_filter) - : ActiveStreamFilterBase(parent, dual_filter), handle_(filter) {} + Matcher::MatchTreeSharedPtr match_tree, + HttpMatchingDataImplSharedPtr matching_data, bool dual_filter) + : ActiveStreamFilterBase(parent, dual_filter, std::move(match_tree), + std::move(matching_data)), + handle_(filter) {} // ActiveStreamFilterBase bool canContinue() override { return true; } @@ -517,14 +639,50 @@ class FilterManager : public ScopeTrackedObject, // Http::FilterChainFactoryCallbacks void addStreamDecoderFilter(StreamDecoderFilterSharedPtr filter) override { - addStreamDecoderFilterWorker(filter, false); + addStreamDecoderFilterWorker(filter, nullptr, nullptr, false); + } + void addStreamDecoderFilter(StreamDecoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) override { + if (match_tree) { + auto matching_data = std::make_shared(); + addStreamDecoderFilterWorker(filter, std::move(match_tree), std::move(matching_data), false); + return; + } + + addStreamDecoderFilterWorker(filter, nullptr, nullptr, false); } void addStreamEncoderFilter(StreamEncoderFilterSharedPtr filter) override { - addStreamEncoderFilterWorker(filter, false); + addStreamEncoderFilterWorker(filter, nullptr, nullptr, false); + } + void addStreamEncoderFilter(StreamEncoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) override { + if (match_tree) { + addStreamEncoderFilterWorker(filter, std::move(match_tree), + std::make_shared(), false); + return; + } + + addStreamEncoderFilterWorker(filter, nullptr, nullptr, false); } void addStreamFilter(StreamFilterSharedPtr filter) override { - addStreamDecoderFilterWorker(filter, true); - addStreamEncoderFilterWorker(filter, true); + addStreamDecoderFilterWorker(filter, nullptr, nullptr, true); + addStreamEncoderFilterWorker(filter, nullptr, nullptr, true); + } + void addStreamFilter(StreamFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) override { + // Note that we share the match data and tree between the two filters to allow things like + // matching on both request and response data. + // TODO(snowp): The match tree might be fully evaluated twice, ideally we should expose + // the result to both filters after the first match evaluation. + if (match_tree) { + auto matching_data = std::make_shared(); + addStreamDecoderFilterWorker(filter, match_tree, matching_data, true); + addStreamEncoderFilterWorker(filter, std::move(match_tree), std::move(matching_data), true); + return; + } + + addStreamDecoderFilterWorker(filter, nullptr, nullptr, true); + addStreamEncoderFilterWorker(filter, nullptr, nullptr, true); } void addAccessLogHandler(AccessLog::InstanceSharedPtr handler) override; @@ -606,8 +764,12 @@ class FilterManager : public ScopeTrackedObject, void decodeMetadata(MetadataMap& metadata_map) { decodeMetadata(nullptr, metadata_map); } // TODO(snowp): Make private as filter chain construction is moved into FM. - void addStreamDecoderFilterWorker(StreamDecoderFilterSharedPtr filter, bool dual_filter); - void addStreamEncoderFilterWorker(StreamEncoderFilterSharedPtr filter, bool dual_filter); + void addStreamDecoderFilterWorker(StreamDecoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree, + HttpMatchingDataImplSharedPtr matching_data, bool dual_filter); + void addStreamEncoderFilterWorker(StreamEncoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree, + HttpMatchingDataImplSharedPtr matching_data, bool dual_filter); void disarmRequestTimeout(); diff --git a/source/common/http/header_utility.cc b/source/common/http/header_utility.cc index c59efda824db..2d22bde10d14 100644 --- a/source/common/http/header_utility.cc +++ b/source/common/http/header_utility.cc @@ -94,6 +94,25 @@ bool HeaderUtility::matchHeaders(const HeaderMap& request_headers, return true; } +HeaderUtility::GetAllOfHeaderAsStringResult +HeaderUtility::getAllOfHeaderAsString(const HeaderMap::GetResult& header_value, + absl::string_view separator) { + GetAllOfHeaderAsStringResult result; + // In this case we concatenate all found headers using a delimiter before performing the + // final match. We use an InlinedVector of absl::string_view to invoke the optimized join + // algorithm. This requires a copying phase before we invoke join. The 3 used as the inline + // size has been arbitrarily chosen. + // TODO(mattklein123): Do we need to normalize any whitespace here? + absl::InlinedVector string_view_vector; + string_view_vector.reserve(header_value.size()); + for (size_t i = 0; i < header_value.size(); i++) { + string_view_vector.push_back(header_value[i]->value().getStringView()); + } + result.result_backing_string_ = absl::StrJoin(string_view_vector, separator); + + return result; +} + HeaderUtility::GetAllOfHeaderAsStringResult HeaderUtility::getAllOfHeaderAsString(const HeaderMap& headers, const Http::LowerCaseString& key, absl::string_view separator) { @@ -108,17 +127,7 @@ HeaderUtility::getAllOfHeaderAsString(const HeaderMap& headers, const Http::Lowe "envoy.reloadable_features.http_match_on_all_headers")) { result.result_ = header_value[0]->value().getStringView(); } else { - // In this case we concatenate all found headers using a delimiter before performing the - // final match. We use an InlinedVector of absl::string_view to invoke the optimized join - // algorithm. This requires a copying phase before we invoke join. The 3 used as the inline - // size has been arbitrarily chosen. - // TODO(mattklein123): Do we need to normalize any whitespace here? - absl::InlinedVector string_view_vector; - string_view_vector.reserve(header_value.size()); - for (size_t i = 0; i < header_value.size(); i++) { - string_view_vector.push_back(header_value[i]->value().getStringView()); - } - result.result_backing_string_ = absl::StrJoin(string_view_vector, separator); + return getAllOfHeaderAsString(header_value, separator); } return result; diff --git a/source/common/http/header_utility.h b/source/common/http/header_utility.h index 864b1d942ad8..ff320408532e 100644 --- a/source/common/http/header_utility.h +++ b/source/common/http/header_utility.h @@ -47,6 +47,8 @@ class HeaderUtility { friend class HeaderUtility; }; + static GetAllOfHeaderAsStringResult getAllOfHeaderAsString(const HeaderMap::GetResult& header, + absl::string_view separator = ","); static GetAllOfHeaderAsStringResult getAllOfHeaderAsString(const HeaderMap& headers, const Http::LowerCaseString& key, absl::string_view separator = ","); diff --git a/test/common/http/conn_manager_utility_test.cc b/test/common/http/conn_manager_utility_test.cc index e22c8feccff2..24ae4608e4cb 100644 --- a/test/common/http/conn_manager_utility_test.cc +++ b/test/common/http/conn_manager_utility_test.cc @@ -433,7 +433,8 @@ TEST_F(ConnectionManagerUtilityTest, EdgeRequestRegenerateRequestIdAndWipeDownst {"x-request-id", "will_be_regenerated"}}; EXPECT_CALL(random_, uuid()); - EXPECT_CALL(runtime_.snapshot_, featureEnabled("tracing.client_enabled", Matcher(_))) + EXPECT_CALL(runtime_.snapshot_, + featureEnabled("tracing.client_enabled", testing::Matcher(_))) .Times(0); EXPECT_EQ((MutateRequestRet{"34.0.0.1:0", false}), callMutateRequestHeaders(headers, Protocol::Http2)); diff --git a/test/common/http/filter_manager_test.cc b/test/common/http/filter_manager_test.cc index fdc6e6a4b6be..08a34e7054f3 100644 --- a/test/common/http/filter_manager_test.cc +++ b/test/common/http/filter_manager_test.cc @@ -1,6 +1,10 @@ +#include "envoy/http/filter.h" +#include "envoy/http/header_map.h" +#include "envoy/matcher/matcher.h" #include "envoy/stream_info/filter_state.h" #include "common/http/filter_manager.h" +#include "common/matcher/exact_map_matcher.h" #include "common/stream_info/filter_state_impl.h" #include "common/stream_info/stream_info_impl.h" @@ -139,6 +143,111 @@ TEST_F(FilterManagerTest, SendLocalReplyDuringEncodingGrpcClassiciation) { filter_manager_->decodeHeaders(*grpc_headers, true); filter_manager_->destroyFilters(); } + +Matcher::MatchTreeSharedPtr createRequestMatchingTree() { + auto tree = std::make_shared>( + std::make_unique("match-header"), absl::nullopt); + + tree->addChild("match", Matcher::OnMatch{ + []() { return std::make_unique(); }, nullptr}); + + return tree; +} + +Matcher::MatchTreeSharedPtr createRequestAndResponseMatchingTree() { + auto tree = std::make_shared>( + std::make_unique("match-header"), absl::nullopt); + + tree->addChild("match", + Matcher::OnMatch{[]() { return std::make_unique(); }, + createRequestMatchingTree()}); + + return tree; +} + +TEST_F(FilterManagerTest, MatchTreeSkipActionDecodingHeaders) { + initialize(); + + // The filter is added, but since we match on the request header we skip the filter. + std::shared_ptr decoder_filter(new MockStreamDecoderFilter()); + EXPECT_CALL(*decoder_filter, setDecoderFilterCallbacks(_)); + EXPECT_CALL(*decoder_filter, onDestroy()); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillRepeatedly(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamDecoderFilter(decoder_filter, createRequestMatchingTree()); + })); + + RequestHeaderMapPtr grpc_headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"match-header", "match"}, + {"content-type", "application/grpc"}}}; + + ON_CALL(filter_manager_callbacks_, requestHeaders()) + .WillByDefault(Return(absl::make_optional(std::ref(*grpc_headers)))); + filter_manager_->createFilterChain(); + + filter_manager_->requestHeadersInitialized(); + filter_manager_->decodeHeaders(*grpc_headers, true); + filter_manager_->destroyFilters(); +} + +TEST_F(FilterManagerTest, MatchTreeSkipActionRequestAndResponseHeaders) { + initialize(); + + EXPECT_CALL(dispatcher_, setTrackedObject(_)).Times(2); + + // This stream filter will skip further callbacks once it sees both the request and response + // header. As such, it should see the decoding callbacks but none of the encoding callbacks. + auto stream_filter = std::make_shared(); + EXPECT_CALL(*stream_filter, setDecoderFilterCallbacks(_)); + EXPECT_CALL(*stream_filter, setEncoderFilterCallbacks(_)); + EXPECT_CALL(*stream_filter, onDestroy()); + EXPECT_CALL(*stream_filter, decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*stream_filter, decodeData(_, true)).WillOnce(Return(FilterDataStatus::Continue)); + + auto decoder_filter = std::make_shared>(); + EXPECT_CALL(*decoder_filter, decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*decoder_filter, decodeData(_, true)) + .WillOnce(Invoke([&](auto&, bool) -> FilterDataStatus { + ResponseHeaderMapPtr headers{new TestResponseHeaderMapImpl{ + {":status", "200"}, {"match-header", "match"}, {"content-type", "application/grpc"}}}; + decoder_filter->callbacks_->encodeHeaders(std::move(headers), false, "details"); + + Buffer::OwnedImpl data("data"); + decoder_filter->callbacks_->encodeData(data, true); + return FilterDataStatus::StopIterationNoBuffer; + })); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillRepeatedly(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamFilter(stream_filter, createRequestAndResponseMatchingTree()); + callbacks.addStreamDecoderFilter(decoder_filter); + })); + + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"match-header", "match"}, + {"content-type", "application/grpc"}}}; + Buffer::OwnedImpl data("data"); + + ON_CALL(filter_manager_callbacks_, requestHeaders()) + .WillByDefault(Return(absl::make_optional(std::ref(*headers)))); + filter_manager_->createFilterChain(); + + EXPECT_CALL(filter_manager_callbacks_, encodeHeaders(_, _)); + EXPECT_CALL(filter_manager_callbacks_, endStream()); + + filter_manager_->requestHeadersInitialized(); + filter_manager_->decodeHeaders(*headers, false); + filter_manager_->decodeData(data, true); + filter_manager_->destroyFilters(); +} } // namespace } // namespace Http } // namespace Envoy \ No newline at end of file diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 2913f1f46629..d50e0866000f 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -39,7 +39,6 @@ using testing::_; using testing::ContainerEq; using testing::Eq; -using testing::Matcher; using testing::MockFunction; using testing::NiceMock; using testing::Return; @@ -3036,15 +3035,17 @@ TEST_F(RouteMatcherTest, FractionalRuntime) { TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, false); - EXPECT_CALL(snapshot, featureEnabled("bogus_key", - Matcher(_), 41)) + EXPECT_CALL(snapshot, + featureEnabled("bogus_key", + testing::Matcher(_), 41)) .WillRepeatedly(Return(true)); EXPECT_EQ( "something_else", config.route(genHeaders("www.lyft.com", "/foo", "GET"), 41)->routeEntry()->clusterName()); - EXPECT_CALL(snapshot, featureEnabled("bogus_key", - Matcher(_), 43)) + EXPECT_CALL(snapshot, + featureEnabled("bogus_key", + testing::Matcher(_), 43)) .WillRepeatedly(Return(false)); EXPECT_EQ( "www2", @@ -5616,11 +5617,13 @@ TEST_F(RoutePropertyTest, DEPRECATED_FEATURE_TEST(TestVHostCorsConfig)) { )EOF"; Runtime::MockSnapshot snapshot; - EXPECT_CALL(snapshot, featureEnabled("cors.www.enabled", - Matcher(_))) + EXPECT_CALL(snapshot, + featureEnabled("cors.www.enabled", + testing::Matcher(_))) .WillOnce(Return(false)); - EXPECT_CALL(snapshot, featureEnabled("cors.www.shadow_enabled", - Matcher(_))) + EXPECT_CALL(snapshot, + featureEnabled("cors.www.shadow_enabled", + testing::Matcher(_))) .WillOnce(Return(true)); EXPECT_CALL(factory_context_.runtime_loader_, snapshot()).WillRepeatedly(ReturnRef(snapshot)); @@ -5673,11 +5676,13 @@ TEST_F(RoutePropertyTest, TestRouteCorsConfig) { )EOF"; Runtime::MockSnapshot snapshot; - EXPECT_CALL(snapshot, featureEnabled("cors.www.enabled", - Matcher(_))) + EXPECT_CALL(snapshot, + featureEnabled("cors.www.enabled", + testing::Matcher(_))) .WillOnce(Return(false)); - EXPECT_CALL(snapshot, featureEnabled("cors.www.shadow_enabled", - Matcher(_))) + EXPECT_CALL(snapshot, + featureEnabled("cors.www.shadow_enabled", + testing::Matcher(_))) .WillOnce(Return(true)); EXPECT_CALL(factory_context_.runtime_loader_, snapshot()).WillRepeatedly(ReturnRef(snapshot)); diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 8414d0f307e6..663f3f326685 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -54,7 +54,6 @@ using testing::AtLeast; using testing::Eq; using testing::InSequence; using testing::Invoke; -using testing::Matcher; using testing::MockFunction; using testing::NiceMock; using testing::Property; @@ -6038,8 +6037,9 @@ TEST(RouterFilterUtilityTest, ShouldShadow) { fractional_percent.set_denominator(envoy::type::v3::FractionalPercent::TEN_THOUSAND); TestShadowPolicy policy("cluster", "foo", fractional_percent); NiceMock runtime; - EXPECT_CALL(runtime.snapshot_, - featureEnabled("foo", Matcher(_), 3)) + EXPECT_CALL( + runtime.snapshot_, + featureEnabled("foo", testing::Matcher(_), 3)) .WillOnce(Return(true)); EXPECT_TRUE(FilterUtility::shouldShadow(policy, runtime, 3)); } diff --git a/test/extensions/filters/http/cdn_loop/config_test.cc b/test/extensions/filters/http/cdn_loop/config_test.cc index 88a4a44462af..7c0fcdbf7f25 100644 --- a/test/extensions/filters/http/cdn_loop/config_test.cc +++ b/test/extensions/filters/http/cdn_loop/config_test.cc @@ -21,7 +21,7 @@ TEST(CdnLoopFilterFactoryTest, ValidValuesWork) { NiceMock context; Http::StreamDecoderFilterSharedPtr filter; Http::MockFilterChainFactoryCallbacks filter_callbacks; - EXPECT_CALL(filter_callbacks, addStreamDecoderFilter).WillOnce(::testing::SaveArg<0>(&filter)); + EXPECT_CALL(filter_callbacks, addStreamDecoderFilter(_)).WillOnce(::testing::SaveArg<0>(&filter)); envoy::extensions::filters::http::cdn_loop::v3alpha::CdnLoopConfig config; config.set_cdn_id("cdn"); diff --git a/test/extensions/filters/http/fault/fault_filter_test.cc b/test/extensions/filters/http/fault/fault_filter_test.cc index f22c9ed88eaf..63707e4d96e9 100644 --- a/test/extensions/filters/http/fault/fault_filter_test.cc +++ b/test/extensions/filters/http/fault/fault_filter_test.cc @@ -31,7 +31,6 @@ using testing::_; using testing::AnyNumber; -using testing::Matcher; using testing::NiceMock; using testing::Return; using testing::ReturnRef; @@ -241,9 +240,10 @@ TEST_F(FaultFilterTest, AbortWithHttpStatus) { .Times(0); // Abort related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", 429)) @@ -288,9 +288,10 @@ TEST_F(FaultFilterTest, HeaderAbortWithHttpStatus) { .Times(0); // Abort related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", 429)) @@ -340,9 +341,10 @@ TEST_F(FaultFilterTest, AbortWithGrpcStatus) { .Times(0); // Abort related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.grpc_status", 5)) @@ -389,9 +391,10 @@ TEST_F(FaultFilterTest, HeaderAbortWithGrpcStatus) { .Times(0); // Abort related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.grpc_status", 5)) @@ -439,9 +442,10 @@ TEST_F(FaultFilterTest, HeaderAbortWithHttpAndGrpcStatus) { .Times(0); // Abort related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", 429)) @@ -477,9 +481,10 @@ TEST_F(FaultFilterTest, FixedDelayZeroDuration) { SetUpTest(fixed_delay_only_yaml); // Delay related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); // Return 0ms delay @@ -504,9 +509,10 @@ TEST_F(FaultFilterTest, Overflow) { SetUpTest(fixed_delay_only_yaml); // Delay related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); // Return 1ms delay @@ -546,9 +552,10 @@ TEST_F(FaultFilterTest, FixedDelayDeprecatedPercentAndNonZeroDuration) { .WillOnce(Return(std::numeric_limits::max())); // Delay related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(50)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(50)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) @@ -590,9 +597,10 @@ TEST_F(FaultFilterTest, DelayForDownstreamCluster) { request_headers_.addCopy("x-envoy-downstream-service-cluster", "cluster"); // Delay related calls. - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.cluster.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.cluster.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) @@ -637,9 +645,10 @@ TEST_F(FaultFilterTest, FixedDelayAndAbortDownstream) { request_headers_.addCopy("x-envoy-downstream-service-cluster", "cluster"); // Delay related calls. - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.cluster.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.cluster.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) @@ -657,9 +666,10 @@ TEST_F(FaultFilterTest, FixedDelayAndAbortDownstream) { EXPECT_EQ(1UL, config_->stats().active_faults_.value()); // Abort related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.cluster.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.cluster.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", 503)) @@ -699,9 +709,10 @@ TEST_F(FaultFilterTest, FixedDelayAndAbort) { .WillOnce(Return(std::numeric_limits::max())); // Delay related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) @@ -717,9 +728,10 @@ TEST_F(FaultFilterTest, FixedDelayAndAbort) { filter_->decodeHeaders(request_headers_, false)); // Abort related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", 503)) @@ -753,9 +765,10 @@ TEST_F(FaultFilterTest, FixedDelayAndAbortDownstreamNodes) { .WillOnce(Return(std::numeric_limits::max())); // Delay related calls. - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) .WillOnce(Return(5000UL)); @@ -770,9 +783,10 @@ TEST_F(FaultFilterTest, FixedDelayAndAbortDownstreamNodes) { filter_->decodeHeaders(request_headers_, false)); // Abort related calls. - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", 503)) .WillOnce(Return(503)); @@ -813,9 +827,10 @@ TEST_F(FaultFilterTest, FixedDelayAndAbortHeaderMatchSuccess) { .WillOnce(Return(std::numeric_limits::max())); // Delay related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) @@ -831,9 +846,10 @@ TEST_F(FaultFilterTest, FixedDelayAndAbortHeaderMatchSuccess) { filter_->decodeHeaders(request_headers_, false)); // Abort related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.abort.abort_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.abort.abort_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", 503)) @@ -865,12 +881,12 @@ TEST_F(FaultFilterTest, FixedDelayAndAbortHeaderMatchFail) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(_))) + testing::Matcher(_))) .Times(0); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", _)).Times(0); EXPECT_CALL(runtime_.snapshot_, featureEnabled("fault.http.abort.abort_percent", - Matcher(_))) + testing::Matcher(_))) .Times(0); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", _)).Times(0); EXPECT_CALL(decoder_filter_callbacks_, encodeHeaders_(_, _)).Times(0); @@ -894,9 +910,10 @@ TEST_F(FaultFilterTest, TimerResetAfterStreamReset) { .WillOnce(Return(std::numeric_limits::max())); // Prep up with a 5s delay - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) @@ -921,7 +938,7 @@ TEST_F(FaultFilterTest, TimerResetAfterStreamReset) { // The timer callback should never be called. EXPECT_CALL(runtime_.snapshot_, featureEnabled("fault.http.abort.abort_percent", - Matcher(_))) + testing::Matcher(_))) .Times(0); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", _)).Times(0); EXPECT_CALL(decoder_filter_callbacks_, encodeHeaders_(_, _)).Times(0); @@ -948,9 +965,10 @@ TEST_F(FaultFilterTest, FaultWithTargetClusterMatchSuccess) { .WillOnce(Return(std::numeric_limits::max())); // Delay related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) @@ -988,12 +1006,12 @@ TEST_F(FaultFilterTest, FaultWithTargetClusterMatchFail) { .WillOnce(ReturnRef(upstream_cluster)); EXPECT_CALL(runtime_.snapshot_, featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(_))) + testing::Matcher(_))) .Times(0); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", _)).Times(0); EXPECT_CALL(runtime_.snapshot_, featureEnabled("fault.http.abort.abort_percent", - Matcher(_))) + testing::Matcher(_))) .Times(0); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", _)).Times(0); EXPECT_CALL(decoder_filter_callbacks_, encodeHeaders_(_, _)).Times(0); @@ -1015,12 +1033,12 @@ TEST_F(FaultFilterTest, FaultWithTargetClusterNullRoute) { EXPECT_CALL(*decoder_filter_callbacks_.route_, routeEntry()).WillRepeatedly(Return(nullptr)); EXPECT_CALL(runtime_.snapshot_, featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(_))) + testing::Matcher(_))) .Times(0); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", _)).Times(0); EXPECT_CALL(runtime_.snapshot_, featureEnabled("fault.http.abort.abort_percent", - Matcher(_))) + testing::Matcher(_))) .Times(0); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.abort.http_status", _)).Times(0); EXPECT_CALL(decoder_filter_callbacks_, encodeHeaders_(_, _)).Times(0); @@ -1056,9 +1074,10 @@ void FaultFilterTest::TestPerFilterConfigFault( .WillOnce(Return(std::numeric_limits::max())); // Delay related calls - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.delay.fixed_delay_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.delay.fixed_delay_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(true)); EXPECT_CALL(runtime_.snapshot_, getInteger("fault.http.delay.fixed_duration_ms", 5000)) @@ -1118,9 +1137,10 @@ class FaultFilterRateLimitTest : public FaultFilterTest { fault.mutable_response_rate_limit()->mutable_percentage()->set_numerator(100); SetUpTest(fault); - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("fault.http.rate_limit.response_percent", - Matcher(Percent(100)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("fault.http.rate_limit.response_percent", + testing::Matcher(Percent(100)))) .WillOnce(Return(enable_runtime)); } }; diff --git a/test/extensions/filters/network/mongo_proxy/proxy_test.cc b/test/extensions/filters/network/mongo_proxy/proxy_test.cc index c43ebe8ca897..82c777a0288a 100644 --- a/test/extensions/filters/network/mongo_proxy/proxy_test.cc +++ b/test/extensions/filters/network/mongo_proxy/proxy_test.cc @@ -102,9 +102,10 @@ class MongoProxyFilterTest : public testing::Test { fault_config_ = std::make_shared(fault); - EXPECT_CALL(runtime_.snapshot_, - featureEnabled("mongo.fault.fixed_delay.percent", - Matcher(Percent(50)))) + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("mongo.fault.fixed_delay.percent", + testing::Matcher(Percent(50)))) .WillOnce(Return(enable_fault)); if (enable_fault) { diff --git a/test/extensions/filters/network/redis_proxy/router_impl_test.cc b/test/extensions/filters/network/redis_proxy/router_impl_test.cc index e558ffcecaaa..6e9f402ae028 100644 --- a/test/extensions/filters/network/redis_proxy/router_impl_test.cc +++ b/test/extensions/filters/network/redis_proxy/router_impl_test.cc @@ -264,9 +264,10 @@ TEST(MirrorPolicyImplTest, DeterminedByRuntimeFraction) { NiceMock runtime; MirrorPolicyImpl policy(config, upstream, runtime); - EXPECT_CALL(runtime.snapshot_, - featureEnabled("runtime_key", - Matcher(Percent(50)))) + EXPECT_CALL( + runtime.snapshot_, + featureEnabled("runtime_key", + testing::Matcher(Percent(50)))) .Times(4) .WillRepeatedly(Return(true)); EXPECT_EQ(true, policy.shouldMirror("get")); @@ -274,9 +275,10 @@ TEST(MirrorPolicyImplTest, DeterminedByRuntimeFraction) { EXPECT_EQ(true, policy.shouldMirror("GET")); EXPECT_EQ(true, policy.shouldMirror("SET")); - EXPECT_CALL(runtime.snapshot_, - featureEnabled("runtime_key", - Matcher(Percent(50)))) + EXPECT_CALL( + runtime.snapshot_, + featureEnabled("runtime_key", + testing::Matcher(Percent(50)))) .Times(4) .WillRepeatedly(Return(false)); EXPECT_EQ(false, policy.shouldMirror("get")); diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 0d38fdddf5c0..5505b702ea04 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -12,6 +12,7 @@ #include "envoy/http/codec.h" #include "envoy/http/conn_pool.h" #include "envoy/http/filter.h" +#include "envoy/matcher/matcher.h" #include "envoy/ssl/connection.h" #include "common/http/conn_manager_config.h" @@ -469,8 +470,17 @@ class MockFilterChainFactoryCallbacks : public Http::FilterChainFactoryCallbacks ~MockFilterChainFactoryCallbacks() override; MOCK_METHOD(void, addStreamDecoderFilter, (Http::StreamDecoderFilterSharedPtr filter)); + MOCK_METHOD(void, addStreamDecoderFilter, + (Http::StreamDecoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree)); MOCK_METHOD(void, addStreamEncoderFilter, (Http::StreamEncoderFilterSharedPtr filter)); + MOCK_METHOD(void, addStreamEncoderFilter, + (Http::StreamEncoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree)); MOCK_METHOD(void, addStreamFilter, (Http::StreamFilterSharedPtr filter)); + MOCK_METHOD(void, addStreamFilter, + (Http::StreamFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree)); MOCK_METHOD(void, addAccessLogHandler, (AccessLog::InstanceSharedPtr handler)); }; From 37e9414964de3e3fafc7da0382124e1eb225460c Mon Sep 17 00:00:00 2001 From: Craig Radcliffe Date: Mon, 14 Dec 2020 16:37:25 -0500 Subject: [PATCH 25/49] dns cache: Replace copy-on-add/remove with lock-guarded global map (#14349) * Change both dns cache and dfp cluster to use global maps guarded by mutexes. Previously, each of these maps would be copied on each add/remove, which led to performance issues as the maps grew. * Change worker DNS cache callback container to use a map from hostnames to lists of callbacks. This ensures that callbacks are only invoked for the hosts that are completely ready (e.g. the dynamic forward proxy's host info has been populated). * Hold the PrimaryHostInfo objects by shared_ptr to reduce the time required for the main thread to hold the map lock. Signed-off-by: Craig Radcliffe --- source/common/common/cleanup.h | 38 ++++ .../clusters/dynamic_forward_proxy/cluster.cc | 180 +++++++-------- .../clusters/dynamic_forward_proxy/cluster.h | 33 ++- .../common/dynamic_forward_proxy/dns_cache.h | 10 +- .../dynamic_forward_proxy/dns_cache_impl.cc | 212 ++++++++++-------- .../dynamic_forward_proxy/dns_cache_impl.h | 72 ++++-- test/common/common/cleanup_test.cc | 54 +++++ .../dynamic_forward_proxy/cluster_test.cc | 15 +- .../dns_cache_impl_test.cc | 6 +- .../common/dynamic_forward_proxy/mocks.h | 4 +- 10 files changed, 386 insertions(+), 238 deletions(-) diff --git a/source/common/common/cleanup.h b/source/common/common/cleanup.h index adf37cef6861..0c1db1da1d8b 100644 --- a/source/common/common/cleanup.h +++ b/source/common/common/cleanup.h @@ -55,4 +55,42 @@ template class RaiiListElement { bool cancelled_; }; +// RAII helper class to add an element to a std::list held inside an absl::flat_hash_map on +// construction and erase it on destruction, unless the cancel method has been called. If the list +// is empty after removal of the element, the destructor will also remove the list from the map. +template class RaiiMapOfListElement { +public: + using MapOfList = absl::flat_hash_map>; + + template + RaiiMapOfListElement(MapOfList& map, const ConvertibleToKey& key, Value value) + : map_(map), list_(map_.try_emplace(key).first->second), key_(key), cancelled_(false) { + it_ = list_.emplace(list_.begin(), value); + } + + virtual ~RaiiMapOfListElement() { + if (!cancelled_) { + erase(); + } + } + + void cancel() { cancelled_ = true; } + +private: + void erase() { + ASSERT(!cancelled_); + list_.erase(it_); + if (list_.empty()) { + map_.erase(key_); + } + cancelled_ = true; + } + + MapOfList& map_; + std::list& list_; + // Because of absl::flat_hash_map iterator instability we have to keep a copy of the key + const Key key_; + typename MapOfList::mapped_type::iterator it_; + bool cancelled_; +}; } // namespace Envoy diff --git a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc index 022516a83708..39712337ecb3 100644 --- a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc +++ b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc @@ -25,8 +25,7 @@ Cluster::Cluster( added_via_api, factory_context.dispatcher().timeSource()), dns_cache_manager_(cache_manager_factory.get()), dns_cache_(dns_cache_manager_->getCache(config.dns_cache_config())), - update_callbacks_handle_(dns_cache_->addUpdateCallbacks(*this)), local_info_(local_info), - host_map_(std::make_shared()) { + update_callbacks_handle_(dns_cache_->addUpdateCallbacks(*this)), local_info_(local_info) { // Block certain TLS context parameters that don't make sense on a cluster-wide scale. We will // support these parameters dynamically in the future. This is not an exhaustive list of // parameters that don't make sense but should be the most obvious ones that a user might set @@ -44,104 +43,99 @@ Cluster::Cluster( void Cluster::startPreInit() { // If we are attaching to a pre-populated cache we need to initialize our hosts. - auto existing_hosts = dns_cache_->hosts(); - if (!existing_hosts.empty()) { - std::shared_ptr new_host_map; - std::unique_ptr hosts_added; - for (const auto& existing_host : existing_hosts) { - addOrUpdateWorker(existing_host.first, existing_host.second, new_host_map, hosts_added); - } - swapAndUpdateMap(new_host_map, *hosts_added, {}); + std::unique_ptr hosts_added; + dns_cache_->iterateHostMap( + [&](absl::string_view host, const Common::DynamicForwardProxy::DnsHostInfoSharedPtr& info) { + addOrUpdateHost(host, info, hosts_added); + }); + if (hosts_added) { + updatePriorityState(*hosts_added, {}); } - onPreInitComplete(); } -void Cluster::addOrUpdateWorker( - const std::string& host, +void Cluster::addOrUpdateHost( + absl::string_view host, const Extensions::Common::DynamicForwardProxy::DnsHostInfoSharedPtr& host_info, - std::shared_ptr& new_host_map, std::unique_ptr& hosts_added) { - // We should never get a host with no address from the cache. - ASSERT(host_info->address() != nullptr); - - // NOTE: Right now we allow a DNS cache to be shared between multiple clusters. Though we have - // connection/request circuit breakers on the cluster, we don't have any way to control the - // maximum hosts on a cluster. We currently assume that host data shared via shared pointer is a - // marginal memory cost above that already used by connections and requests, so relying on - // connection/request circuit breakers is sufficient. We may have to revisit this in the future. - - HostInfoMapSharedPtr current_map = getCurrentHostMap(); - const auto host_map_it = current_map->find(host); - if (host_map_it != current_map->end()) { - // If we only have an address change, we can do that swap inline without any other updates. - // The appropriate R/W locking is in place to allow this. The details of this locking are: - // - Hosts are not thread local, they are global. - // - We take a read lock when reading the address and a write lock when changing it. - // - Address updates are very rare. - // - Address reads are only done when a connection is being made and a "real" host - // description is created or the host is queried via the admin endpoint. Both of - // these operations are relatively rare and the read lock is held for a short period - // of time. - // - // TODO(mattklein123): Right now the dynamic forward proxy / DNS cache works similar to how - // logical DNS works, meaning that we only store a single address per - // resolution. It would not be difficult to also expose strict DNS - // semantics, meaning the cache would expose multiple addresses and the - // cluster would create multiple logical hosts based on those addresses. - // We will leave this is a follow up depending on need. - ASSERT(host_info == host_map_it->second.shared_host_info_); - ASSERT(host_map_it->second.shared_host_info_->address() != - host_map_it->second.logical_host_->address()); - ENVOY_LOG(debug, "updating dfproxy cluster host address '{}'", host); - host_map_it->second.logical_host_->setNewAddress(host_info->address(), dummy_lb_endpoint_); - return; - } + Upstream::LogicalHostSharedPtr emplaced_host; + { + absl::WriterMutexLock lock{&host_map_lock_}; + // We should never get a host with no address from the cache. + ASSERT(host_info->address() != nullptr); + + // NOTE: Right now we allow a DNS cache to be shared between multiple clusters. Though we have + // connection/request circuit breakers on the cluster, we don't have any way to control the + // maximum hosts on a cluster. We currently assume that host data shared via shared pointer is a + // marginal memory cost above that already used by connections and requests, so relying on + // connection/request circuit breakers is sufficient. We may have to revisit this in the future. + const auto host_map_it = host_map_.find(host); + if (host_map_it != host_map_.end()) { + // If we only have an address change, we can do that swap inline without any other updates. + // The appropriate R/W locking is in place to allow this. The details of this locking are: + // - Hosts are not thread local, they are global. + // - We take a read lock when reading the address and a write lock when changing it. + // - Address updates are very rare. + // - Address reads are only done when a connection is being made and a "real" host + // description is created or the host is queried via the admin endpoint. Both of + // these operations are relatively rare and the read lock is held for a short period + // of time. + // + // TODO(mattklein123): Right now the dynamic forward proxy / DNS cache works similar to how + // logical DNS works, meaning that we only store a single address per + // resolution. It would not be difficult to also expose strict DNS + // semantics, meaning the cache would expose multiple addresses and the + // cluster would create multiple logical hosts based on those addresses. + // We will leave this is a follow up depending on need. + ASSERT(host_info == host_map_it->second.shared_host_info_); + ASSERT(host_map_it->second.shared_host_info_->address() != + host_map_it->second.logical_host_->address()); + ENVOY_LOG(debug, "updating dfproxy cluster host address '{}'", host); + host_map_it->second.logical_host_->setNewAddress(host_info->address(), dummy_lb_endpoint_); + return; + } - ENVOY_LOG(debug, "adding new dfproxy cluster host '{}'", host); + ENVOY_LOG(debug, "adding new dfproxy cluster host '{}'", host); - if (new_host_map == nullptr) { - new_host_map = std::make_shared(*current_map); + emplaced_host = host_map_ + .try_emplace(host, host_info, + std::make_shared( + info(), std::string{host}, host_info->address(), + dummy_locality_lb_endpoint_, dummy_lb_endpoint_, nullptr, + time_source_)) + .first->second.logical_host_; } - const auto emplaced = - new_host_map->try_emplace(host, host_info, - std::make_shared( - info(), host, host_info->address(), dummy_locality_lb_endpoint_, - dummy_lb_endpoint_, nullptr, time_source_)); + + ASSERT(emplaced_host); if (hosts_added == nullptr) { hosts_added = std::make_unique(); } - hosts_added->emplace_back(emplaced.first->second.logical_host_); + hosts_added->emplace_back(emplaced_host); } void Cluster::onDnsHostAddOrUpdate( const std::string& host, const Extensions::Common::DynamicForwardProxy::DnsHostInfoSharedPtr& host_info) { - std::shared_ptr new_host_map; + ENVOY_LOG(debug, "Adding host info for {}", host); + std::unique_ptr hosts_added; - addOrUpdateWorker(host, host_info, new_host_map, hosts_added); + addOrUpdateHost(host, host_info, hosts_added); if (hosts_added != nullptr) { - ASSERT(!new_host_map->empty()); ASSERT(!hosts_added->empty()); - // Swap in the new map. This will be picked up when the per-worker LBs are recreated via - // the host set update. - swapAndUpdateMap(new_host_map, *hosts_added, {}); + updatePriorityState(*hosts_added, {}); } } -void Cluster::swapAndUpdateMap(const HostInfoMapSharedPtr& new_hosts_map, - const Upstream::HostVector& hosts_added, - const Upstream::HostVector& hosts_removed) { - { - absl::WriterMutexLock lock(&host_map_lock_); - host_map_ = new_hosts_map; - } - +void Cluster::updatePriorityState(const Upstream::HostVector& hosts_added, + const Upstream::HostVector& hosts_removed) { Upstream::PriorityStateManager priority_state_manager(*this, local_info_, nullptr); priority_state_manager.initializePriorityFor(dummy_locality_lb_endpoint_); - for (const auto& host : (*new_hosts_map)) { - priority_state_manager.registerHostForPriority(host.second.logical_host_, - dummy_locality_lb_endpoint_); + { + absl::ReaderMutexLock lock{&host_map_lock_}; + for (const auto& host : host_map_) { + priority_state_manager.registerHostForPriority(host.second.logical_host_, + dummy_locality_lb_endpoint_); + } } priority_state_manager.updateClusterPrioritySet( 0, std::move(priority_state_manager.priorityState()[0].first), hosts_added, hosts_removed, @@ -149,18 +143,16 @@ void Cluster::swapAndUpdateMap(const HostInfoMapSharedPtr& new_hosts_map, } void Cluster::onDnsHostRemove(const std::string& host) { - HostInfoMapSharedPtr current_map = getCurrentHostMap(); - const auto host_map_it = current_map->find(host); - ASSERT(host_map_it != current_map->end()); - const auto new_host_map = std::make_shared(*current_map); Upstream::HostVector hosts_removed; - hosts_removed.emplace_back(host_map_it->second.logical_host_); - new_host_map->erase(host); - ENVOY_LOG(debug, "removing dfproxy cluster host '{}'", host); - - // Swap in the new map. This will be picked up when the per-worker LBs are recreated via - // the host set update. - swapAndUpdateMap(new_host_map, {}, hosts_removed); + { + absl::WriterMutexLock lock{&host_map_lock_}; + const auto host_map_it = host_map_.find(host); + ASSERT(host_map_it != host_map_.end()); + hosts_removed.emplace_back(host_map_it->second.logical_host_); + host_map_.erase(host); + ENVOY_LOG(debug, "removing dfproxy cluster host '{}'", host); + } + updatePriorityState({}, hosts_removed); } Upstream::HostConstSharedPtr @@ -179,13 +171,15 @@ Cluster::LoadBalancer::chooseHost(Upstream::LoadBalancerContext* context) { if (host.empty()) { return nullptr; } - - const auto host_it = host_map_->find(host); - if (host_it == host_map_->end()) { - return nullptr; - } else { - host_it->second.shared_host_info_->touch(); - return host_it->second.logical_host_; + { + absl::ReaderMutexLock lock{&cluster_.host_map_lock_}; + const auto host_it = cluster_.host_map_.find(host); + if (host_it == cluster_.host_map_.end()) { + return nullptr; + } else { + host_it->second.shared_host_info_->touch(); + return host_it->second.logical_host_; + } } } diff --git a/source/extensions/clusters/dynamic_forward_proxy/cluster.h b/source/extensions/clusters/dynamic_forward_proxy/cluster.h index a34b8d6b1871..32f82808570d 100644 --- a/source/extensions/clusters/dynamic_forward_proxy/cluster.h +++ b/source/extensions/clusters/dynamic_forward_proxy/cluster.h @@ -52,10 +52,9 @@ class Cluster : public Upstream::BaseDynamicClusterImpl, }; using HostInfoMap = absl::flat_hash_map; - using HostInfoMapSharedPtr = std::shared_ptr; struct LoadBalancer : public Upstream::LoadBalancer { - LoadBalancer(const HostInfoMapSharedPtr& host_map) : host_map_(host_map) {} + LoadBalancer(const Cluster& cluster) : cluster_(cluster) {} // Upstream::LoadBalancer Upstream::HostConstSharedPtr chooseHost(Upstream::LoadBalancerContext* context) override; @@ -64,16 +63,14 @@ class Cluster : public Upstream::BaseDynamicClusterImpl, return nullptr; } - const HostInfoMapSharedPtr host_map_; + const Cluster& cluster_; }; struct LoadBalancerFactory : public Upstream::LoadBalancerFactory { LoadBalancerFactory(Cluster& cluster) : cluster_(cluster) {} // Upstream::LoadBalancerFactory - Upstream::LoadBalancerPtr create() override { - return std::make_unique(cluster_.getCurrentHostMap()); - } + Upstream::LoadBalancerPtr create() override { return std::make_unique(cluster_); } Cluster& cluster_; }; @@ -90,19 +87,15 @@ class Cluster : public Upstream::BaseDynamicClusterImpl, Cluster& cluster_; }; - HostInfoMapSharedPtr getCurrentHostMap() { - absl::ReaderMutexLock lock(&host_map_lock_); - return host_map_; - } - void - addOrUpdateWorker(const std::string& host, - const Extensions::Common::DynamicForwardProxy::DnsHostInfoSharedPtr& host_info, - std::shared_ptr& new_host_map, - std::unique_ptr& hosts_added); - void swapAndUpdateMap(const HostInfoMapSharedPtr& new_hosts_map, - const Upstream::HostVector& hosts_added, - const Upstream::HostVector& hosts_removed); + addOrUpdateHost(absl::string_view host, + const Extensions::Common::DynamicForwardProxy::DnsHostInfoSharedPtr& host_info, + std::unique_ptr& hosts_added) + ABSL_LOCKS_EXCLUDED(host_map_lock_); + + void updatePriorityState(const Upstream::HostVector& hosts_added, + const Upstream::HostVector& hosts_removed) + ABSL_LOCKS_EXCLUDED(host_map_lock_); const Extensions::Common::DynamicForwardProxy::DnsCacheManagerSharedPtr dns_cache_manager_; const Extensions::Common::DynamicForwardProxy::DnsCacheSharedPtr dns_cache_; @@ -112,8 +105,8 @@ class Cluster : public Upstream::BaseDynamicClusterImpl, const envoy::config::endpoint::v3::LbEndpoint dummy_lb_endpoint_; const LocalInfo::LocalInfo& local_info_; - absl::Mutex host_map_lock_; - HostInfoMapSharedPtr host_map_ ABSL_GUARDED_BY(host_map_lock_); + mutable absl::Mutex host_map_lock_; + HostInfoMap host_map_ ABSL_GUARDED_BY(host_map_lock_); friend class ClusterFactory; friend class ClusterTest; diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache.h b/source/extensions/common/dynamic_forward_proxy/dns_cache.h index 74e7fa134a2a..7425639531db 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache.h +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache.h @@ -23,7 +23,7 @@ class DnsHostInfo { * Returns the host's currently resolved address. This address may change periodically due to * async re-resolution. */ - virtual Network::Address::InstanceConstSharedPtr address() PURE; + virtual Network::Address::InstanceConstSharedPtr address() const PURE; /** * Returns the host that was actually resolved via DNS. If port was originally specified it will @@ -172,10 +172,14 @@ class DnsCache { */ virtual AddUpdateCallbacksHandlePtr addUpdateCallbacks(UpdateCallbacks& callbacks) PURE; + using IterateHostMapCb = std::function; + /** - * @return all hosts currently stored in the cache. + * Iterates over all entries in the cache, calling a callback for each entry + * + * @param iterate_callback the callback to invoke for each entry in the cache */ - virtual absl::flat_hash_map hosts() PURE; + virtual void iterateHostMap(IterateHostMapCb iterate_callback) PURE; /** * Retrieve the DNS host info of a given host currently stored in the cache. diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc index ac09ab220f72..094255c84924 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc @@ -31,8 +31,7 @@ DnsCacheImpl::DnsCacheImpl( config, refresh_interval_.count(), random)), host_ttl_(PROTOBUF_GET_MS_OR_DEFAULT(config, host_ttl, 300000)), max_hosts_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_hosts, 1024)) { - tls_slot_.set([](Event::Dispatcher&) { return std::make_shared(); }); - updateTlsHostsMap(); + tls_slot_.set([&](Event::Dispatcher&) { return std::make_shared(*this); }); } DnsCacheImpl::~DnsCacheImpl() { @@ -56,19 +55,26 @@ DnsCacheImpl::loadDnsCacheEntry(absl::string_view host, uint16_t default_port, LoadDnsCacheEntryCallbacks& callbacks) { ENVOY_LOG(debug, "thread local lookup for host '{}'", host); ThreadLocalHostInfo& tls_host_info = *tls_slot_; - auto tls_host = tls_host_info.host_map_->find(host); - if (tls_host != tls_host_info.host_map_->end()) { - ENVOY_LOG(debug, "thread local hit for host '{}'", host); + + auto [cache_hit, is_overflow] = [&]() { + // TODO(chradcliffe): Consider returning the looked-up host + absl::ReaderMutexLock read_lock{&primary_hosts_lock_}; + auto tls_host = primary_hosts_.find(host); + bool cache_hit = + tls_host != primary_hosts_.end() && tls_host->second->host_info_->firstResolveComplete(); + bool is_overflow = primary_hosts_.size() >= max_hosts_; + return std::make_tuple(cache_hit, is_overflow); + }(); + + if (cache_hit) { + ENVOY_LOG(debug, "cache hit for host '{}'", host); return {LoadDnsCacheEntryStatus::InCache, nullptr}; - } else if (tls_host_info.host_map_->size() >= max_hosts_) { - // Given that we do this check in thread local context, it's possible for two threads to race - // and potentially go slightly above the configured max hosts. This is an OK given compromise - // given how much simpler the implementation is. + } else if (is_overflow) { ENVOY_LOG(debug, "DNS cache overflow for host '{}'", host); stats_.host_overflow_.inc(); return {LoadDnsCacheEntryStatus::Overflow, nullptr}; } else { - ENVOY_LOG(debug, "thread local miss for host '{}', posting to main thread", host); + ENVOY_LOG(debug, "cache miss for host '{}', posting to main thread", host); main_thread_dispatcher_.post( [this, host = std::string(host), default_port]() { startCacheLoad(host, default_port); }); return {LoadDnsCacheEntryStatus::Loading, @@ -91,34 +97,30 @@ DnsCacheImpl::canCreateDnsRequest(ResourceLimitOptRef pending_requests) { return std::make_unique(current_pending_requests); } -absl::flat_hash_map DnsCacheImpl::hosts() { - absl::flat_hash_map ret; +void DnsCacheImpl::iterateHostMap(IterateHostMapCb iterate_callback) { + absl::ReaderMutexLock reader_lock{&primary_hosts_lock_}; for (const auto& host : primary_hosts_) { // Only include hosts that have ever resolved to an address. - if (host.second->host_info_->address_ != nullptr) { - ret.emplace(host.first, host.second->host_info_); + if (host.second->host_info_->address() != nullptr) { + iterate_callback(host.first, host.second->host_info_); } } - return ret; } absl::optional DnsCacheImpl::getHost(absl::string_view host_name) { // Find a host with the given name. - auto it = primary_hosts_.find(host_name); - if (it == primary_hosts_.end()) { - return {}; - } - - // Extract host info. - auto&& host_info = it->second->host_info_; + const auto host_info = [&]() -> const DnsHostInfoSharedPtr { + absl::ReaderMutexLock reader_lock{&primary_hosts_lock_}; + auto it = primary_hosts_.find(host_name); + return it != primary_hosts_.end() ? it->second->host_info_ : nullptr; + }(); // Only include hosts that have ever resolved to an address. - if (host_info->address_ == nullptr) { + if (!host_info || host_info->address() == nullptr) { return {}; + } else { + return host_info; } - - // Return host info. - return host_info; } DnsCacheImpl::AddUpdateCallbacksHandlePtr @@ -127,11 +129,21 @@ DnsCacheImpl::addUpdateCallbacks(UpdateCallbacks& callbacks) { } void DnsCacheImpl::startCacheLoad(const std::string& host, uint16_t default_port) { + ASSERT(main_thread_dispatcher_.isThreadSafe()); + // It's possible for multiple requests to race trying to start a resolution. If a host is // already in the map it's either in the process of being resolved or the resolution is already // heading out to the worker threads. Either way the pending resolution will be completed. - const auto primary_host_it = primary_hosts_.find(host); - if (primary_host_it != primary_hosts_.end()) { + + // Functions like this one that modify primary_hosts_ are only called in the main thread so we + // know it is safe to use the PrimaryHostInfo pointers outside of the lock. + auto* primary_host = [&]() { + absl::ReaderMutexLock reader_lock{&primary_hosts_lock_}; + auto host_it = primary_hosts_.find(host); + return host_it != primary_hosts_.end() ? host_it->second.get() : nullptr; + }(); + + if (primary_host) { ENVOY_LOG(debug, "main thread resolve for host '{}' skipped. Entry present", host); return; } @@ -141,49 +153,72 @@ void DnsCacheImpl::startCacheLoad(const std::string& host, uint16_t default_port // TODO(mattklein123): Right now, the same host with different ports will become two // independent primary hosts with independent DNS resolutions. I'm not sure how much this will // matter, but we could consider collapsing these down and sharing the underlying DNS resolution. - auto& primary_host = *primary_hosts_ - // try_emplace() is used here for direct argument forwarding. - .try_emplace(host, std::make_unique( - *this, std::string(host_attributes.host_), - host_attributes.port_.value_or(default_port), - host_attributes.is_ip_address_, - [this, host]() { onReResolve(host); })) - .first->second; - startResolve(host, primary_host); + { + absl::WriterMutexLock writer_lock{&primary_hosts_lock_}; + primary_host = primary_hosts_ + // try_emplace() is used here for direct argument forwarding. + .try_emplace(host, std::make_unique( + *this, std::string(host_attributes.host_), + host_attributes.port_.value_or(default_port), + host_attributes.is_ip_address_, + [this, host]() { onReResolve(host); })) + .first->second.get(); + } + + startResolve(host, *primary_host); } void DnsCacheImpl::onReResolve(const std::string& host) { - const auto primary_host_it = primary_hosts_.find(host); - ASSERT(primary_host_it != primary_hosts_.end()); + ASSERT(main_thread_dispatcher_.isThreadSafe()); + // If we need to erase the host, hold onto the PrimaryHostInfo object that owns this callback. + // This is defined at function scope so that it is only erased on function exit to avoid + // use-after-free issues + PrimaryHostInfoPtr host_to_erase; + + // Functions like this one that modify primary_hosts_ are only called in the main thread so we + // know it is safe to use the PrimaryHostInfo pointers outside of the lock. + auto* primary_host = [&]() { + absl::ReaderMutexLock reader_lock{&primary_hosts_lock_}; + const auto primary_host_it = primary_hosts_.find(host); + ASSERT(primary_host_it != primary_hosts_.end()); + return primary_host_it->second.get(); + }(); const std::chrono::steady_clock::duration now_duration = main_thread_dispatcher_.timeSource().monotonicTime().time_since_epoch(); - ENVOY_LOG(debug, "host='{}' TTL check: now={} last_used={}", primary_host_it->first, - now_duration.count(), - primary_host_it->second->host_info_->last_used_time_.load().count()); - if (now_duration - primary_host_it->second->host_info_->last_used_time_.load() > host_ttl_) { + auto last_used_time = primary_host->host_info_->lastUsedTime(); + ENVOY_LOG(debug, "host='{}' TTL check: now={} last_used={}", host, now_duration.count(), + last_used_time.count()); + if ((now_duration - last_used_time) > host_ttl_) { ENVOY_LOG(debug, "host='{}' TTL expired, removing", host); // If the host has no address then that means that the DnsCacheImpl has never // runAddUpdateCallbacks for this host, and thus the callback targets are not aware of it. // Therefore, runRemoveCallbacks should only be ran if the host's address != nullptr. - if (primary_host_it->second->host_info_->address_) { + if (primary_host->host_info_->address()) { runRemoveCallbacks(host); } - primary_hosts_.erase(primary_host_it); - updateTlsHostsMap(); + { + absl::WriterMutexLock writer_lock{&primary_hosts_lock_}; + auto host_it = primary_hosts_.find(host); + ASSERT(host_it != primary_hosts_.end()); + host_to_erase = std::move(host_it->second); + primary_hosts_.erase(host_it); + } + notifyThreads(host); } else { - startResolve(host, *primary_host_it->second); + startResolve(host, *primary_host); } } void DnsCacheImpl::startResolve(const std::string& host, PrimaryHostInfo& host_info) { ENVOY_LOG(debug, "starting main thread resolve for host='{}' dns='{}' port='{}'", host, - host_info.host_info_->resolved_host_, host_info.port_); + host_info.host_info_->resolvedHost(), host_info.port_); ASSERT(host_info.active_query_ == nullptr); stats_.dns_query_attempt_.inc(); + host_info.active_query_ = - resolver_->resolve(host_info.host_info_->resolved_host_, dns_lookup_family_, + resolver_->resolve(host_info.host_info_->resolvedHost(), dns_lookup_family_, [this, host](Network::DnsResolver::ResolutionStatus status, std::list&& response) { finishResolve(host, status, std::move(response)); @@ -193,21 +228,27 @@ void DnsCacheImpl::startResolve(const std::string& host, PrimaryHostInfo& host_i void DnsCacheImpl::finishResolve(const std::string& host, Network::DnsResolver::ResolutionStatus status, std::list&& response) { + ASSERT(main_thread_dispatcher_.isThreadSafe()); ENVOY_LOG(debug, "main thread resolve complete for host '{}'. {} results", host, response.size()); - const auto primary_host_it = primary_hosts_.find(host); - ASSERT(primary_host_it != primary_hosts_.end()); - auto& primary_host_info = *primary_host_it->second; - primary_host_info.active_query_ = nullptr; - const bool first_resolve = !primary_host_info.host_info_->first_resolve_complete_; - primary_host_info.host_info_->first_resolve_complete_ = true; + // Functions like this one that modify primary_hosts_ are only called in the main thread so we + // know it is safe to use the PrimaryHostInfo pointers outside of the lock. + auto* primary_host_info = [&]() { + absl::ReaderMutexLock reader_lock{&primary_hosts_lock_}; + const auto primary_host_it = primary_hosts_.find(host); + ASSERT(primary_host_it != primary_hosts_.end()); + return primary_host_it->second.get(); + }(); + + const bool first_resolve = !primary_host_info->host_info_->firstResolveComplete(); + primary_host_info->active_query_ = nullptr; // If the DNS resolver successfully resolved with an empty response list, the dns cache does not // update. This ensures that a potentially previously resolved address does not stabilize back to // 0 hosts. const auto new_address = !response.empty() ? Network::Utility::getAddressWithPort(*(response.front().address_), - primary_host_info.port_) + primary_host_info->port_) : nullptr; if (status == Network::DnsResolver::ResolutionStatus::Failure) { @@ -224,17 +265,18 @@ void DnsCacheImpl::finishResolve(const std::string& host, // This means that once a host gets an address it will stick even in the case of a subsequent // resolution failure. bool address_changed = false; - if (new_address != nullptr && (primary_host_info.host_info_->address_ == nullptr || - *primary_host_info.host_info_->address_ != *new_address)) { + auto current_address = primary_host_info->host_info_->address(); + if (new_address != nullptr && (current_address == nullptr || *current_address != *new_address)) { ENVOY_LOG(debug, "host '{}' address has changed", host); - primary_host_info.host_info_->address_ = new_address; - runAddUpdateCallbacks(host, primary_host_info.host_info_); + primary_host_info->host_info_->setAddress(new_address); + runAddUpdateCallbacks(host, primary_host_info->host_info_); address_changed = true; stats_.host_address_changed_.inc(); } if (first_resolve || address_changed) { - updateTlsHostsMap(); + primary_host_info->host_info_->setFirstResolveComplete(); + notifyThreads(host); } // Kick off the refresh timer. @@ -242,12 +284,12 @@ void DnsCacheImpl::finishResolve(const std::string& host, // is populated dynamically. if (status == Network::DnsResolver::ResolutionStatus::Success) { failure_backoff_strategy_->reset(); - primary_host_info.refresh_timer_->enableTimer(refresh_interval_); + primary_host_info->refresh_timer_->enableTimer(refresh_interval_); ENVOY_LOG(debug, "DNS refresh rate reset for host '{}', refresh rate {} ms", host, refresh_interval_.count()); } else { const uint64_t refresh_interval = failure_backoff_strategy_->nextBackOffMs(); - primary_host_info.refresh_timer_->enableTimer(std::chrono::milliseconds(refresh_interval)); + primary_host_info->refresh_timer_->enableTimer(std::chrono::milliseconds(refresh_interval)); ENVOY_LOG(debug, "DNS refresh rate reset for host '{}', (failure) refresh rate {} ms", host, refresh_interval); } @@ -255,51 +297,43 @@ void DnsCacheImpl::finishResolve(const std::string& host, void DnsCacheImpl::runAddUpdateCallbacks(const std::string& host, const DnsHostInfoSharedPtr& host_info) { - for (auto callbacks : update_callbacks_) { + for (auto* callbacks : update_callbacks_) { callbacks->callbacks_.onDnsHostAddOrUpdate(host, host_info); } } void DnsCacheImpl::runRemoveCallbacks(const std::string& host) { - for (auto callbacks : update_callbacks_) { + for (auto* callbacks : update_callbacks_) { callbacks->callbacks_.onDnsHostRemove(host); } } -void DnsCacheImpl::updateTlsHostsMap() { - TlsHostMapSharedPtr new_host_map = std::make_shared(); - for (const auto& primary_host : primary_hosts_) { - // Do not include hosts that have not resolved at least once. - if (primary_host.second->host_info_->first_resolve_complete_) { - new_host_map->emplace(primary_host.first, primary_host.second->host_info_); - } - } - - tls_slot_.runOnAllThreads([new_host_map](OptRef local_host_info) { - local_host_info->updateHostMap(new_host_map); +void DnsCacheImpl::notifyThreads(const std::string& host) { + auto host_ptr = std::make_shared(host); + tls_slot_.runOnAllThreads([host_ptr](OptRef local_host_info) { + local_host_info->onHostMapUpdate(host_ptr); }); } DnsCacheImpl::ThreadLocalHostInfo::~ThreadLocalHostInfo() { // Make sure we cancel any handles that still exist. - for (auto pending_resolution : pending_resolutions_) { - pending_resolution->cancel(); + for (const auto& per_host_list : pending_resolutions_) { + for (auto pending_resolution : per_host_list.second) { + pending_resolution->cancel(); + } } } -void DnsCacheImpl::ThreadLocalHostInfo::updateHostMap(const TlsHostMapSharedPtr& new_host_map) { - host_map_ = new_host_map; - for (auto pending_resolution_it = pending_resolutions_.begin(); - pending_resolution_it != pending_resolutions_.end();) { - auto& pending_resolution = **pending_resolution_it; - if (host_map_->count(pending_resolution.host_) != 0) { - auto& callbacks = pending_resolution.callbacks_; - pending_resolution.cancel(); - pending_resolution_it = pending_resolutions_.erase(pending_resolution_it); +void DnsCacheImpl::ThreadLocalHostInfo::onHostMapUpdate( + std::shared_ptr resolved_host) { + auto host_it = pending_resolutions_.find(*resolved_host); + if (host_it != pending_resolutions_.end()) { + for (auto* resolution : host_it->second) { + auto& callbacks = resolution->callbacks_; + resolution->cancel(); callbacks.onLoadDnsCacheComplete(); - } else { - ++pending_resolution_it; } + pending_resolutions_.erase(host_it); } } diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h index bf5106374702..d918ddcb7a7a 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h @@ -51,55 +51,77 @@ class DnsCacheImpl : public DnsCache, Logger::Loggable hosts() override; + void iterateHostMap(IterateHostMapCb cb) override; absl::optional getHost(absl::string_view host_name) override; Upstream::ResourceAutoIncDecPtr canCreateDnsRequest(ResourceLimitOptRef pending_requests) override; private: - using TlsHostMap = absl::flat_hash_map; - using TlsHostMapSharedPtr = std::shared_ptr; - - struct LoadDnsCacheEntryHandleImpl : public LoadDnsCacheEntryHandle, - RaiiListElement { - LoadDnsCacheEntryHandleImpl(std::list& parent, - absl::string_view host, LoadDnsCacheEntryCallbacks& callbacks) - : RaiiListElement(parent, this), host_(host), + struct LoadDnsCacheEntryHandleImpl + : public LoadDnsCacheEntryHandle, + RaiiMapOfListElement { + LoadDnsCacheEntryHandleImpl( + absl::flat_hash_map>& parent, + absl::string_view host, LoadDnsCacheEntryCallbacks& callbacks) + : RaiiMapOfListElement(parent, host, this), callbacks_(callbacks) {} - const std::string host_; LoadDnsCacheEntryCallbacks& callbacks_; }; - // Per-thread DNS cache info including the currently known hosts as well as any pending callbacks. + // Per-thread DNS cache info including pending callbacks. struct ThreadLocalHostInfo : public ThreadLocal::ThreadLocalObject { + ThreadLocalHostInfo(DnsCacheImpl& parent) : parent_{parent} {} ~ThreadLocalHostInfo() override; - void updateHostMap(const TlsHostMapSharedPtr& new_host_map); - - TlsHostMapSharedPtr host_map_; - std::list pending_resolutions_; + void onHostMapUpdate(std::shared_ptr resolved_host); + absl::flat_hash_map> pending_resolutions_; + DnsCacheImpl& parent_; }; - struct DnsHostInfoImpl : public DnsHostInfo { + class DnsHostInfoImpl : public DnsHostInfo { + public: DnsHostInfoImpl(TimeSource& time_source, absl::string_view resolved_host, bool is_ip_address) : time_source_(time_source), resolved_host_(resolved_host), is_ip_address_(is_ip_address) { touch(); } // DnsHostInfo - Network::Address::InstanceConstSharedPtr address() override { return address_; } + Network::Address::InstanceConstSharedPtr address() const override { + absl::ReaderMutexLock lock{&resolve_lock_}; + return address_; + } const std::string& resolvedHost() const override { return resolved_host_; } bool isIpAddress() const override { return is_ip_address_; } void touch() final { last_used_time_ = time_source_.monotonicTime().time_since_epoch(); } + void setAddress(Network::Address::InstanceConstSharedPtr address) { + absl::WriterMutexLock lock{&resolve_lock_}; + first_resolve_complete_ = true; + address_ = address; + } + std::chrono::steady_clock::duration lastUsedTime() const { return last_used_time_.load(); } + + bool firstResolveComplete() const { + absl::ReaderMutexLock lock{&resolve_lock_}; + return first_resolve_complete_; + } + + void setFirstResolveComplete() { + absl::WriterMutexLock lock{&resolve_lock_}; + first_resolve_complete_ = true; + } + + private: TimeSource& time_source_; const std::string resolved_host_; const bool is_ip_address_; - bool first_resolve_complete_{}; - Network::Address::InstanceConstSharedPtr address_; + mutable absl::Mutex resolve_lock_; + Network::Address::InstanceConstSharedPtr address_ ABSL_GUARDED_BY(resolve_lock_); + // Using std::chrono::steady_clock::duration is required for compilation within an atomic vs. // using MonotonicTime. std::atomic last_used_time_; + bool first_resolve_complete_ ABSL_GUARDED_BY(resolve_lock_){false}; }; using DnsHostInfoImplSharedPtr = std::shared_ptr; @@ -117,6 +139,8 @@ class DnsCacheImpl : public DnsCache, Logger::Loggable; struct AddUpdateCallbacksHandleImpl : public AddUpdateCallbacksHandle, @@ -129,12 +153,14 @@ class DnsCacheImpl : public DnsCache, Logger::Loggable&& response); void runAddUpdateCallbacks(const std::string& host, const DnsHostInfoSharedPtr& host_info); void runRemoveCallbacks(const std::string& host); - void updateTlsHostsMap(); + void notifyThreads(const std::string& host); void onReResolve(const std::string& host); Event::Dispatcher& main_thread_dispatcher_; @@ -144,7 +170,9 @@ class DnsCacheImpl : public DnsCache, Logger::Loggable update_callbacks_; - absl::flat_hash_map primary_hosts_; + absl::Mutex primary_hosts_lock_; + absl::flat_hash_map + primary_hosts_ ABSL_GUARDED_BY(primary_hosts_lock_); DnsCacheResourceManagerImpl resource_manager_; const std::chrono::milliseconds refresh_interval_; const BackOffStrategyPtr failure_backoff_strategy_; diff --git a/test/common/common/cleanup_test.cc b/test/common/common/cleanup_test.cc index 98a590308727..25c3ea2b29f4 100644 --- a/test/common/common/cleanup_test.cc +++ b/test/common/common/cleanup_test.cc @@ -60,4 +60,58 @@ TEST(RaiiListElementTest, DeleteOnErase) { EXPECT_EQ(l.size(), 0); } +TEST(RaiiMapOfListElement, DeleteOnDestruction) { + absl::flat_hash_map> map; + { + EXPECT_EQ(map.size(), 0); + RaiiMapOfListElement element(map, 1, 1); + EXPECT_EQ(map.size(), 1); + auto it = map.find(1); + ASSERT_NE(map.end(), it); + EXPECT_EQ(it->second.size(), 1); + } + EXPECT_EQ(map.size(), 0); +} + +TEST(RaiiMapOfListElementTest, CancelDelete) { + absl::flat_hash_map> map; + + { + EXPECT_EQ(map.size(), 0); + RaiiMapOfListElement element(map, 1, 1); + EXPECT_EQ(map.size(), 1); + auto it = map.find(1); + ASSERT_NE(map.end(), it); + EXPECT_EQ(it->second.size(), 1); + element.cancel(); + } + EXPECT_EQ(map.size(), 1); + auto it = map.find(1); + ASSERT_NE(map.end(), it); + EXPECT_EQ(it->second.size(), 1); +} + +TEST(RaiiMapOfListElement, MultipleEntriesSameKey) { + absl::flat_hash_map> map; + { + EXPECT_EQ(map.size(), 0); + RaiiMapOfListElement element(map, 1, 1); + EXPECT_EQ(map.size(), 1); + auto it = map.find(1); + ASSERT_NE(map.end(), it); + EXPECT_EQ(it->second.size(), 1); + { + RaiiMapOfListElement second_element(map, 1, 2); + EXPECT_EQ(map.size(), 1); + it = map.find(1); + ASSERT_NE(map.end(), it); + EXPECT_EQ(it->second.size(), 2); + } + it = map.find(1); + ASSERT_NE(map.end(), it); + EXPECT_EQ(it->second.size(), 1); + } + EXPECT_EQ(map.size(), 0); +} + } // namespace Envoy diff --git a/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc b/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc index 3bf37f32648c..8009305fdfb4 100644 --- a/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc +++ b/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc @@ -70,7 +70,11 @@ class ClusterTest : public testing::Test, for (const auto& host : host_map_) { existing_hosts.emplace(host.first, host.second); } - EXPECT_CALL(*dns_cache_manager_->dns_cache_, hosts()).WillOnce(Return(existing_hosts)); + EXPECT_CALL(*dns_cache_manager_->dns_cache_, iterateHostMap(_)).WillOnce(Invoke([&](auto cb) { + for (const auto& host : host_map_) { + cb(host.first, host.second); + } + })); if (!existing_hosts.empty()) { EXPECT_CALL(*this, onMemberUpdateCb(SizeIs(existing_hosts.size()), SizeIs(0))); } @@ -154,14 +158,12 @@ TEST_F(ClusterTest, BasicFlow) { // Verify no host LB cases. EXPECT_EQ(nullptr, lb_->chooseHost(setHostAndReturnContext("foo"))); - // LB will not resolve host1 until it has been updated. + // LB will immediately resolve host1. EXPECT_CALL(*this, onMemberUpdateCb(SizeIs(1), SizeIs(0))); update_callbacks_->onDnsHostAddOrUpdate("host1", host_map_["host1"]); - EXPECT_EQ(nullptr, lb_->chooseHost(setHostAndReturnContext("host1"))); EXPECT_EQ(1UL, cluster_->prioritySet().hostSetsPerPriority()[0]->hosts().size()); EXPECT_EQ("1.2.3.4:0", cluster_->prioritySet().hostSetsPerPriority()[0]->hosts()[0]->address()->asString()); - refreshLb(); EXPECT_CALL(*host_map_["host1"], touch()); EXPECT_EQ("1.2.3.4:0", lb_->chooseHost(setHostAndReturnContext("host1"))->address()->asString()); @@ -174,13 +176,10 @@ TEST_F(ClusterTest, BasicFlow) { EXPECT_CALL(*host_map_["host1"], touch()); EXPECT_EQ("2.3.4.5:0", lb_->chooseHost(setHostAndReturnContext("host1"))->address()->asString()); - // Remove the host, LB will still resolve until it is refreshed. + // Remove the host, LB will immediately fail to find the host in the map. EXPECT_CALL(*this, onMemberUpdateCb(SizeIs(0), SizeIs(1))); update_callbacks_->onDnsHostRemove("host1"); EXPECT_EQ(0UL, cluster_->prioritySet().hostSetsPerPriority()[0]->hosts().size()); - EXPECT_CALL(*host_map_["host1"], touch()); - EXPECT_EQ("2.3.4.5:0", lb_->chooseHost(setHostAndReturnContext("host1"))->address()->asString()); - refreshLb(); EXPECT_EQ(nullptr, lb_->chooseHost(setHostAndReturnContext("host1"))); } diff --git a/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc b/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc index 87b04b8836e4..9afbe86d8895 100644 --- a/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc +++ b/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc @@ -30,6 +30,8 @@ class DnsCacheImplTest : public testing::Test, public Event::TestUsingSimulatedT config_.set_name("foo"); config_.set_dns_lookup_family(envoy::config::cluster::v3::Cluster::V4_ONLY); + EXPECT_CALL(dispatcher_, isThreadSafe).WillRepeatedly(Return(true)); + EXPECT_CALL(dispatcher_, createDnsResolver(_, _)).WillOnce(Return(resolver_)); dns_cache_ = std::make_unique(dispatcher_, tls_, random_, loader_, store_, config_); @@ -569,7 +571,9 @@ TEST_F(DnsCacheImplTest, MultipleResolveDifferentHost) { resolve_cb1(Network::DnsResolver::ResolutionStatus::Success, TestUtility::makeDnsResponse({"10.0.0.2"})); - auto hosts = dns_cache_->hosts(); + absl::flat_hash_map hosts; + dns_cache_->iterateHostMap( + [&](absl::string_view host, const DnsHostInfoSharedPtr& info) { hosts.emplace(host, info); }); EXPECT_EQ(2, hosts.size()); EXPECT_THAT(hosts["bar.com"], DnsHostInfoEquals("10.0.0.1:443", "bar.com", false)); EXPECT_THAT(hosts["foo.com"], DnsHostInfoEquals("10.0.0.2:80", "foo.com", false)); diff --git a/test/extensions/common/dynamic_forward_proxy/mocks.h b/test/extensions/common/dynamic_forward_proxy/mocks.h index 8129cad90e7f..aee7140a0aed 100644 --- a/test/extensions/common/dynamic_forward_proxy/mocks.h +++ b/test/extensions/common/dynamic_forward_proxy/mocks.h @@ -56,7 +56,7 @@ class MockDnsCache : public DnsCache { MOCK_METHOD(DnsCache::AddUpdateCallbacksHandle*, addUpdateCallbacks_, (UpdateCallbacks & callbacks)); - MOCK_METHOD((absl::flat_hash_map), hosts, ()); + MOCK_METHOD((void), iterateHostMap, (IterateHostMapCb)); MOCK_METHOD((absl::optional), getHost, (absl::string_view)); MOCK_METHOD(Upstream::ResourceAutoIncDec*, canCreateDnsRequest_, (ResourceLimitOptRef)); }; @@ -85,7 +85,7 @@ class MockDnsHostInfo : public DnsHostInfo { MockDnsHostInfo(); ~MockDnsHostInfo() override; - MOCK_METHOD(Network::Address::InstanceConstSharedPtr, address, ()); + MOCK_METHOD(Network::Address::InstanceConstSharedPtr, address, (), (const)); MOCK_METHOD(const std::string&, resolvedHost, (), (const)); MOCK_METHOD(bool, isIpAddress, (), (const)); MOCK_METHOD(void, touch, ()); From 0dd74a9a6f1cce26763443f9247b131bb8eb7254 Mon Sep 17 00:00:00 2001 From: htuch Date: Mon, 14 Dec 2020 19:09:58 -0500 Subject: [PATCH 26/49] config: v2 non-pubsub transport API fatal-by-default. (#14389) This is a followup to #14223, covering remaining uses of the transport_api_version field. Risk level: High (this will break anyone who is still using v2 and has not enabled CLI or runtime override) Testing: Various tests updated, some exemplar tests added to server_test. Release Notes: Same as #13950. Signed-off-by: Harvey Tuch --- source/common/config/BUILD | 1 + .../config/subscription_factory_impl.cc | 40 +++++-------------- .../common/config/subscription_factory_impl.h | 4 +- source/common/config/utility.h | 29 ++++++++++++++ .../common/upstream/cluster_manager_impl.cc | 12 +++--- source/extensions/access_loggers/common/BUILD | 1 + .../common/grpc_access_logger.h | 3 +- .../extensions/filters/http/ext_authz/BUILD | 1 + .../filters/http/ext_authz/config.cc | 5 ++- .../extensions/filters/http/ratelimit/BUILD | 1 + .../filters/http/ratelimit/config.cc | 8 ++-- .../filters/network/ext_authz/BUILD | 1 + .../filters/network/ext_authz/config.cc | 3 +- .../filters/network/ratelimit/BUILD | 1 + .../filters/network/ratelimit/config.cc | 10 ++--- .../thrift_proxy/filters/ratelimit/BUILD | 1 + .../thrift_proxy/filters/ratelimit/config.cc | 4 +- .../stat_sinks/metrics_service/BUILD | 1 + .../stat_sinks/metrics_service/config.cc | 3 +- source/server/server.cc | 8 ++-- test/common/config/BUILD | 2 +- .../config/subscription_factory_impl_test.cc | 15 ++----- test/config/utility.cc | 16 ++++++-- test/config/utility.h | 4 +- .../grpc/grpc_access_log_impl_test.cc | 1 + .../access_loggers/grpc/http_config_test.cc | 1 + .../http_grpc_access_log_integration_test.cc | 3 ++ .../tcp_grpc_access_log_integration_test.cc | 3 ++ .../filters/http/ext_authz/config_test.cc | 4 ++ .../ext_authz/ext_authz_integration_test.cc | 3 ++ .../ratelimit/ratelimit_integration_test.cc | 4 +- .../filters/network/common/fuzz/BUILD | 1 + .../fuzz/network_readfilter_fuzz_test.cc | 4 +- .../filters/network/ext_authz/BUILD | 1 + .../filters/network/ext_authz/config_test.cc | 6 ++- .../metrics_service_integration_test.cc | 3 ++ test/integration/ads_integration.cc | 13 +++--- test/integration/ads_integration.h | 4 +- test/integration/ads_integration_test.cc | 40 ++++++++++++------- test/integration/hds_integration_test.cc | 3 ++ .../load_stats_integration_test.cc | 3 ++ test/server/server_test.cc | 30 ++++++++++++++ test/server/test_data/server/ads_v2.yaml | 14 +++++++ test/server/test_data/server/hds_v2.yaml | 13 ++++++ tools/code_format/check_format.py | 9 ++++- 45 files changed, 238 insertions(+), 99 deletions(-) create mode 100644 test/server/test_data/server/ads_v2.yaml create mode 100644 test/server/test_data/server/hds_v2.yaml diff --git a/source/common/config/BUILD b/source/common/config/BUILD index a92b3e017920..067f1d9ada0f 100644 --- a/source/common/config/BUILD +++ b/source/common/config/BUILD @@ -392,6 +392,7 @@ envoy_cc_library( "//source/common/grpc:common_lib", "//source/common/protobuf", "//source/common/protobuf:utility_lib", + "//source/common/runtime:runtime_features_lib", "//source/common/singleton:const_singleton", "//source/common/stats:histogram_lib", "//source/common/stats:stats_lib", diff --git a/source/common/config/subscription_factory_impl.cc b/source/common/config/subscription_factory_impl.cc index bb07b4d72d96..ba40d3ec99dc 100644 --- a/source/common/config/subscription_factory_impl.cc +++ b/source/common/config/subscription_factory_impl.cc @@ -19,9 +19,9 @@ namespace Config { SubscriptionFactoryImpl::SubscriptionFactoryImpl( const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher, Upstream::ClusterManager& cm, ProtobufMessage::ValidationVisitor& validation_visitor, - Api::Api& api, Runtime::Loader& runtime) + Api::Api& api) : local_info_(local_info), dispatcher_(dispatcher), cm_(cm), - validation_visitor_(validation_visitor), api_(api), runtime_(runtime) {} + validation_visitor_(validation_visitor), api_(api) {} SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( const envoy::config::core::v3::ConfigSource& config, absl::string_view type_url, @@ -41,24 +41,7 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( const envoy::config::core::v3::ApiConfigSource& api_config_source = config.api_config_source(); Utility::checkApiConfigSourceSubscriptionBackingCluster(cm_.primaryClusters(), api_config_source); - const auto transport_api_version = api_config_source.transport_api_version(); - if (transport_api_version == envoy::config::core::v3::ApiVersion::AUTO || - transport_api_version == envoy::config::core::v3::ApiVersion::V2) { - runtime_.countDeprecatedFeatureUse(); - const std::string& warning = fmt::format( - "V2 (and AUTO) xDS transport protocol versions are deprecated in {}. " - "The v2 xDS major version is deprecated and disabled by default. Support for v2 will be " - "removed from Envoy at the start of Q1 2021. You may make use of v2 in Q4 2020 by " - "following the advice in https://www.envoyproxy.io/docs/envoy/latest/faq/api/transition.", - config.DebugString()); - ENVOY_LOG(warn, warning); - auto& runtime_snapshot = runtime_.snapshot(); - if (!runtime_snapshot.runtimeFeatureEnabled( - "envoy.reloadable_features.enable_deprecated_v2_api")) { - throw DeprecatedMajorVersionException(warning); - } - } - + const auto transport_api_version = Utility::getAndCheckTransportVersion(api_config_source); switch (api_config_source.api_type()) { case envoy::config::core::v3::ApiConfigSource::hidden_envoy_deprecated_UNSUPPORTED_REST_LEGACY: throw EnvoyException( @@ -70,9 +53,9 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( local_info_, cm_, api_config_source.cluster_names()[0], dispatcher_, api_.randomGenerator(), Utility::apiConfigSourceRefreshDelay(api_config_source), Utility::apiConfigSourceRequestTimeout(api_config_source), - restMethod(type_url, api_config_source.transport_api_version()), type_url, - api_config_source.transport_api_version(), callbacks, resource_decoder, stats, - Utility::configSourceInitialFetchTimeout(config), validation_visitor_); + restMethod(type_url, transport_api_version), type_url, transport_api_version, callbacks, + resource_decoder, stats, Utility::configSourceInitialFetchTimeout(config), + validation_visitor_); case envoy::config::core::v3::ApiConfigSource::GRPC: return std::make_unique( std::make_shared( @@ -80,9 +63,8 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), api_config_source, scope, true) ->create(), - dispatcher_, sotwGrpcMethod(type_url, api_config_source.transport_api_version()), - api_config_source.transport_api_version(), api_.randomGenerator(), scope, - Utility::parseRateLimitSettings(api_config_source), + dispatcher_, sotwGrpcMethod(type_url, transport_api_version), transport_api_version, + api_.randomGenerator(), scope, Utility::parseRateLimitSettings(api_config_source), api_config_source.set_node_on_first_message_only()), callbacks, resource_decoder, stats, type_url, dispatcher_, Utility::configSourceInitialFetchTimeout(config), @@ -93,9 +75,9 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( Config::Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), api_config_source, scope, true) ->create(), - dispatcher_, deltaGrpcMethod(type_url, api_config_source.transport_api_version()), - api_config_source.transport_api_version(), api_.randomGenerator(), scope, - Utility::parseRateLimitSettings(api_config_source), local_info_), + dispatcher_, deltaGrpcMethod(type_url, transport_api_version), transport_api_version, + api_.randomGenerator(), scope, Utility::parseRateLimitSettings(api_config_source), + local_info_), callbacks, resource_decoder, stats, type_url, dispatcher_, Utility::configSourceInitialFetchTimeout(config), false); } diff --git a/source/common/config/subscription_factory_impl.h b/source/common/config/subscription_factory_impl.h index cc076be46d76..5c0555533a50 100644 --- a/source/common/config/subscription_factory_impl.h +++ b/source/common/config/subscription_factory_impl.h @@ -17,8 +17,7 @@ class SubscriptionFactoryImpl : public SubscriptionFactory, Logger::Loggable + static envoy::config::core::v3::ApiVersion + getAndCheckTransportVersion(const Proto& api_config_source) { + const auto transport_api_version = api_config_source.transport_api_version(); + if (transport_api_version == envoy::config::core::v3::ApiVersion::AUTO || + transport_api_version == envoy::config::core::v3::ApiVersion::V2) { + Runtime::LoaderSingleton::getExisting()->countDeprecatedFeatureUse(); + const std::string& warning = fmt::format( + "V2 (and AUTO) xDS transport protocol versions are deprecated in {}. " + "The v2 xDS major version is deprecated and disabled by default. Support for v2 will be " + "removed from Envoy at the start of Q1 2021. You may make use of v2 in Q4 2020 by " + "following the advice in https://www.envoyproxy.io/docs/envoy/latest/faq/api/transition.", + api_config_source.DebugString()); + ENVOY_LOG_MISC(warn, warning); + if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_deprecated_v2_api")) { + throw DeprecatedMajorVersionException(warning); + } + } + return transport_api_version; + } + /** * Parses RateLimit configuration from envoy::config::core::v3::ApiConfigSource to * RateLimitSettings. diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index af026fe66b23..b1950599c0dd 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -265,7 +265,7 @@ ClusterManagerImpl::ClusterManagerImpl( cluster_request_response_size_stat_names_(stats.symbolTable()), cluster_timeout_budget_stat_names_(stats.symbolTable()), subscription_factory_(local_info, main_thread_dispatcher, *this, - validation_context.dynamicValidationVisitor(), api, runtime_) { + validation_context.dynamicValidationVisitor(), api) { async_client_manager_ = std::make_unique( *this, tls, time_source_, api, grpc_context.statNames()); const auto& cm_config = bootstrap.cluster_manager(); @@ -325,14 +325,14 @@ ClusterManagerImpl::ClusterManagerImpl( ->create(), main_thread_dispatcher, *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( - dyn_resources.ads_config().transport_api_version() == + Config::Utility::getAndCheckTransportVersion(dyn_resources.ads_config()) == envoy::config::core::v3::ApiVersion::V3 // TODO(htuch): consolidate with type_to_endpoint.cc, once we sort out the future // direction of that module re: https://github.com/envoyproxy/envoy/issues/10650. ? "envoy.service.discovery.v3.AggregatedDiscoveryService.DeltaAggregatedResources" : "envoy.service.discovery.v2.AggregatedDiscoveryService." "DeltaAggregatedResources"), - dyn_resources.ads_config().transport_api_version(), random_, stats_, + Config::Utility::getAndCheckTransportVersion(dyn_resources.ads_config()), random_, stats_, Envoy::Config::Utility::parseRateLimitSettings(dyn_resources.ads_config()), local_info); } else { ads_mux_ = std::make_shared( @@ -342,7 +342,7 @@ ClusterManagerImpl::ClusterManagerImpl( ->create(), main_thread_dispatcher, *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( - dyn_resources.ads_config().transport_api_version() == + Config::Utility::getAndCheckTransportVersion(dyn_resources.ads_config()) == envoy::config::core::v3::ApiVersion::V3 // TODO(htuch): consolidate with type_to_endpoint.cc, once we sort out the future // direction of that module re: https://github.com/envoyproxy/envoy/issues/10650. @@ -350,7 +350,7 @@ ClusterManagerImpl::ClusterManagerImpl( "StreamAggregatedResources" : "envoy.service.discovery.v2.AggregatedDiscoveryService." "StreamAggregatedResources"), - dyn_resources.ads_config().transport_api_version(), random_, stats_, + Config::Utility::getAndCheckTransportVersion(dyn_resources.ads_config()), random_, stats_, Envoy::Config::Utility::parseRateLimitSettings(dyn_resources.ads_config()), bootstrap.dynamic_resources().ads_config().set_node_on_first_message_only()); } @@ -425,7 +425,7 @@ void ClusterManagerImpl::initializeSecondaryClusters( Config::Utility::factoryForGrpcApiConfigSource(*async_client_manager_, load_stats_config, stats_, false) ->create(), - load_stats_config.transport_api_version(), dispatcher_); + Config::Utility::getAndCheckTransportVersion(load_stats_config), dispatcher_); } } diff --git a/source/extensions/access_loggers/common/BUILD b/source/extensions/access_loggers/common/BUILD index 9a55e9e2cad7..c77fe9dca536 100644 --- a/source/extensions/access_loggers/common/BUILD +++ b/source/extensions/access_loggers/common/BUILD @@ -33,6 +33,7 @@ envoy_cc_library( "//include/envoy/stats:stats_interface", "//include/envoy/thread_local:thread_local_interface", "//source/common/common:assert_lib", + "//source/common/config:utility_lib", "//source/common/grpc:typed_async_client_lib", "//source/common/protobuf:utility_lib", "@com_google_absl//absl/types:optional", diff --git a/source/extensions/access_loggers/common/grpc_access_logger.h b/source/extensions/access_loggers/common/grpc_access_logger.h index 90e7b0d8a814..911326ee547a 100644 --- a/source/extensions/access_loggers/common/grpc_access_logger.h +++ b/source/extensions/access_loggers/common/grpc_access_logger.h @@ -11,6 +11,7 @@ #include "envoy/thread_local/thread_local.h" #include "common/common/assert.h" +#include "common/config/utility.h" #include "common/grpc/typed_async_client.h" #include "common/protobuf/utility.h" @@ -201,7 +202,7 @@ class GrpcAccessLoggerCache : public Singleton::Instance, factory->create(), config.log_name(), std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(config, buffer_flush_interval, 1000)), PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, buffer_size_bytes, 16384), cache.dispatcher_, - local_info_, scope, config.transport_api_version()); + local_info_, scope, Config::Utility::getAndCheckTransportVersion(config)); cache.access_loggers_.emplace(cache_key, logger); return logger; } diff --git a/source/extensions/filters/http/ext_authz/BUILD b/source/extensions/filters/http/ext_authz/BUILD index c7583679783b..9a902c51777d 100644 --- a/source/extensions/filters/http/ext_authz/BUILD +++ b/source/extensions/filters/http/ext_authz/BUILD @@ -45,6 +45,7 @@ envoy_cc_extension( ":ext_authz", "//include/envoy/registry", "//include/envoy/stats:stats_macros", + "//source/common/config:utility_lib", "//source/common/grpc:google_async_client_cache", "//source/common/protobuf:utility_lib", "//source/extensions/filters/common/ext_authz:ext_authz_http_lib", diff --git a/source/extensions/filters/http/ext_authz/config.cc b/source/extensions/filters/http/ext_authz/config.cc index 22162671e73d..5446cb7a55c2 100644 --- a/source/extensions/filters/http/ext_authz/config.cc +++ b/source/extensions/filters/http/ext_authz/config.cc @@ -8,6 +8,7 @@ #include "envoy/extensions/filters/http/ext_authz/v3/ext_authz.pb.validate.h" #include "envoy/registry/registry.h" +#include "common/config/utility.h" #include "common/grpc/google_async_client_cache.h" #include "common/protobuf/utility.h" @@ -59,7 +60,7 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( context.clusterManager().grpcAsyncClientManager(), context.scope(), context.threadLocal(), proto_config.grpc_service()); callback = [async_client_cache, filter_config, timeout_ms, proto_config, - transport_api_version = proto_config.transport_api_version()]( + transport_api_version = Config::Utility::getAndCheckTransportVersion(proto_config)]( Http::FilterChainFactoryCallbacks& callbacks) { auto client = std::make_unique( async_client_cache->getAsyncClient(), std::chrono::milliseconds(timeout_ms), @@ -79,7 +80,7 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( const uint32_t timeout_ms = PROTOBUF_GET_MS_OR_DEFAULT(proto_config.grpc_service(), timeout, DefaultTimeout); callback = [grpc_service = proto_config.grpc_service(), &context, filter_config, timeout_ms, - transport_api_version = proto_config.transport_api_version()]( + transport_api_version = Config::Utility::getAndCheckTransportVersion(proto_config)]( Http::FilterChainFactoryCallbacks& callbacks) { const auto async_client_factory = context.clusterManager().grpcAsyncClientManager().factoryForGrpcService( diff --git a/source/extensions/filters/http/ratelimit/BUILD b/source/extensions/filters/http/ratelimit/BUILD index 0b9584711194..a4090ee21d79 100644 --- a/source/extensions/filters/http/ratelimit/BUILD +++ b/source/extensions/filters/http/ratelimit/BUILD @@ -49,6 +49,7 @@ envoy_cc_extension( deps = [ ":ratelimit_lib", "//include/envoy/registry", + "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", "//source/extensions/filters/common/ratelimit:ratelimit_client_interface", "//source/extensions/filters/common/ratelimit:ratelimit_lib", diff --git a/source/extensions/filters/http/ratelimit/config.cc b/source/extensions/filters/http/ratelimit/config.cc index 9ff4038e9930..912604d37aac 100644 --- a/source/extensions/filters/http/ratelimit/config.cc +++ b/source/extensions/filters/http/ratelimit/config.cc @@ -7,6 +7,7 @@ #include "envoy/extensions/filters/http/ratelimit/v3/rate_limit.pb.validate.h" #include "envoy/registry/registry.h" +#include "common/config/utility.h" #include "common/protobuf/utility.h" #include "extensions/filters/common/ratelimit/ratelimit_impl.h" @@ -30,9 +31,10 @@ Http::FilterFactoryCb RateLimitFilterConfig::createFilterFactoryFromProtoTyped( return [proto_config, &context, timeout, filter_config](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter(std::make_shared( - filter_config, Filters::Common::RateLimit::rateLimitClient( - context, proto_config.rate_limit_service().grpc_service(), timeout, - proto_config.rate_limit_service().transport_api_version()))); + filter_config, + Filters::Common::RateLimit::rateLimitClient( + context, proto_config.rate_limit_service().grpc_service(), timeout, + Config::Utility::getAndCheckTransportVersion(proto_config.rate_limit_service())))); }; } diff --git a/source/extensions/filters/network/ext_authz/BUILD b/source/extensions/filters/network/ext_authz/BUILD index 3ef4a4738a3d..4d43cbd30eeb 100644 --- a/source/extensions/filters/network/ext_authz/BUILD +++ b/source/extensions/filters/network/ext_authz/BUILD @@ -40,6 +40,7 @@ envoy_cc_extension( security_posture = "robust_to_untrusted_downstream", deps = [ "//include/envoy/registry", + "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", "//source/extensions/filters/network:well_known_names", "//source/extensions/filters/network/common:factory_base_lib", diff --git a/source/extensions/filters/network/ext_authz/config.cc b/source/extensions/filters/network/ext_authz/config.cc index a8a4188b395e..65e852f6a181 100644 --- a/source/extensions/filters/network/ext_authz/config.cc +++ b/source/extensions/filters/network/ext_authz/config.cc @@ -9,6 +9,7 @@ #include "envoy/network/connection.h" #include "envoy/registry/registry.h" +#include "common/config/utility.h" #include "common/protobuf/utility.h" #include "extensions/filters/common/ext_authz/ext_authz.h" @@ -27,7 +28,7 @@ Network::FilterFactoryCb ExtAuthzConfigFactory::createFilterFactoryFromProtoType const uint32_t timeout_ms = PROTOBUF_GET_MS_OR_DEFAULT(proto_config.grpc_service(), timeout, 200); return [grpc_service = proto_config.grpc_service(), &context, ext_authz_config, - transport_api_version = proto_config.transport_api_version(), + transport_api_version = Envoy::Config::Utility::getAndCheckTransportVersion(proto_config), timeout_ms](Network::FilterManager& filter_manager) -> void { auto async_client_factory = context.clusterManager().grpcAsyncClientManager().factoryForGrpcService( diff --git a/source/extensions/filters/network/ratelimit/BUILD b/source/extensions/filters/network/ratelimit/BUILD index f653adf348fb..035bfad5b6f7 100644 --- a/source/extensions/filters/network/ratelimit/BUILD +++ b/source/extensions/filters/network/ratelimit/BUILD @@ -41,6 +41,7 @@ envoy_cc_extension( security_posture = "robust_to_untrusted_downstream", deps = [ "//include/envoy/registry", + "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", "//source/extensions/filters/common/ratelimit:ratelimit_client_interface", "//source/extensions/filters/common/ratelimit:ratelimit_lib", diff --git a/source/extensions/filters/network/ratelimit/config.cc b/source/extensions/filters/network/ratelimit/config.cc index 82037f5b424f..b60bd47e54d9 100644 --- a/source/extensions/filters/network/ratelimit/config.cc +++ b/source/extensions/filters/network/ratelimit/config.cc @@ -7,6 +7,7 @@ #include "envoy/extensions/filters/network/ratelimit/v3/rate_limit.pb.validate.h" #include "envoy/registry/registry.h" +#include "common/config/utility.h" #include "common/protobuf/utility.h" #include "extensions/filters/common/ratelimit/ratelimit_impl.h" @@ -32,11 +33,10 @@ Network::FilterFactoryCb RateLimitConfigFactory::createFilterFactoryFromProtoTyp return [proto_config, &context, timeout, filter_config](Network::FilterManager& filter_manager) -> void { filter_manager.addReadFilter(std::make_shared( - filter_config, - - Filters::Common::RateLimit::rateLimitClient( - context, proto_config.rate_limit_service().grpc_service(), timeout, - proto_config.rate_limit_service().transport_api_version()))); + filter_config, Filters::Common::RateLimit::rateLimitClient( + context, proto_config.rate_limit_service().grpc_service(), timeout, + Envoy::Config::Utility::getAndCheckTransportVersion( + proto_config.rate_limit_service())))); }; } diff --git a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/BUILD b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/BUILD index 7252afc340a7..02425a8b069e 100644 --- a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/BUILD +++ b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/BUILD @@ -36,6 +36,7 @@ envoy_cc_extension( deps = [ ":ratelimit_lib", "//include/envoy/registry", + "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", "//source/extensions/filters/common/ratelimit:ratelimit_client_interface", "//source/extensions/filters/common/ratelimit:ratelimit_lib", diff --git a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/config.cc b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/config.cc index 9813ec583c97..9383c2782c19 100644 --- a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/config.cc +++ b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/config.cc @@ -7,6 +7,7 @@ #include "envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3/rate_limit.pb.validate.h" #include "envoy/registry/registry.h" +#include "common/config/utility.h" #include "common/protobuf/utility.h" #include "extensions/filters/common/ratelimit/ratelimit_impl.h" @@ -35,7 +36,8 @@ RateLimitFilterConfig::createFilterFactoryFromProtoTyped( callbacks.addDecoderFilter(std::make_shared( config, Filters::Common::RateLimit::rateLimitClient( context, proto_config.rate_limit_service().grpc_service(), timeout, - proto_config.rate_limit_service().transport_api_version()))); + Envoy::Config::Utility::getAndCheckTransportVersion( + proto_config.rate_limit_service())))); }; } diff --git a/source/extensions/stat_sinks/metrics_service/BUILD b/source/extensions/stat_sinks/metrics_service/BUILD index df78d152ba53..cf7a8ce39cba 100644 --- a/source/extensions/stat_sinks/metrics_service/BUILD +++ b/source/extensions/stat_sinks/metrics_service/BUILD @@ -47,6 +47,7 @@ envoy_cc_extension( deps = [ "//include/envoy/registry", "//source/common/common:assert_lib", + "//source/common/config:utility_lib", "//source/extensions/stat_sinks:well_known_names", "//source/extensions/stat_sinks/metrics_service:metrics_proto_descriptors_lib", "//source/extensions/stat_sinks/metrics_service:metrics_service_grpc_lib", diff --git a/source/extensions/stat_sinks/metrics_service/config.cc b/source/extensions/stat_sinks/metrics_service/config.cc index 8b44af894b8b..a1e9be3782e6 100644 --- a/source/extensions/stat_sinks/metrics_service/config.cc +++ b/source/extensions/stat_sinks/metrics_service/config.cc @@ -5,6 +5,7 @@ #include "envoy/registry/registry.h" #include "common/common/assert.h" +#include "common/config/utility.h" #include "common/grpc/async_client_impl.h" #include "common/network/resolver_impl.h" @@ -26,7 +27,7 @@ MetricsServiceSinkFactory::createStatsSink(const Protobuf::Message& config, MessageUtil::downcastAndValidate( config, server.messageValidationContext().staticValidationVisitor()); const auto& grpc_service = sink_config.grpc_service(); - const auto& transport_api_version = sink_config.transport_api_version(); + const auto& transport_api_version = Config::Utility::getAndCheckTransportVersion(sink_config); ENVOY_LOG(debug, "Metrics Service gRPC service configuration: {}", grpc_service.DebugString()); std::shared_ptrcreate(), - hds_config.transport_api_version(), *dispatcher_, Runtime::LoaderSingleton::get(), - stats_store_, *ssl_context_manager_, info_factory_, access_log_manager_, - *config_.clusterManager(), *local_info_, *admin_, *singleton_manager_, thread_local_, - messageValidationContext().dynamicValidationVisitor(), *api_); + Config::Utility::getAndCheckTransportVersion(hds_config), *dispatcher_, + Runtime::LoaderSingleton::get(), stats_store_, *ssl_context_manager_, info_factory_, + access_log_manager_, *config_.clusterManager(), *local_info_, *admin_, *singleton_manager_, + thread_local_, messageValidationContext().dynamicValidationVisitor(), *api_); } // If there is no global limit to the number of active connections, warn on startup. diff --git a/test/common/config/BUILD b/test/common/config/BUILD index 72e18e2372f4..80a2bfa5b6d5 100644 --- a/test/common/config/BUILD +++ b/test/common/config/BUILD @@ -296,11 +296,11 @@ envoy_cc_test( "//test/mocks/filesystem:filesystem_mocks", "//test/mocks/local_info:local_info_mocks", "//test/mocks/protobuf:protobuf_mocks", - "//test/mocks/runtime:runtime_mocks", "//test/mocks/stats:stats_mocks", "//test/mocks/upstream:cluster_manager_mocks", "//test/test_common:environment_lib", "//test/test_common:logging_lib", + "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/test/common/config/subscription_factory_impl_test.cc b/test/common/config/subscription_factory_impl_test.cc index 0a92ab644493..efa98bfafb12 100644 --- a/test/common/config/subscription_factory_impl_test.cc +++ b/test/common/config/subscription_factory_impl_test.cc @@ -16,11 +16,11 @@ #include "test/mocks/filesystem/mocks.h" #include "test/mocks/local_info/mocks.h" #include "test/mocks/protobuf/mocks.h" -#include "test/mocks/runtime/mocks.h" #include "test/mocks/stats/mocks.h" #include "test/mocks/upstream/cluster_manager.h" #include "test/test_common/environment.h" #include "test/test_common/logging.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -40,8 +40,7 @@ class SubscriptionFactoryTest : public testing::Test { SubscriptionFactoryTest() : http_request_(&cm_.thread_local_cluster_.async_client_), api_(Api::createApiForTest(stats_store_, random_)), - subscription_factory_(local_info_, dispatcher_, cm_, validation_visitor_, *api_, runtime_) { - } + subscription_factory_(local_info_, dispatcher_, cm_, validation_visitor_, *api_) {} SubscriptionPtr subscriptionFromConfigSource(const envoy::config::core::v3::ConfigSource& config) { @@ -338,11 +337,8 @@ TEST_F(SubscriptionFactoryTest, LogWarningOnDeprecatedV2Transport) { envoy::config::core::v3::ApiVersion::V2); config.mutable_api_config_source()->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name( "static_cluster"); - NiceMock snapshot; - EXPECT_CALL(runtime_, snapshot()).WillRepeatedly(ReturnRef(snapshot)); - EXPECT_CALL(snapshot, runtimeFeatureEnabled(_)).WillOnce(Return(false)); - EXPECT_CALL(runtime_, countDeprecatedFeatureUse()); + TestScopedRuntime scoped_runtime; Upstream::ClusterManager::ClusterSet primary_clusters; primary_clusters.insert("static_cluster"); EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); @@ -363,11 +359,8 @@ TEST_F(SubscriptionFactoryTest, LogWarningOnDeprecatedAutoTransport) { envoy::config::core::v3::ApiVersion::AUTO); config.mutable_api_config_source()->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name( "static_cluster"); - NiceMock snapshot; - EXPECT_CALL(runtime_, snapshot()).WillRepeatedly(ReturnRef(snapshot)); - EXPECT_CALL(snapshot, runtimeFeatureEnabled(_)).WillOnce(Return(false)); - EXPECT_CALL(runtime_, countDeprecatedFeatureUse()); + TestScopedRuntime scoped_runtime; Upstream::ClusterManager::ClusterSet primary_clusters; primary_clusters.insert("static_cluster"); EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); diff --git a/test/config/utility.cc b/test/config/utility.cc index 115201d53095..680cd9c536ea 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -342,7 +342,13 @@ std::string ConfigHelper::discoveredClustersBootstrap(const std::string& api_typ // TODO(#6327) cleaner approach to testing with static config. std::string ConfigHelper::adsBootstrap(const std::string& api_type, - envoy::config::core::v3::ApiVersion api_version) { + envoy::config::core::v3::ApiVersion resource_api_version, + envoy::config::core::v3::ApiVersion transport_api_version) { + // We use this to allow tests to default to having a single API version but override and make + // the transport/resource API version distinction when needed. + if (transport_api_version == envoy::config::core::v3::ApiVersion::AUTO) { + transport_api_version = resource_api_version; + } return fmt::format(R"EOF( dynamic_resources: lds_config: @@ -352,7 +358,7 @@ std::string ConfigHelper::adsBootstrap(const std::string& api_type, resource_api_version: {1} ads: {{}} ads_config: - transport_api_version: {1} + transport_api_version: {2} api_type: {0} static_resources: clusters: @@ -376,13 +382,15 @@ std::string ConfigHelper::adsBootstrap(const std::string& api_type, explicit_http_config: http2_protocol_options: {{}} admin: - access_log_path: {2} + access_log_path: {3} address: socket_address: address: 127.0.0.1 port_value: 0 )EOF", - api_type, api_version == envoy::config::core::v3::ApiVersion::V2 ? "V2" : "V3", + api_type, + resource_api_version == envoy::config::core::v3::ApiVersion::V2 ? "V2" : "V3", + transport_api_version == envoy::config::core::v3::ApiVersion::V2 ? "V2" : "V3", Platform::null_device_path); } diff --git a/test/config/utility.h b/test/config/utility.h index ade1ce1b2d87..0649e1f6d8c6 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -122,7 +122,9 @@ class ConfigHelper { // api_type should be REST, GRPC, or DELTA_GRPC. static std::string discoveredClustersBootstrap(const std::string& api_type); static std::string adsBootstrap(const std::string& api_type, - envoy::config::core::v3::ApiVersion api_version); + envoy::config::core::v3::ApiVersion resource_api_version, + envoy::config::core::v3::ApiVersion transport_api_version = + envoy::config::core::v3::ApiVersion::AUTO); // Builds a standard Cluster config fragment, with a single endpoint (at address:port). static envoy::config::cluster::v3::Cluster buildStaticCluster(const std::string& name, int port, const std::string& address); diff --git a/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc b/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc index ed4a980036e1..e1637db9bf21 100644 --- a/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc +++ b/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc @@ -382,6 +382,7 @@ TEST_F(GrpcAccessLoggerCacheImplTest, Deduplication) { envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig config; config.set_log_name("log-1"); config.mutable_grpc_service()->mutable_envoy_grpc()->set_cluster_name("cluster-1"); + config.set_transport_api_version(envoy::config::core::v3::ApiVersion::V3); expectClientCreation(); GrpcAccessLoggerSharedPtr logger1 = diff --git a/test/extensions/access_loggers/grpc/http_config_test.cc b/test/extensions/access_loggers/grpc/http_config_test.cc index 37ba5220244f..1b91b69159bf 100644 --- a/test/extensions/access_loggers/grpc/http_config_test.cc +++ b/test/extensions/access_loggers/grpc/http_config_test.cc @@ -40,6 +40,7 @@ class HttpGrpcAccessLogConfigTest : public testing::Test { auto* common_config = http_grpc_access_log_.mutable_common_config(); common_config->set_log_name("foo"); common_config->mutable_grpc_service()->mutable_envoy_grpc()->set_cluster_name("bar"); + common_config->set_transport_api_version(envoy::config::core::v3::ApiVersion::V3); TestUtility::jsonConvert(http_grpc_access_log_, *message_); } diff --git a/test/extensions/access_loggers/grpc/http_grpc_access_log_integration_test.cc b/test/extensions/access_loggers/grpc/http_grpc_access_log_integration_test.cc index 9b702dc35564..590a3d280e51 100644 --- a/test/extensions/access_loggers/grpc/http_grpc_access_log_integration_test.cc +++ b/test/extensions/access_loggers/grpc/http_grpc_access_log_integration_test.cc @@ -30,6 +30,9 @@ class AccessLogIntegrationTest : public Grpc::VersionedGrpcClientIntegrationPara } void initialize() override { + if (apiVersion() != envoy::config::core::v3::ApiVersion::V3) { + config_helper_.enableDeprecatedV2Api(); + } config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto* accesslog_cluster = bootstrap.mutable_static_resources()->add_clusters(); accesslog_cluster->MergeFrom(bootstrap.static_resources().clusters()[0]); diff --git a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc index 22a3d800a89f..e8516e0f6d3a 100644 --- a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc +++ b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc @@ -38,6 +38,9 @@ class TcpGrpcAccessLogIntegrationTest : public Grpc::VersionedGrpcClientIntegrat } void initialize() override { + if (apiVersion() != envoy::config::core::v3::ApiVersion::V3) { + config_helper_.enableDeprecatedV2Api(); + } config_helper_.renameListener("tcp_proxy"); config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto* accesslog_cluster = bootstrap.mutable_static_resources()->add_clusters(); diff --git a/test/extensions/filters/http/ext_authz/config_test.cc b/test/extensions/filters/http/ext_authz/config_test.cc index 86c267939004..0d48e3b481ba 100644 --- a/test/extensions/filters/http/ext_authz/config_test.cc +++ b/test/extensions/filters/http/ext_authz/config_test.cc @@ -22,6 +22,10 @@ namespace ExtAuthz { namespace { void expectCorrectProtoGrpc(envoy::config::core::v3::ApiVersion api_version) { + std::unique_ptr _deprecated_v2_api; + if (api_version != envoy::config::core::v3::ApiVersion::V3) { + _deprecated_v2_api = std::make_unique(); + } std::string yaml = R"EOF( transport_api_version: V3 grpc_service: diff --git a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc index 953c431d7320..c3ec137d8360 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc @@ -35,6 +35,9 @@ class ExtAuthzGrpcIntegrationTest : public Grpc::VersionedGrpcClientIntegrationP } void initializeConfig(bool disable_with_metadata = false) { + if (apiVersion() != envoy::config::core::v3::ApiVersion::V3) { + config_helper_.enableDeprecatedV2Api(); + } config_helper_.addConfigModifier([this, disable_with_metadata]( envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto* ext_authz_cluster = bootstrap.mutable_static_resources()->add_clusters(); diff --git a/test/extensions/filters/http/ratelimit/ratelimit_integration_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_integration_test.cc index 80928a3da073..b02137388102 100644 --- a/test/extensions/filters/http/ratelimit/ratelimit_integration_test.cc +++ b/test/extensions/filters/http/ratelimit/ratelimit_integration_test.cc @@ -38,7 +38,9 @@ class RatelimitIntegrationTest : public Grpc::VersionedGrpcClientIntegrationPara } void initialize() override { - + if (apiVersion() != envoy::config::core::v3::ApiVersion::V3) { + config_helper_.enableDeprecatedV2Api(); + } config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto* ratelimit_cluster = bootstrap.mutable_static_resources()->add_clusters(); ratelimit_cluster->MergeFrom(bootstrap.static_resources().clusters()[0]); diff --git a/test/extensions/filters/network/common/fuzz/BUILD b/test/extensions/filters/network/common/fuzz/BUILD index 9d90563c9c30..41b2869c3d84 100644 --- a/test/extensions/filters/network/common/fuzz/BUILD +++ b/test/extensions/filters/network/common/fuzz/BUILD @@ -67,6 +67,7 @@ envoy_cc_fuzz_test( ":uber_readfilter_lib", "//source/common/config:utility_lib", "//test/config:utility_lib", + "//test/test_common:test_runtime_lib", ] + envoy_all_network_filters(), ) diff --git a/test/extensions/filters/network/common/fuzz/network_readfilter_fuzz_test.cc b/test/extensions/filters/network/common/fuzz/network_readfilter_fuzz_test.cc index cacff3aa8938..2582fe207c09 100644 --- a/test/extensions/filters/network/common/fuzz/network_readfilter_fuzz_test.cc +++ b/test/extensions/filters/network/common/fuzz/network_readfilter_fuzz_test.cc @@ -7,11 +7,13 @@ #include "test/extensions/filters/network/common/fuzz/network_readfilter_fuzz.pb.validate.h" #include "test/extensions/filters/network/common/fuzz/uber_readfilter.h" #include "test/fuzz/fuzz_runner.h" +#include "test/test_common/test_runtime.h" namespace Envoy { namespace Extensions { namespace NetworkFilters { DEFINE_PROTO_FUZZER(const test::extensions::filters::network::FilterFuzzTestCase& input) { + TestDeprecatedV2Api _deprecated_v2_api; ABSL_ATTRIBUTE_UNUSED static PostProcessorRegistration reg = { [](test::extensions::filters::network::FilterFuzzTestCase* input, unsigned int seed) { // This post-processor mutation is applied only when libprotobuf-mutator @@ -58,4 +60,4 @@ DEFINE_PROTO_FUZZER(const test::extensions::filters::network::FilterFuzzTestCase } // namespace NetworkFilters } // namespace Extensions -} // namespace Envoy \ No newline at end of file +} // namespace Envoy diff --git a/test/extensions/filters/network/ext_authz/BUILD b/test/extensions/filters/network/ext_authz/BUILD index d3a01660f4cd..d04bd170c39f 100644 --- a/test/extensions/filters/network/ext_authz/BUILD +++ b/test/extensions/filters/network/ext_authz/BUILD @@ -41,6 +41,7 @@ envoy_extension_cc_test( deps = [ "//source/extensions/filters/network/ext_authz:config", "//test/mocks/server:factory_context_mocks", + "//test/test_common:test_runtime_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/ext_authz/v3:pkg_cc_proto", ], diff --git a/test/extensions/filters/network/ext_authz/config_test.cc b/test/extensions/filters/network/ext_authz/config_test.cc index a96e02ee918e..9cdb483aa7ac 100644 --- a/test/extensions/filters/network/ext_authz/config_test.cc +++ b/test/extensions/filters/network/ext_authz/config_test.cc @@ -6,6 +6,7 @@ #include "extensions/filters/network/ext_authz/config.h" #include "test/mocks/server/factory_context.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -21,8 +22,11 @@ namespace ExtAuthz { namespace { void expectCorrectProto(envoy::config::core::v3::ApiVersion api_version) { + std::unique_ptr _deprecated_v2_api; + if (api_version != envoy::config::core::v3::ApiVersion::V3) { + _deprecated_v2_api = std::make_unique(); + } std::string yaml = R"EOF( - transport_api_version: V3 grpc_service: google_grpc: target_uri: ext_authz_server diff --git a/test/extensions/stats_sinks/metrics_service/metrics_service_integration_test.cc b/test/extensions/stats_sinks/metrics_service/metrics_service_integration_test.cc index 81b89d4ff9bf..0bf4f9cada60 100644 --- a/test/extensions/stats_sinks/metrics_service/metrics_service_integration_test.cc +++ b/test/extensions/stats_sinks/metrics_service/metrics_service_integration_test.cc @@ -30,6 +30,9 @@ class MetricsServiceIntegrationTest : public Grpc::VersionedGrpcClientIntegratio } void initialize() override { + if (apiVersion() != envoy::config::core::v3::ApiVersion::V3) { + config_helper_.enableDeprecatedV2Api(); + } config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { // metrics_service cluster for Envoy gRPC. auto* metrics_service_cluster = bootstrap.mutable_static_resources()->add_clusters(); diff --git a/test/integration/ads_integration.cc b/test/integration/ads_integration.cc index c8f1c3b7b52c..cf0fbeb71046 100644 --- a/test/integration/ads_integration.cc +++ b/test/integration/ads_integration.cc @@ -20,16 +20,17 @@ using testing::AssertionResult; namespace Envoy { -AdsIntegrationTest::AdsIntegrationTest(const envoy::config::core::v3::ApiVersion api_version) - : HttpIntegrationTest( - Http::CodecClient::Type::HTTP2, ipVersion(), - ConfigHelper::adsBootstrap( - sotwOrDelta() == Grpc::SotwOrDelta::Sotw ? "GRPC" : "DELTA_GRPC", api_version)) { +AdsIntegrationTest::AdsIntegrationTest(envoy::config::core::v3::ApiVersion resource_api_version, + envoy::config::core::v3::ApiVersion transport_api_version) + : HttpIntegrationTest(Http::CodecClient::Type::HTTP2, ipVersion(), + ConfigHelper::adsBootstrap( + sotwOrDelta() == Grpc::SotwOrDelta::Sotw ? "GRPC" : "DELTA_GRPC", + resource_api_version, transport_api_version)) { use_lds_ = false; create_xds_upstream_ = true; tls_xds_upstream_ = true; sotw_or_delta_ = sotwOrDelta(); - api_version_ = api_version; + api_version_ = resource_api_version; setUpstreamProtocol(FakeHttpConnection::Type::HTTP2); } diff --git a/test/integration/ads_integration.h b/test/integration/ads_integration.h index 804b3b3f315e..18468f975d79 100644 --- a/test/integration/ads_integration.h +++ b/test/integration/ads_integration.h @@ -17,7 +17,9 @@ namespace Envoy { class AdsIntegrationTest : public Grpc::DeltaSotwIntegrationParamTest, public HttpIntegrationTest { public: - AdsIntegrationTest(const envoy::config::core::v3::ApiVersion api_version); + AdsIntegrationTest(envoy::config::core::v3::ApiVersion resource_api_version, + envoy::config::core::v3::ApiVersion transport_api_version = + envoy::config::core::v3::ApiVersion::AUTO); AdsIntegrationTest() : AdsIntegrationTest(envoy::config::core::v3::ApiVersion::V3) {} void TearDown() override; diff --git a/test/integration/ads_integration_test.cc b/test/integration/ads_integration_test.cc index 90e2b56230d9..1f49cd284222 100644 --- a/test/integration/ads_integration_test.cc +++ b/test/integration/ads_integration_test.cc @@ -1403,20 +1403,6 @@ TEST_P(AdsClusterV2Test, DEPRECATED_FEATURE_TEST(BasicClusterInitialWarming)) { test_server_->waitForGaugeGe("cluster_manager.active_clusters", 2); } -// If we attempt to use v2 APIs by default, the configuration should be rejected. -TEST_P(AdsClusterV2Test, DEPRECATED_FEATURE_TEST(RejectV2ConfigByDefault)) { - fatal_by_default_v2_override_ = true; - initialize(); - const auto cds_type_url = Config::getTypeUrl( - envoy::config::core::v3::ApiVersion::V2); - - EXPECT_TRUE(compareDiscoveryRequest(cds_type_url, "", {}, {}, {}, true)); - sendDiscoveryResponse( - cds_type_url, {buildCluster("cluster_0")}, {buildCluster("cluster_0")}, {}, "1", true); - test_server_->waitForCounterGe("cluster_manager.cds.update_rejected", 1); - EXPECT_EQ(1, test_server_->gauge("runtime.deprecated_feature_seen_since_process_start")->value()); -} - // Verify CDS is paused during cluster warming. TEST_P(AdsClusterV2Test, DEPRECATED_FEATURE_TEST(CdsPausedDuringWarming)) { initialize(); @@ -1567,4 +1553,30 @@ TEST_P(AdsClusterV2Test, DEPRECATED_FEATURE_TEST(TypeUrlAnnotationRegression)) { test_server_->waitForCounterGe("cluster_manager.cds.update_rejected", 1); } +// Validate v2 resource are rejected by default. +class AdsV2ResourceRejectTest : public AdsIntegrationTest { +public: + // We need to use a v3 transport as we're not going to set the v2 allow overrides. + AdsV2ResourceRejectTest() + : AdsIntegrationTest(envoy::config::core::v3::ApiVersion::V2, + envoy::config::core::v3::ApiVersion::V3) {} +}; + +INSTANTIATE_TEST_SUITE_P(IpVersionsClientTypeDelta, AdsV2ResourceRejectTest, + DELTA_SOTW_GRPC_CLIENT_INTEGRATION_PARAMS); + +// If we attempt to use v2 APIs by default, the configuration should be rejected. +TEST_P(AdsV2ResourceRejectTest, DEPRECATED_FEATURE_TEST(RejectV2ConfigByDefault)) { + fatal_by_default_v2_override_ = true; + initialize(); + const auto cds_type_url = Config::getTypeUrl( + envoy::config::core::v3::ApiVersion::V2); + + EXPECT_TRUE(compareDiscoveryRequest(cds_type_url, "", {}, {}, {}, true)); + sendDiscoveryResponse( + cds_type_url, {buildCluster("cluster_0")}, {buildCluster("cluster_0")}, {}, "1", true); + test_server_->waitForCounterGe("cluster_manager.cds.update_rejected", 1); + EXPECT_EQ(1, test_server_->gauge("runtime.deprecated_feature_seen_since_process_start")->value()); +} + } // namespace Envoy diff --git a/test/integration/hds_integration_test.cc b/test/integration/hds_integration_test.cc index 378538f2fcb0..c473226d73f1 100644 --- a/test/integration/hds_integration_test.cc +++ b/test/integration/hds_integration_test.cc @@ -38,6 +38,9 @@ class HdsIntegrationTest : public Grpc::VersionedGrpcClientIntegrationParamTest, HttpIntegrationTest::createUpstreams(); } void initialize() override { + if (apiVersion() != envoy::config::core::v3::ApiVersion::V3) { + config_helper_.enableDeprecatedV2Api(); + } setUpstreamCount(upstream_endpoints_); config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { // Setup hds and corresponding gRPC cluster. diff --git a/test/integration/load_stats_integration_test.cc b/test/integration/load_stats_integration_test.cc index f7b007fa9a69..d47682397192 100644 --- a/test/integration/load_stats_integration_test.cc +++ b/test/integration/load_stats_integration_test.cc @@ -105,6 +105,9 @@ class LoadStatsIntegrationTest : public Grpc::VersionedGrpcClientIntegrationPara } void initialize() override { + if (apiVersion() != envoy::config::core::v3::ApiVersion::V3) { + config_helper_.enableDeprecatedV2Api(); + } setUpstreamCount(upstream_endpoints_); config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { // Setup load reporting and corresponding gRPC cluster. diff --git a/test/server/server_test.cc b/test/server/server_test.cc index 5669a566527e..162126c25b3c 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -694,6 +694,24 @@ TEST_P(ServerInstanceImplTest, "V2 .and AUTO. xDS transport protocol versions are deprecated in.*"); } +// Validate that bootstrap with v2 ADS transport is rejected when --bootstrap-version is not +// set. +TEST_P(ServerInstanceImplTest, + DEPRECATED_FEATURE_TEST(FailToLoadV2AdsTransportWithoutExplicitVersion)) { + EXPECT_THROW_WITH_REGEX(initialize("test/server/test_data/server/ads_v2.yaml"), + DeprecatedMajorVersionException, + "V2 .and AUTO. xDS transport protocol versions are deprecated in.*"); +} + +// Validate that bootstrap with v2 HDS transport is rejected when --bootstrap-version is not +// set. +TEST_P(ServerInstanceImplTest, + DEPRECATED_FEATURE_TEST(FailToLoadV2HdsTransportWithoutExplicitVersion)) { + EXPECT_THROW_WITH_REGEX(initialize("test/server/test_data/server/hds_v2.yaml"), + DeprecatedMajorVersionException, + "V2 .and AUTO. xDS transport protocol versions are deprecated in.*"); +} + // Validate that bootstrap v2 is rejected when --bootstrap-version is not set. TEST_P(ServerInstanceImplTest, DEPRECATED_FEATURE_TEST(FailToLoadV2BootstrapWithoutExplicitVersion)) { @@ -831,6 +849,18 @@ TEST_P(ServerInstanceImplTest, DEPRECATED_FEATURE_TEST(LoadsV2TransportWithoutEx initialize("test/server/test_data/server/dynamic_v2.yaml"); } +// Validate that bootstrap with v2 ADS transport loads when --bootstrap-version is set. +TEST_P(ServerInstanceImplTest, DEPRECATED_FEATURE_TEST(LoadsV2AdsTransportWithoutExplicitVersion)) { + options_.bootstrap_version_ = 2; + initialize("test/server/test_data/server/ads_v2.yaml"); +} + +// Validate that bootstrap with v2 HDS transport loads when --bootstrap-version is set. +TEST_P(ServerInstanceImplTest, DEPRECATED_FEATURE_TEST(LoadsV2HdsTransportWithoutExplicitVersion)) { + options_.bootstrap_version_ = 2; + initialize("test/server/test_data/server/hds_v2.yaml"); +} + // Validate that bootstrap pb_text loads. TEST_P(ServerInstanceImplTest, LoadsBootstrapFromPbText) { EXPECT_LOG_NOT_CONTAINS("trace", "Configuration does not parse cleanly as v3", diff --git a/test/server/test_data/server/ads_v2.yaml b/test/server/test_data/server/ads_v2.yaml new file mode 100644 index 000000000000..03e5693b75cb --- /dev/null +++ b/test/server/test_data/server/ads_v2.yaml @@ -0,0 +1,14 @@ +node: + id: bootstrap_id + cluster: bootstrap_cluster +static_resources: + clusters: + - name: dummy_cluster + connect_timeout: 1s +dynamic_resources: + ads_config: + api_type: GRPC + transport_api_version: V2 + grpc_services: + envoy_grpc: + cluster_name: "dummy_cluster" diff --git a/test/server/test_data/server/hds_v2.yaml b/test/server/test_data/server/hds_v2.yaml new file mode 100644 index 000000000000..fb87f8239e63 --- /dev/null +++ b/test/server/test_data/server/hds_v2.yaml @@ -0,0 +1,13 @@ +node: + id: bootstrap_id + cluster: bootstrap_cluster +static_resources: + clusters: + - name: dummy_cluster + connect_timeout: 1s +hds_config: + api_type: GRPC + transport_api_version: V2 + grpc_services: + envoy_grpc: + cluster_name: "dummy_cluster" diff --git a/tools/code_format/check_format.py b/tools/code_format/check_format.py index 7e666ed0619f..c7b68dba7cb9 100755 --- a/tools/code_format/check_format.py +++ b/tools/code_format/check_format.py @@ -110,6 +110,11 @@ EXCEPTION_DENYLIST = ("./source/common/http/http2/codec_impl.h", "./source/common/http/http2/codec_impl.cc") +# Header files that can throw exceptions. These should be limited; the only +# valid situation identified so far is template functions used for config +# processing. +EXCEPTION_ALLOWLIST = ("./source/common/config/utility.h") + # We want all URL references to exist in repository_locations.bzl files and have # metadata that conforms to the schema in ./api/bazel/external_deps.bzl. Below # we have some exceptions for either infrastructure files or places we fall @@ -424,11 +429,11 @@ def allowlistedForUnpackTo(self, file_path): def denylistedForExceptions(self, file_path): # Returns true when it is a non test header file or the file_path is in DENYLIST or - # it is under toos/testdata subdirectory. + # it is under tools/testdata subdirectory. if file_path.endswith(DOCS_SUFFIX): return False - return (file_path.endswith('.h') and not file_path.startswith("./test/")) or file_path in EXCEPTION_DENYLIST \ + return (file_path.endswith('.h') and not file_path.startswith("./test/") and not file_path in EXCEPTION_ALLOWLIST) or file_path in EXCEPTION_DENYLIST \ or self.isInSubdir(file_path, 'tools/testdata') def allowlistedForBuildUrls(self, file_path): From 357ed13f5d6c12ac0ef15e2d05216afa53953527 Mon Sep 17 00:00:00 2001 From: Taylor Barrella Date: Mon, 14 Dec 2020 17:23:23 -0800 Subject: [PATCH 27/49] Undeprecate use_original_dst (#14397) See #5355 Risk Level: Low Testing: Listener unit tests, grep Docs Changes: Generated documentation for the proto field Signed-off-by: Taylor Barrella --- api/envoy/config/listener/v3/listener.proto | 11 ++++++++--- api/envoy/config/listener/v4alpha/listener.proto | 11 ++++++++--- docs/root/version_history/current.rst | 1 + .../envoy/config/listener/v3/listener.proto | 9 +++++++-- .../envoy/config/listener/v4alpha/listener.proto | 11 ++++++++--- include/envoy/network/listener.h | 3 +-- source/server/listener_impl.cc | 6 +++--- 7 files changed, 36 insertions(+), 16 deletions(-) diff --git a/api/envoy/config/listener/v3/listener.proto b/api/envoy/config/listener/v3/listener.proto index beb5cd16f68f..682861d5f2d9 100644 --- a/api/envoy/config/listener/v3/listener.proto +++ b/api/envoy/config/listener/v3/listener.proto @@ -93,9 +93,7 @@ message Listener { } } - reserved 14, 4; - - reserved "use_original_dst"; + reserved 14; // The unique name by which this listener is known. If no name is provided, // Envoy will allocate an internal UUID for the listener. If the listener is to be dynamically @@ -116,6 +114,13 @@ message Listener { // :ref:`FAQ entry `. repeated FilterChain filter_chains = 3; + // If a connection is redirected using *iptables*, the port on which the proxy + // receives it might be different from the original destination address. When this flag is set to + // true, the listener hands off redirected connections to the listener associated with the + // original destination address. If there is no listener associated with the original destination + // address, the connection is handled by the listener that receives it. Defaults to false. + google.protobuf.BoolValue use_original_dst = 4; + // The default filter chain if none of the filter chain matches. If no default filter chain is supplied, // the connection will be closed. The filter chain match is ignored in this field. FilterChain default_filter_chain = 25; diff --git a/api/envoy/config/listener/v4alpha/listener.proto b/api/envoy/config/listener/v4alpha/listener.proto index 44d56f32170e..86ba484cdcf4 100644 --- a/api/envoy/config/listener/v4alpha/listener.proto +++ b/api/envoy/config/listener/v4alpha/listener.proto @@ -96,9 +96,7 @@ message Listener { } } - reserved 14, 4; - - reserved "use_original_dst"; + reserved 14; // The unique name by which this listener is known. If no name is provided, // Envoy will allocate an internal UUID for the listener. If the listener is to be dynamically @@ -119,6 +117,13 @@ message Listener { // :ref:`FAQ entry `. repeated FilterChain filter_chains = 3; + // If a connection is redirected using *iptables*, the port on which the proxy + // receives it might be different from the original destination address. When this flag is set to + // true, the listener hands off redirected connections to the listener associated with the + // original destination address. If there is no listener associated with the original destination + // address, the connection is handled by the listener that receives it. Defaults to false. + google.protobuf.BoolValue use_original_dst = 4; + // The default filter chain if none of the filter chain matches. If no default filter chain is supplied, // the connection will be closed. The filter chain match is ignored in this field. FilterChain default_filter_chain = 25; diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 94e90b463b72..7109eb761e53 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -69,6 +69,7 @@ New Features * jwt_authn: added support for :ref:`per-route config `. * kill_request: added new :ref:`HTTP kill request filter `. * listener: added an optional :ref:`default filter chain `. If this field is supplied, and none of the :ref:`filter_chains ` matches, this default filter chain is used to serve the connection. +* listener: added back the :ref:`use_original_dst field `. * log: added a new custom flag ``%_`` to the log pattern to print the actual message to log, but with escaped newlines. * lua: added `downstreamDirectRemoteAddress()` and `downstreamLocalAddress()` APIs to :ref:`streamInfo() `. * mongo_proxy: the list of commands to produce metrics for is now :ref:`configurable `. diff --git a/generated_api_shadow/envoy/config/listener/v3/listener.proto b/generated_api_shadow/envoy/config/listener/v3/listener.proto index bc84041e2643..682861d5f2d9 100644 --- a/generated_api_shadow/envoy/config/listener/v3/listener.proto +++ b/generated_api_shadow/envoy/config/listener/v3/listener.proto @@ -114,6 +114,13 @@ message Listener { // :ref:`FAQ entry `. repeated FilterChain filter_chains = 3; + // If a connection is redirected using *iptables*, the port on which the proxy + // receives it might be different from the original destination address. When this flag is set to + // true, the listener hands off redirected connections to the listener associated with the + // original destination address. If there is no listener associated with the original destination + // address, the connection is handled by the listener that receives it. Defaults to false. + google.protobuf.BoolValue use_original_dst = 4; + // The default filter chain if none of the filter chain matches. If no default filter chain is supplied, // the connection will be closed. The filter chain match is ignored in this field. FilterChain default_filter_chain = 25; @@ -265,6 +272,4 @@ message Listener { // The maximum length a tcp listener's pending connections queue can grow to. If no value is // provided net.core.somaxconn will be used on Linux and 128 otherwise. google.protobuf.UInt32Value tcp_backlog_size = 24; - - google.protobuf.BoolValue hidden_envoy_deprecated_use_original_dst = 4 [deprecated = true]; } diff --git a/generated_api_shadow/envoy/config/listener/v4alpha/listener.proto b/generated_api_shadow/envoy/config/listener/v4alpha/listener.proto index 44d56f32170e..86ba484cdcf4 100644 --- a/generated_api_shadow/envoy/config/listener/v4alpha/listener.proto +++ b/generated_api_shadow/envoy/config/listener/v4alpha/listener.proto @@ -96,9 +96,7 @@ message Listener { } } - reserved 14, 4; - - reserved "use_original_dst"; + reserved 14; // The unique name by which this listener is known. If no name is provided, // Envoy will allocate an internal UUID for the listener. If the listener is to be dynamically @@ -119,6 +117,13 @@ message Listener { // :ref:`FAQ entry `. repeated FilterChain filter_chains = 3; + // If a connection is redirected using *iptables*, the port on which the proxy + // receives it might be different from the original destination address. When this flag is set to + // true, the listener hands off redirected connections to the listener associated with the + // original destination address. If there is no listener associated with the original destination + // address, the connection is handled by the listener that receives it. Defaults to false. + google.protobuf.BoolValue use_original_dst = 4; + // The default filter chain if none of the filter chain matches. If no default filter chain is supplied, // the connection will be closed. The filter chain match is ignored in this field. FilterChain default_filter_chain = 25; diff --git a/include/envoy/network/listener.h b/include/envoy/network/listener.h index d708d02ce7e0..73edb4e7e9d5 100644 --- a/include/envoy/network/listener.h +++ b/include/envoy/network/listener.h @@ -94,8 +94,7 @@ class ListenerConfig { /** * @return bool if a connection should be handed off to another Listener after the original * destination address has been restored. 'true' when 'use_original_dst' flag in listener - * configuration is set, false otherwise. Note that this flag is deprecated and will be - * removed from the v2 API. + * configuration is set, false otherwise. */ virtual bool handOffRestoredDestinationConnections() const PURE; diff --git a/source/server/listener_impl.cc b/source/server/listener_impl.cc index 322699861133..03d156734c53 100644 --- a/source/server/listener_impl.cc +++ b/source/server/listener_impl.cc @@ -245,7 +245,7 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, : parent_(parent), address_(Network::Address::resolveProtoAddress(config.address())), bind_to_port_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config.deprecated_v1(), bind_to_port, true)), hand_off_restored_destination_connections_( - PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, hidden_envoy_deprecated_use_original_dst, false)), + PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_original_dst, false)), per_connection_buffer_limit_bytes_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, per_connection_buffer_limit_bytes, 1024 * 1024)), listener_tag_(parent_.factory_.nextListenerTag()), name_(name), added_via_api_(added_via_api), @@ -324,7 +324,7 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, : parent_(parent), address_(origin.address_), bind_to_port_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config.deprecated_v1(), bind_to_port, true)), hand_off_restored_destination_connections_( - PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, hidden_envoy_deprecated_use_original_dst, false)), + PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_original_dst, false)), per_connection_buffer_limit_bytes_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, per_connection_buffer_limit_bytes, 1024 * 1024)), listener_tag_(origin.listener_tag_), name_(name), added_via_api_(added_via_api), @@ -529,7 +529,7 @@ void ListenerImpl::buildSocketOptions() { void ListenerImpl::buildOriginalDstListenerFilter() { // Add original dst listener filter if 'use_original_dst' flag is set. - if (PROTOBUF_GET_WRAPPED_OR_DEFAULT(config_, hidden_envoy_deprecated_use_original_dst, false)) { + if (PROTOBUF_GET_WRAPPED_OR_DEFAULT(config_, use_original_dst, false)) { auto& factory = Config::Utility::getAndCheckFactoryByName( Extensions::ListenerFilters::ListenerFilterNames::get().OriginalDst); From 43bf707f9516264f086ded6431e294150fa30b06 Mon Sep 17 00:00:00 2001 From: John Esmet Date: Tue, 15 Dec 2020 03:29:47 -0500 Subject: [PATCH 28/49] formatter: add text_format_source, relax minimum string length on text_format (#14276) This change is motivated by #14221 where we use a SubstitutionFormatString as a way to define custom HTTP response body rewrites. Commit Message: formatter: add text_format_source, relax minimum string length on text_format in SubstitutionFormatString Additional Description: The relaxed field validation on text_format now allows a user to replace something with nothing, e.g. to replace a non-empty HTTP response body with an empty one. The text_format_source field allows for a DataSource to be used to supply text inside of providing it inline. Risk Level: low (new fields) Testing: unit test needed for text_format_source Docs Changes: NEEDED Release Notes: NEEDED Platform Specific Features: Signed-off-by: John Esmet --- .../core/v3/substitution_format_string.proto | 25 ++++++- .../v4alpha/substitution_format_string.proto | 44 +++++++----- .../access_log_format_helper.template.yaml | 8 +-- configs/envoy_double_proxy.template.yaml | 3 +- configs/envoy_front_proxy.template.yaml | 3 +- docs/root/version_history/current.rst | 3 + .../core/v3/substitution_format_string.proto | 25 ++++++- .../v4alpha/substitution_format_string.proto | 25 ++++++- source/common/formatter/BUILD | 2 + .../formatter/substitution_format_string.cc | 8 ++- .../formatter/substitution_format_string.h | 3 +- source/common/local_reply/BUILD | 1 + source/common/local_reply/local_reply.cc | 11 +-- .../extensions/access_loggers/file/config.cc | 11 +-- .../common/access_log/access_log_impl_test.cc | 6 +- test/common/formatter/BUILD | 1 + .../substitution_format_string_test.cc | 11 +-- test/common/local_reply/local_reply_test.cc | 71 ++++++++++++++++--- .../common/router/router_upstream_log_test.cc | 12 ++-- test/common/tcp_proxy/tcp_proxy_test.cc | 6 +- test/config/utility.cc | 6 +- .../access_loggers/file/config_test.cc | 3 +- .../local_reply_integration_test.cc | 6 +- .../proxy_proto_integration_test.cc | 2 +- .../integration/tcp_proxy_integration_test.cc | 2 +- 25 files changed, 233 insertions(+), 65 deletions(-) diff --git a/api/envoy/config/core/v3/substitution_format_string.proto b/api/envoy/config/core/v3/substitution_format_string.proto index 10d99b878bdd..fa14db45ddda 100644 --- a/api/envoy/config/core/v3/substitution_format_string.proto +++ b/api/envoy/config/core/v3/substitution_format_string.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.core.v3; +import "envoy/config/core/v3/base.proto"; + import "google/protobuf/struct.proto"; import "udpa/annotations/status.proto"; @@ -16,6 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // Configuration to use multiple :ref:`command operators ` // to generate a new string in either plain text or JSON format. +// [#next-free-field: 6] message SubstitutionFormatString { oneof format { option (validate.required) = true; @@ -36,7 +39,8 @@ message SubstitutionFormatString { // // upstream connect error:503:path=/foo // - string text_format = 1 [(validate.rules).string = {min_len: 1}]; + // Deprecated in favor of :ref:`text_format_source `. To migrate text format strings, use the :ref:`inline_string ` field. + string text_format = 1 [deprecated = true]; // Specify a format with command operators to form a JSON string. // Its details is described in :ref:`format dictionary`. @@ -61,6 +65,25 @@ message SubstitutionFormatString { // } // google.protobuf.Struct json_format = 2 [(validate.rules).message = {required: true}]; + + // Specify a format with command operators to form a text string. + // Its details is described in :ref:`format string`. + // + // For example, setting ``text_format`` like below, + // + // .. validated-code-block:: yaml + // :type-name: envoy.config.core.v3.SubstitutionFormatString + // + // text_format_source: + // inline_string: "%LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path=%REQ(:path)%\n" + // + // generates plain text similar to: + // + // .. code-block:: text + // + // upstream connect error:503:path=/foo + // + DataSource text_format_source = 5; } // If set to true, when command operators are evaluated to null, diff --git a/api/envoy/config/core/v4alpha/substitution_format_string.proto b/api/envoy/config/core/v4alpha/substitution_format_string.proto index e996bcbc0cf6..383d5dfd5e6d 100644 --- a/api/envoy/config/core/v4alpha/substitution_format_string.proto +++ b/api/envoy/config/core/v4alpha/substitution_format_string.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.core.v4alpha; +import "envoy/config/core/v4alpha/base.proto"; + import "google/protobuf/struct.proto"; import "udpa/annotations/status.proto"; @@ -17,31 +19,18 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // Configuration to use multiple :ref:`command operators ` // to generate a new string in either plain text or JSON format. +// [#next-free-field: 6] message SubstitutionFormatString { option (udpa.annotations.versioning).previous_message_type = "envoy.config.core.v3.SubstitutionFormatString"; + reserved 1; + + reserved "text_format"; + oneof format { option (validate.required) = true; - // Specify a format with command operators to form a text string. - // Its details is described in :ref:`format string`. - // - // For example, setting ``text_format`` like below, - // - // .. validated-code-block:: yaml - // :type-name: envoy.config.core.v3.SubstitutionFormatString - // - // text_format: "%LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path=%REQ(:path)%\n" - // - // generates plain text similar to: - // - // .. code-block:: text - // - // upstream connect error:503:path=/foo - // - string text_format = 1 [(validate.rules).string = {min_len: 1}]; - // Specify a format with command operators to form a JSON string. // Its details is described in :ref:`format dictionary`. // Values are rendered as strings, numbers, or boolean values as appropriate. @@ -65,6 +54,25 @@ message SubstitutionFormatString { // } // google.protobuf.Struct json_format = 2 [(validate.rules).message = {required: true}]; + + // Specify a format with command operators to form a text string. + // Its details is described in :ref:`format string`. + // + // For example, setting ``text_format`` like below, + // + // .. validated-code-block:: yaml + // :type-name: envoy.config.core.v3.SubstitutionFormatString + // + // text_format_source: + // inline_string: "%LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path=%REQ(:path)%\n" + // + // generates plain text similar to: + // + // .. code-block:: text + // + // upstream connect error:503:path=/foo + // + DataSource text_format_source = 5; } // If set to true, when command operators are evaluated to null, diff --git a/configs/access_log_format_helper.template.yaml b/configs/access_log_format_helper.template.yaml index 9861a51e9bfb..64c087587153 100644 --- a/configs/access_log_format_helper.template.yaml +++ b/configs/access_log_format_helper.template.yaml @@ -1,15 +1,15 @@ {% macro ingress_sampled_log() -%} - log_format: {text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH):256% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\"\n"} + log_format: {text_format_source: {inline_string: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH):256% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\"\n"}} {% endmacro %} {% macro ingress_full() -%} - log_format: {text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\"\n"} + log_format: {text_format_source: {inline_string: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\"\n"}} {% endmacro %} {% macro egress_error_log() -%} - log_format: {text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH):256% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n"} + log_format: {text_format_source: {inline_string: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH):256% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n"}} {% endmacro %} {% macro egress_error_amazon_service() -%} - log_format: {text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH):256% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\" \"%RESP(X-AMZN-RequestId)%\"\n"} + log_format: {text_format_source: { inline_string: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH):256% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\" \"%RESP(X-AMZN-RequestId)%\"\n"}} {% endmacro %} diff --git a/configs/envoy_double_proxy.template.yaml b/configs/envoy_double_proxy.template.yaml index d674a56e9355..1dcaaf84765a 100644 --- a/configs/envoy_double_proxy.template.yaml +++ b/configs/envoy_double_proxy.template.yaml @@ -91,7 +91,8 @@ "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog path: /var/log/envoy/access_error.log log_format: - text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%REQ(X-LYFT-USER-ID)%\" \"%RESP(GRPC-STATUS)%\"\n" + text_format_source: + inline_string: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%REQ(X-LYFT-USER-ID)%\" \"%RESP(GRPC-STATUS)%\"\n" {% if proxy_proto %} use_remote_address: true {%endif -%} diff --git a/configs/envoy_front_proxy.template.yaml b/configs/envoy_front_proxy.template.yaml index 974fae913d82..e60670f112c1 100644 --- a/configs/envoy_front_proxy.template.yaml +++ b/configs/envoy_front_proxy.template.yaml @@ -102,7 +102,8 @@ "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog path: "/var/log/envoy/access_error.log" log_format: - text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%REQ(X-LYFT-USER-ID)%\" \"%RESP(GRPC-STATUS)%\"\n" + text_format_source: + inline_string: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%REQ(X-LYFT-USER-ID)%\" \"%RESP(GRPC-STATUS)%\"\n" {% endmacro -%} static_resources: listeners: diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 7109eb761e53..43fc32a15e14 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -17,6 +17,7 @@ Minor Behavior Changes * expr filter: added `connection.termination_details` property support. * ext_authz filter: disable `envoy.reloadable_features.ext_authz_measure_timeout_on_check_created` by default. * ext_authz filter: the deprecated field :ref:`use_alpha ` is no longer supported and cannot be set anymore. +* formatter: the :ref:`text_format ` field no longer requires at least one byte, and may now be the empty string. It has also become deprecated: see Deprecated section. * grpc_web filter: if a `grpc-accept-encoding` header is present it's passed as-is to the upstream and if it isn't `grpc-accept-encoding:identity` is sent instead. The header was always overwriten with `grpc-accept-encoding:identity,deflate,gzip` before. * http: upstream protocol will now only be logged if an upstream stream was established. * jwt_authn filter: added support of Jwt time constraint verification with a clock skew (default to 60 seconds) and added a filter config field :ref:`clock_skew_seconds ` to configure it. @@ -60,6 +61,7 @@ New Features * compression: the :ref:`compressor ` filter adds support for compressing request payloads. Its configuration is unified with the :ref:`decompressor ` filter with two new fields for different directions - :ref:`requests ` and :ref:`responses `. The latter deprecates the old response-specific fields and, if used, roots the response-specific stats in `.compressor...response.*` instead of `.compressor...*`. * config: added ability to flush stats when the admin's :ref:`/stats endpoint ` is hit instead of on a timer via :ref:`stats_flush_on_admin `. * config: added new runtime feature `envoy.features.enable_all_deprecated_features` that allows the use of all deprecated features. +* formatter: added new :ref:`text_format_source ` field to support format strings both inline and from a file. * grpc: implemented header value syntax support when defining :ref:`initial metadata ` for gRPC-based `ext_authz` :ref:`HTTP ` and :ref:`network ` filters, and :ref:`ratelimit ` filters. * grpc-json: added support for configuring :ref:`unescaping behavior ` for path components. * hds: added support for delta updates in the :ref:`HealthCheckSpecifier `, making only the Endpoints and Health Checkers that changed be reconstructed on receiving a new message, rather than the entire HDS. @@ -95,6 +97,7 @@ Deprecated ---------- * cluster: HTTP configuration for upstream clusters has beem reworked. HTTP-specific configuration is now done in the new :ref:`http_protocol_options ` message, configured via the cluster's :ref:`extension_protocol_options`. This replaces explicit HTTP configuration in cluster config, including :ref:`upstream_http_protocol_options` :ref:`common_http_protocol_options` :ref:`http_protocol_options` :ref:`http2_protocol_options` and :ref:`protocol_selection`. Examples of before-and-after configuration can be found in the :ref:`http_protocol_options docs ` and all of Envoy's example configurations have been updated to the new style of config. * compression: the fields :ref:`content_length `, :ref:`content_type `, :ref:`disable_on_etag_header `, :ref:`remove_accept_encoding_header ` and :ref:`runtime_enabled ` of the :ref:`Compressor ` message have been deprecated in favor of :ref:`response_direction_config `. +* formatter: :ref:`text_format ` is now deprecated in favor of :ref:`text_format_source `. To migrate existing text format strings, use the :ref:`inline_string ` field. * gzip: :ref:`HTTP Gzip filter ` is rejected now unless explicitly allowed with :ref:`runtime override ` `envoy.deprecated_features.allow_deprecated_gzip_http_filter` set to `true`. * logging: the `--log-format-prefix-with-location` option is removed. * ratelimit: the :ref:`dynamic metadata ` action is deprecated in favor of the more generic :ref:`metadata ` action. diff --git a/generated_api_shadow/envoy/config/core/v3/substitution_format_string.proto b/generated_api_shadow/envoy/config/core/v3/substitution_format_string.proto index 10d99b878bdd..fa14db45ddda 100644 --- a/generated_api_shadow/envoy/config/core/v3/substitution_format_string.proto +++ b/generated_api_shadow/envoy/config/core/v3/substitution_format_string.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.core.v3; +import "envoy/config/core/v3/base.proto"; + import "google/protobuf/struct.proto"; import "udpa/annotations/status.proto"; @@ -16,6 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // Configuration to use multiple :ref:`command operators ` // to generate a new string in either plain text or JSON format. +// [#next-free-field: 6] message SubstitutionFormatString { oneof format { option (validate.required) = true; @@ -36,7 +39,8 @@ message SubstitutionFormatString { // // upstream connect error:503:path=/foo // - string text_format = 1 [(validate.rules).string = {min_len: 1}]; + // Deprecated in favor of :ref:`text_format_source `. To migrate text format strings, use the :ref:`inline_string ` field. + string text_format = 1 [deprecated = true]; // Specify a format with command operators to form a JSON string. // Its details is described in :ref:`format dictionary`. @@ -61,6 +65,25 @@ message SubstitutionFormatString { // } // google.protobuf.Struct json_format = 2 [(validate.rules).message = {required: true}]; + + // Specify a format with command operators to form a text string. + // Its details is described in :ref:`format string`. + // + // For example, setting ``text_format`` like below, + // + // .. validated-code-block:: yaml + // :type-name: envoy.config.core.v3.SubstitutionFormatString + // + // text_format_source: + // inline_string: "%LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path=%REQ(:path)%\n" + // + // generates plain text similar to: + // + // .. code-block:: text + // + // upstream connect error:503:path=/foo + // + DataSource text_format_source = 5; } // If set to true, when command operators are evaluated to null, diff --git a/generated_api_shadow/envoy/config/core/v4alpha/substitution_format_string.proto b/generated_api_shadow/envoy/config/core/v4alpha/substitution_format_string.proto index e996bcbc0cf6..8fc44f05a9c3 100644 --- a/generated_api_shadow/envoy/config/core/v4alpha/substitution_format_string.proto +++ b/generated_api_shadow/envoy/config/core/v4alpha/substitution_format_string.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.core.v4alpha; +import "envoy/config/core/v4alpha/base.proto"; + import "google/protobuf/struct.proto"; import "udpa/annotations/status.proto"; @@ -17,6 +19,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // Configuration to use multiple :ref:`command operators ` // to generate a new string in either plain text or JSON format. +// [#next-free-field: 6] message SubstitutionFormatString { option (udpa.annotations.versioning).previous_message_type = "envoy.config.core.v3.SubstitutionFormatString"; @@ -40,7 +43,8 @@ message SubstitutionFormatString { // // upstream connect error:503:path=/foo // - string text_format = 1 [(validate.rules).string = {min_len: 1}]; + // Deprecated in favor of :ref:`text_format_source `. To migrate text format strings, use the :ref:`inline_string ` field. + string hidden_envoy_deprecated_text_format = 1 [deprecated = true]; // Specify a format with command operators to form a JSON string. // Its details is described in :ref:`format dictionary`. @@ -65,6 +69,25 @@ message SubstitutionFormatString { // } // google.protobuf.Struct json_format = 2 [(validate.rules).message = {required: true}]; + + // Specify a format with command operators to form a text string. + // Its details is described in :ref:`format string`. + // + // For example, setting ``text_format`` like below, + // + // .. validated-code-block:: yaml + // :type-name: envoy.config.core.v3.SubstitutionFormatString + // + // text_format_source: + // inline_string: "%LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path=%REQ(:path)%\n" + // + // generates plain text similar to: + // + // .. code-block:: text + // + // upstream connect error:503:path=/foo + // + DataSource text_format_source = 5; } // If set to true, when command operators are evaluated to null, diff --git a/source/common/formatter/BUILD b/source/common/formatter/BUILD index d4eb45228abf..5747f9d1e7f9 100644 --- a/source/common/formatter/BUILD +++ b/source/common/formatter/BUILD @@ -14,10 +14,12 @@ envoy_cc_library( hdrs = ["substitution_formatter.h"], external_deps = ["abseil_str_format"], deps = [ + "//include/envoy/api:api_interface", "//include/envoy/formatter:substitution_formatter_interface", "//include/envoy/stream_info:stream_info_interface", "//source/common/common:assert_lib", "//source/common/common:utility_lib", + "//source/common/config:datasource_lib", "//source/common/config:metadata_lib", "//source/common/grpc:common_lib", "//source/common/http:utility_lib", diff --git a/source/common/formatter/substitution_format_string.cc b/source/common/formatter/substitution_format_string.cc index 89228e499b69..372b9fc6a612 100644 --- a/source/common/formatter/substitution_format_string.cc +++ b/source/common/formatter/substitution_format_string.cc @@ -1,5 +1,8 @@ #include "common/formatter/substitution_format_string.h" +#include "envoy/api/api.h" + +#include "common/config/datasource.h" #include "common/formatter/substitution_formatter.h" namespace Envoy { @@ -12,12 +15,15 @@ SubstitutionFormatStringUtils::createJsonFormatter(const ProtobufWkt::Struct& st } FormatterPtr SubstitutionFormatStringUtils::fromProtoConfig( - const envoy::config::core::v3::SubstitutionFormatString& config) { + const envoy::config::core::v3::SubstitutionFormatString& config, Api::Api& api) { switch (config.format_case()) { case envoy::config::core::v3::SubstitutionFormatString::FormatCase::kTextFormat: return std::make_unique(config.text_format(), config.omit_empty_values()); case envoy::config::core::v3::SubstitutionFormatString::FormatCase::kJsonFormat: { return createJsonFormatter(config.json_format(), true, config.omit_empty_values()); + case envoy::config::core::v3::SubstitutionFormatString::FormatCase::kTextFormatSource: + return std::make_unique( + Config::DataSource::read(config.text_format_source(), true, api)); } default: NOT_REACHED_GCOVR_EXCL_LINE; diff --git a/source/common/formatter/substitution_format_string.h b/source/common/formatter/substitution_format_string.h index 1878ec8ca0eb..d60033cabafd 100644 --- a/source/common/formatter/substitution_format_string.h +++ b/source/common/formatter/substitution_format_string.h @@ -2,6 +2,7 @@ #include +#include "envoy/api/api.h" #include "envoy/config/core/v3/substitution_format_string.pb.h" #include "envoy/formatter/substitution_formatter.h" @@ -19,7 +20,7 @@ class SubstitutionFormatStringUtils { * Generate a formatter object from config SubstitutionFormatString. */ static FormatterPtr - fromProtoConfig(const envoy::config::core::v3::SubstitutionFormatString& config); + fromProtoConfig(const envoy::config::core::v3::SubstitutionFormatString& config, Api::Api& api); /** * Generate a Json formatter object from proto::Struct config diff --git a/source/common/local_reply/BUILD b/source/common/local_reply/BUILD index 16995a49ea86..a01895c3bebc 100644 --- a/source/common/local_reply/BUILD +++ b/source/common/local_reply/BUILD @@ -13,6 +13,7 @@ envoy_cc_library( srcs = ["local_reply.cc"], hdrs = ["local_reply.h"], deps = [ + "//include/envoy/api:api_interface", "//include/envoy/http:codes_interface", "//include/envoy/http:header_map_interface", "//include/envoy/server:filter_config_interface", diff --git a/source/common/local_reply/local_reply.cc b/source/common/local_reply/local_reply.cc index a960ffa4e952..c3719513f124 100644 --- a/source/common/local_reply/local_reply.cc +++ b/source/common/local_reply/local_reply.cc @@ -3,6 +3,8 @@ #include #include +#include "envoy/api/api.h" + #include "common/access_log/access_log_impl.h" #include "common/common/enum_to_int.h" #include "common/config/datasource.h" @@ -20,8 +22,8 @@ class BodyFormatter { : formatter_(std::make_unique("%LOCAL_REPLY_BODY%")), content_type_(Http::Headers::get().ContentTypeValues.Text) {} - BodyFormatter(const envoy::config::core::v3::SubstitutionFormatString& config) - : formatter_(Formatter::SubstitutionFormatStringUtils::fromProtoConfig(config)), + BodyFormatter(const envoy::config::core::v3::SubstitutionFormatString& config, Api::Api& api) + : formatter_(Formatter::SubstitutionFormatStringUtils::fromProtoConfig(config, api)), content_type_( !config.content_type().empty() ? config.content_type() @@ -65,7 +67,8 @@ class ResponseMapper { } if (config.has_body_format_override()) { - body_formatter_ = std::make_unique(config.body_format_override()); + body_formatter_ = + std::make_unique(config.body_format_override(), context.api()); } header_parser_ = Envoy::Router::HeaderParser::configure(config.headers_to_add()); @@ -118,7 +121,7 @@ class LocalReplyImpl : public LocalReply { config, Server::Configuration::FactoryContext& context) : body_formatter_(config.has_body_format() - ? std::make_unique(config.body_format()) + ? std::make_unique(config.body_format(), context.api()) : std::make_unique()) { for (const auto& mapper : config.mappers()) { mappers_.emplace_back(std::make_unique(mapper, context)); diff --git a/source/extensions/access_loggers/file/config.cc b/source/extensions/access_loggers/file/config.cc index 73acb38ffd6b..9c850966f882 100644 --- a/source/extensions/access_loggers/file/config.cc +++ b/source/extensions/access_loggers/file/config.cc @@ -35,8 +35,9 @@ FileAccessLogFactory::createAccessLogInstance(const Protobuf::Message& config, formatter = Formatter::SubstitutionFormatUtils::defaultSubstitutionFormatter(); } else { envoy::config::core::v3::SubstitutionFormatString sff_config; - sff_config.set_text_format(fal_config.format()); - formatter = Formatter::SubstitutionFormatStringUtils::fromProtoConfig(sff_config); + sff_config.mutable_text_format_source()->set_inline_string(fal_config.format()); + formatter = + Formatter::SubstitutionFormatStringUtils::fromProtoConfig(sff_config, context.api()); } break; case envoy::extensions::access_loggers::file::v3::FileAccessLog::AccessLogFormatCase::kJsonFormat: @@ -47,11 +48,13 @@ FileAccessLogFactory::createAccessLogInstance(const Protobuf::Message& config, kTypedJsonFormat: { envoy::config::core::v3::SubstitutionFormatString sff_config; *sff_config.mutable_json_format() = fal_config.typed_json_format(); - formatter = Formatter::SubstitutionFormatStringUtils::fromProtoConfig(sff_config); + formatter = + Formatter::SubstitutionFormatStringUtils::fromProtoConfig(sff_config, context.api()); break; } case envoy::extensions::access_loggers::file::v3::FileAccessLog::AccessLogFormatCase::kLogFormat: - formatter = Formatter::SubstitutionFormatStringUtils::fromProtoConfig(fal_config.log_format()); + formatter = Formatter::SubstitutionFormatStringUtils::fromProtoConfig(fal_config.log_format(), + context.api()); break; case envoy::extensions::access_loggers::file::v3::FileAccessLog::AccessLogFormatCase:: ACCESS_LOG_FORMAT_NOT_SET: diff --git a/test/common/access_log/access_log_impl_test.cc b/test/common/access_log/access_log_impl_test.cc index 88238473b512..663f35cd1105 100644 --- a/test/common/access_log/access_log_impl_test.cc +++ b/test/common/access_log/access_log_impl_test.cc @@ -119,7 +119,8 @@ name: accesslog "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog path: /dev/null log_format: - text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH):256% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %ROUTE_NAME% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\"\n" + text_format_source: + inline_string: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH):256% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %ROUTE_NAME% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\"\n" )EOF"; InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); @@ -1062,7 +1063,8 @@ name: accesslog "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog path: /dev/null log_format: - text_format: "%GRPC_STATUS%\n" + text_format_source: + inline_string: "%GRPC_STATUS%\n" )EOF"; InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); diff --git a/test/common/formatter/BUILD b/test/common/formatter/BUILD index c0203c1f1f00..9001f4820bdc 100644 --- a/test/common/formatter/BUILD +++ b/test/common/formatter/BUILD @@ -59,6 +59,7 @@ envoy_cc_test( deps = [ "//source/common/formatter:substitution_format_string_lib", "//test/mocks/http:http_mocks", + "//test/mocks/server:factory_context_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/test/common/formatter/substitution_format_string_test.cc b/test/common/formatter/substitution_format_string_test.cc index 9109999ea8ae..53c9a2086b29 100644 --- a/test/common/formatter/substitution_format_string_test.cc +++ b/test/common/formatter/substitution_format_string_test.cc @@ -3,6 +3,7 @@ #include "common/formatter/substitution_format_string.h" #include "test/mocks/http/mocks.h" +#include "test/mocks/server/factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/test_common/utility.h" @@ -29,6 +30,7 @@ class SubstitutionFormatStringUtilsTest : public ::testing::Test { std::string body_; envoy::config::core::v3::SubstitutionFormatString config_; + NiceMock context_; }; TEST_F(SubstitutionFormatStringUtilsTest, TestEmptyIsInvalid) { @@ -39,11 +41,12 @@ TEST_F(SubstitutionFormatStringUtilsTest, TestEmptyIsInvalid) { TEST_F(SubstitutionFormatStringUtilsTest, TestFromProtoConfigText) { const std::string yaml = R"EOF( - text_format: "plain text, path=%REQ(:path)%, code=%RESPONSE_CODE%" + text_format_source: + inline_string: "plain text, path=%REQ(:path)%, code=%RESPONSE_CODE%" )EOF"; TestUtility::loadFromYaml(yaml, config_); - auto formatter = SubstitutionFormatStringUtils::fromProtoConfig(config_); + auto formatter = SubstitutionFormatStringUtils::fromProtoConfig(config_, context_.api()); EXPECT_EQ("plain text, path=/bar/foo, code=200", formatter->format(request_headers_, response_headers_, response_trailers_, stream_info_, body_)); @@ -60,7 +63,7 @@ TEST_F(SubstitutionFormatStringUtilsTest, TestFromProtoConfigJson) { )EOF"; TestUtility::loadFromYaml(yaml, config_); - auto formatter = SubstitutionFormatStringUtils::fromProtoConfig(config_); + auto formatter = SubstitutionFormatStringUtils::fromProtoConfig(config_, context_.api()); const auto out_json = formatter->format(request_headers_, response_headers_, response_trailers_, stream_info_, body_); @@ -89,7 +92,7 @@ TEST_F(SubstitutionFormatStringUtilsTest, TestInvalidConfigs) { for (const auto& yaml : invalid_configs) { TestUtility::loadFromYaml(yaml, config_); EXPECT_THROW_WITH_MESSAGE( - SubstitutionFormatStringUtils::fromProtoConfig(config_), EnvoyException, + SubstitutionFormatStringUtils::fromProtoConfig(config_, context_.api()), EnvoyException, "Only string values or nested structs are supported in the JSON access log format."); } } diff --git a/test/common/local_reply/local_reply_test.cc b/test/common/local_reply/local_reply_test.cc index e635bfea2d43..1ebab1e2e46a 100644 --- a/test/common/local_reply/local_reply_test.cc +++ b/test/common/local_reply/local_reply_test.cc @@ -106,7 +106,8 @@ TEST_F(LocalReplyTest, TestDefaultTextFormatter) { // Default text formatter without any mappers const std::string yaml = R"( body_format: - text_format: "%LOCAL_REPLY_BODY% %RESPONSE_CODE%" + text_format_source: + inline_string: "%LOCAL_REPLY_BODY% %RESPONSE_CODE%" )"; TestUtility::loadFromYaml(yaml, config_); auto local = Factory::create(config_, context_); @@ -163,6 +164,15 @@ TEST_F(LocalReplyTest, TestMapperRewrite) { status_code: 401 body: inline_string: "400 body text" + - filter: + status_code_filter: + comparison: + op: EQ + value: + default_value: 403 + runtime_key: key_b + body: + inline_string: "" - filter: status_code_filter: comparison: @@ -200,7 +210,17 @@ TEST_F(LocalReplyTest, TestMapperRewrite) { EXPECT_EQ(body_, "400 body text"); EXPECT_EQ(content_type_, "text/plain"); - // code=410 matches the second filter; rewrite body only + // code=403 matches the second filter; does not rewrite code, sets an empty body and content_type. + resetData(403); + body_ = "original body text"; + local->rewrite(&request_headers_, response_headers_, stream_info_, code_, body_, content_type_); + EXPECT_EQ(code_, static_cast(403)); + EXPECT_EQ(stream_info_.response_code_, 403U); + EXPECT_EQ(response_headers_.Status()->value().getStringView(), "403"); + EXPECT_EQ(body_, ""); + EXPECT_EQ(content_type_, "text/plain"); + + // code=410 matches the third filter; rewrite body only resetData(410); local->rewrite(&request_headers_, response_headers_, stream_info_, code_, body_, content_type_); EXPECT_EQ(code_, static_cast(410)); @@ -209,7 +229,7 @@ TEST_F(LocalReplyTest, TestMapperRewrite) { EXPECT_EQ(body_, "410 body text"); EXPECT_EQ(content_type_, "text/plain"); - // code=420 matches the third filter; rewrite code only + // code=420 matches the fourth filter; rewrite code only resetData(420); local->rewrite(&request_headers_, response_headers_, stream_info_, code_, body_, content_type_); EXPECT_EQ(code_, static_cast(421)); @@ -218,7 +238,7 @@ TEST_F(LocalReplyTest, TestMapperRewrite) { EXPECT_EQ(body_, TestInitBody); EXPECT_EQ(content_type_, "text/plain"); - // code=430 matches the fourth filter; rewrite nothing + // code=430 matches the fifth filter; rewrite nothing resetData(430); local->rewrite(&request_headers_, response_headers_, stream_info_, code_, body_, content_type_); EXPECT_EQ(code_, static_cast(430)); @@ -228,6 +248,37 @@ TEST_F(LocalReplyTest, TestMapperRewrite) { EXPECT_EQ(content_type_, "text/plain"); } +// Test that we can use the deprecated `text_format` field with an empty value, +// which is currently the only use case that it serves until we relax the +// size validation on `DataSource.inline_string`. +TEST_F(LocalReplyTest, DEPRECATED_FEATURE_TEST(TestMapperRewriteDeprecatedTextFormatEmpty)) { + // Match with response_code, and rewrite the code and body. + const std::string yaml = R"( + mappers: + - filter: + status_code_filter: + comparison: + op: EQ + value: + default_value: 404 + runtime_key: key_b + body_format_override: + text_format: "" +)"; + TestUtility::loadFromYaml(yaml, config_); + auto local = Factory::create(config_, context_); + + // code=404 matches the only filter; does not rewrite code, sets an empty body and content_type. + resetData(404); + body_ = "original body text"; + local->rewrite(&request_headers_, response_headers_, stream_info_, code_, body_, content_type_); + EXPECT_EQ(code_, static_cast(404)); + EXPECT_EQ(stream_info_.response_code_, 404U); + EXPECT_EQ(response_headers_.Status()->value().getStringView(), "404"); + EXPECT_EQ(body_, ""); + EXPECT_EQ(content_type_, "text/plain"); +} + TEST_F(LocalReplyTest, TestMapperFormat) { // Match with response_code, and rewrite the code and body. const std::string yaml = R"( @@ -259,7 +310,8 @@ TEST_F(LocalReplyTest, TestMapperFormat) { body: inline_string: "411 body text" body_format: - text_format: "%LOCAL_REPLY_BODY% %RESPONSE_CODE% default formatter" + text_format_source: + inline_string: "%LOCAL_REPLY_BODY% %RESPONSE_CODE% default formatter" )"; TestUtility::loadFromYaml(yaml, config_); auto local = Factory::create(config_, context_); @@ -350,7 +402,8 @@ TEST_F(LocalReplyTest, TestMapperWithContentType) { body: inline_string: "401 body text" body_format_override: - text_format: "

%LOCAL_REPLY_BODY%

" + text_format_source: + inline_string: "

%LOCAL_REPLY_BODY%

" content_type: "text/html; charset=UTF-8" - filter: status_code_filter: @@ -373,9 +426,11 @@ TEST_F(LocalReplyTest, TestMapperWithContentType) { body: inline_string: "421 body text" body_format_override: - text_format: "%LOCAL_REPLY_BODY%" + text_format_source: + inline_string: "%LOCAL_REPLY_BODY%" body_format: - text_format: "

%LOCAL_REPLY_BODY%

%RESPONSE_CODE% default formatter" + text_format_source: + inline_string: "

%LOCAL_REPLY_BODY%

%RESPONSE_CODE% default formatter" content_type: "text/html; charset=UTF-8" )"; TestUtility::loadFromYaml(yaml, config_); diff --git a/test/common/router/router_upstream_log_test.cc b/test/common/router/router_upstream_log_test.cc index 184cc54ef5a7..5931269988b1 100644 --- a/test/common/router/router_upstream_log_test.cc +++ b/test/common/router/router_upstream_log_test.cc @@ -42,9 +42,10 @@ name: accesslog typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog log_format: - text_format: "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL% %RESPONSE_CODE% - %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %REQ(:AUTHORITY)% %UPSTREAM_HOST% - %UPSTREAM_LOCAL_ADDRESS% %RESP(X-UPSTREAM-HEADER)% %TRAILER(X-TRAILER)%\n" + text_format_source: + inline_string: "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL% %RESPONSE_CODE% + %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %REQ(:AUTHORITY)% %UPSTREAM_HOST% + %UPSTREAM_LOCAL_ADDRESS% %RESP(X-UPSTREAM-HEADER)% %TRAILER(X-TRAILER)%\n" path: "/dev/null" )EOF"; @@ -296,8 +297,9 @@ name: accesslog typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog log_format: - text_format: "[%START_TIME%] %REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL% - %DURATION% %RESPONSE_DURATION% %REQUEST_DURATION%" + text_format_source: + inline_string: "[%START_TIME%] %REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL% + %DURATION% %RESPONSE_DURATION% %REQUEST_DURATION%" path: "/dev/null" )EOF"; diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 749c294314d7..540506dd97df 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -821,7 +821,8 @@ TEST(ConfigTest, AccessLogConfig) { { envoy::extensions::access_loggers::file::v3::FileAccessLog file_access_log; file_access_log.set_path("some_path"); - file_access_log.mutable_log_format()->set_text_format("the format specifier"); + file_access_log.mutable_log_format()->mutable_text_format_source()->set_inline_string( + "the format specifier"); log->mutable_typed_config()->PackFrom(file_access_log); } @@ -878,7 +879,8 @@ class TcpProxyTest : public testing::Test { access_log->set_name(Extensions::AccessLoggers::AccessLogNames::get().File); envoy::extensions::access_loggers::file::v3::FileAccessLog file_access_log; file_access_log.set_path("unused"); - file_access_log.mutable_log_format()->set_text_format(access_log_format); + file_access_log.mutable_log_format()->mutable_text_format_source()->set_inline_string( + access_log_format); access_log->mutable_typed_config()->PackFrom(file_access_log); return config; } diff --git a/test/config/utility.cc b/test/config/utility.cc index 680cd9c536ea..78316eac7d03 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -1017,7 +1017,8 @@ bool ConfigHelper::setAccessLog(const std::string& filename, absl::string_view f loadHttpConnectionManager(hcm_config); envoy::extensions::access_loggers::file::v3::FileAccessLog access_log_config; if (!format.empty()) { - access_log_config.mutable_log_format()->set_text_format(absl::StrCat(format, "\n")); + access_log_config.mutable_log_format()->mutable_text_format_source()->set_inline_string( + absl::StrCat(format, "\n")); } access_log_config.set_path(filename); hcm_config.mutable_access_log(0)->mutable_typed_config()->PackFrom(access_log_config); @@ -1032,7 +1033,8 @@ bool ConfigHelper::setListenerAccessLog(const std::string& filename, absl::strin } envoy::extensions::access_loggers::file::v3::FileAccessLog access_log_config; if (!format.empty()) { - access_log_config.mutable_log_format()->set_text_format(std::string(format)); + access_log_config.mutable_log_format()->mutable_text_format_source()->set_inline_string( + std::string(format)); } access_log_config.set_path(filename); bootstrap_.mutable_static_resources() diff --git a/test/extensions/access_loggers/file/config_test.cc b/test/extensions/access_loggers/file/config_test.cc index 9d7215e3f713..972de0b751a1 100644 --- a/test/extensions/access_loggers/file/config_test.cc +++ b/test/extensions/access_loggers/file/config_test.cc @@ -152,7 +152,8 @@ TEST_F(FileAccessLogTest, LogFormatText) { R"( path: "/foo" log_format: - text_format: "plain_text - %REQ(:path)% - %RESPONSE_CODE%" + text_format_source: + inline_string: "plain_text - %REQ(:path)% - %RESPONSE_CODE%" )", "plain_text - /bar/foo - 200", false); } diff --git a/test/integration/local_reply_integration_test.cc b/test/integration/local_reply_integration_test.cc index d89669f0cb76..74da5ae0c25a 100644 --- a/test/integration/local_reply_integration_test.cc +++ b/test/integration/local_reply_integration_test.cc @@ -162,7 +162,8 @@ TEST_P(LocalReplyIntegrationTest, MapStatusCodeAndFormatToJsonForFirstMatchingFi body: inline_string: "customized body text" body_format_override: - text_format: "%LOCAL_REPLY_BODY% %RESPONSE_CODE%" + text_format_source: + inline_string: "%LOCAL_REPLY_BODY% %RESPONSE_CODE%" - filter: header_filter: header: @@ -367,7 +368,8 @@ TEST_P(LocalReplyIntegrationTest, ShouldFormatResponseToCustomString) { body: inline_string: "customized body text" body_format: - text_format: "%RESPONSE_CODE% - %LOCAL_REPLY_BODY%" + text_format_source: + inline_string: "%RESPONSE_CODE% - %LOCAL_REPLY_BODY%" )EOF"; setLocalReplyConfig(yaml); initialize(); diff --git a/test/integration/proxy_proto_integration_test.cc b/test/integration/proxy_proto_integration_test.cc index 9cea358aa4d1..e11899b65b7b 100644 --- a/test/integration/proxy_proto_integration_test.cc +++ b/test/integration/proxy_proto_integration_test.cc @@ -250,7 +250,7 @@ TEST_P(ProxyProtoTcpIntegrationTest, AccessLog) { access_log->set_name("accesslog"); envoy::extensions::access_loggers::file::v3::FileAccessLog access_log_config; access_log_config.set_path(access_log_path); - access_log_config.mutable_log_format()->set_text_format( + access_log_config.mutable_log_format()->mutable_text_format_source()->set_inline_string( "remote=%DOWNSTREAM_REMOTE_ADDRESS% local=%DOWNSTREAM_LOCAL_ADDRESS%"); access_log->mutable_typed_config()->PackFrom(access_log_config); config_blob->PackFrom(tcp_proxy_config); diff --git a/test/integration/tcp_proxy_integration_test.cc b/test/integration/tcp_proxy_integration_test.cc index d8e98c10daba..8d9857e705db 100644 --- a/test/integration/tcp_proxy_integration_test.cc +++ b/test/integration/tcp_proxy_integration_test.cc @@ -316,7 +316,7 @@ TEST_P(TcpProxyIntegrationTest, AccessLog) { access_log->set_name("accesslog"); envoy::extensions::access_loggers::file::v3::FileAccessLog access_log_config; access_log_config.set_path(access_log_path); - access_log_config.mutable_log_format()->set_text_format( + access_log_config.mutable_log_format()->mutable_text_format_source()->set_inline_string( "upstreamlocal=%UPSTREAM_LOCAL_ADDRESS% " "upstreamhost=%UPSTREAM_HOST% downstream=%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% " "sent=%BYTES_SENT% received=%BYTES_RECEIVED%\n"); From a0486d87811e3914eeaf77805969910933c54f27 Mon Sep 17 00:00:00 2001 From: Kuat Date: Tue, 15 Dec 2020 02:09:44 -0800 Subject: [PATCH 29/49] wasm: cleanup duplicate stats (#14409) Removes stat duplication for `wasm.v8.created` and `wasm_vm.v8.created` which appear to be measuring the same thing. Same for `active`. Additional Description: Risk Level: low Testing: Docs Changes: none since no docs written yet for wasm Release Notes: Platform Specific Features: Signed-off-by: Kuat Yessenov --- source/extensions/common/wasm/ext/BUILD | 8 ---- source/extensions/common/wasm/wasm.cc | 5 +-- .../extensions/common/wasm/wasm_extension.cc | 6 +-- .../extensions/common/wasm/wasm_extension.h | 6 +-- .../common/wasm/wasm_runtime_factory.h | 1 - source/extensions/common/wasm/wasm_vm.cc | 5 +-- source/extensions/common/wasm/wasm_vm.h | 38 ++----------------- source/extensions/wasm_runtime/null/config.cc | 1 - source/extensions/wasm_runtime/v8/config.cc | 1 - .../wasm_runtime/wasmtime/config.cc | 1 - source/extensions/wasm_runtime/wavm/config.cc | 1 - test/extensions/common/wasm/wasm_vm_test.cc | 22 +++++------ 12 files changed, 21 insertions(+), 74 deletions(-) diff --git a/source/extensions/common/wasm/ext/BUILD b/source/extensions/common/wasm/ext/BUILD index 286c0774edfe..e23f578fb5dd 100644 --- a/source/extensions/common/wasm/ext/BUILD +++ b/source/extensions/common/wasm/ext/BUILD @@ -85,11 +85,3 @@ cc_proto_library( # "//external:protobuf_clib", ], ) - -filegroup( - name = "jslib", - srcs = [ - "envoy_wasm_intrinsics.js", - ], - visibility = ["//visibility:public"], -) diff --git a/source/extensions/common/wasm/wasm.cc b/source/extensions/common/wasm/wasm.cc index 1b0e8e513be2..c3099c554e87 100644 --- a/source/extensions/common/wasm/wasm.cc +++ b/source/extensions/common/wasm/wasm.cc @@ -102,7 +102,7 @@ void Wasm::initializeLifecycle(Server::ServerLifecycleNotifier& lifecycle_notifi Wasm::Wasm(absl::string_view runtime, absl::string_view vm_id, absl::string_view vm_configuration, absl::string_view vm_key, const Stats::ScopeSharedPtr& scope, Upstream::ClusterManager& cluster_manager, Event::Dispatcher& dispatcher) - : WasmBase(createWasmVm(runtime, scope), vm_id, vm_configuration, vm_key), scope_(scope), + : WasmBase(createWasmVm(runtime), vm_id, vm_configuration, vm_key), scope_(scope), cluster_manager_(cluster_manager), dispatcher_(dispatcher), time_source_(dispatcher.timeSource()), wasm_stats_(WasmStats{ @@ -116,8 +116,7 @@ Wasm::Wasm(WasmHandleSharedPtr base_wasm_handle, Event::Dispatcher& dispatcher) : WasmBase(base_wasm_handle, [&base_wasm_handle]() { return createWasmVm( - getEnvoyWasmIntegration(*base_wasm_handle->wasm()->wasm_vm()).runtime(), - getWasm(base_wasm_handle)->scope_); + getEnvoyWasmIntegration(*base_wasm_handle->wasm()->wasm_vm()).runtime()); }), scope_(getWasm(base_wasm_handle)->scope_), cluster_manager_(getWasm(base_wasm_handle)->clusterManager()), dispatcher_(dispatcher), diff --git a/source/extensions/common/wasm/wasm_extension.cc b/source/extensions/common/wasm/wasm_extension.cc index 1917fa792a82..d37fcf79f95b 100644 --- a/source/extensions/common/wasm/wasm_extension.cc +++ b/source/extensions/common/wasm/wasm_extension.cc @@ -40,10 +40,8 @@ RegisterWasmExtension::RegisterWasmExtension(WasmExtension* extension) { } std::unique_ptr -EnvoyWasm::createEnvoyWasmVmIntegration(const Stats::ScopeSharedPtr& scope, - absl::string_view runtime, - absl::string_view short_runtime) { - return std::make_unique(scope, runtime, short_runtime); +EnvoyWasm::createEnvoyWasmVmIntegration(absl::string_view runtime) { + return std::make_unique(runtime); } PluginHandleExtensionFactory EnvoyWasm::pluginFactory() { diff --git a/source/extensions/common/wasm/wasm_extension.h b/source/extensions/common/wasm/wasm_extension.h index 1061fd1cf1cf..5842fd6227d9 100644 --- a/source/extensions/common/wasm/wasm_extension.h +++ b/source/extensions/common/wasm/wasm_extension.h @@ -54,8 +54,7 @@ class WasmExtension : Logger::Loggable { virtual void initialize() = 0; virtual std::unique_ptr - createEnvoyWasmVmIntegration(const Stats::ScopeSharedPtr& scope, absl::string_view runtime, - absl::string_view short_runtime) = 0; + createEnvoyWasmVmIntegration(absl::string_view runtime) = 0; virtual PluginHandleExtensionFactory pluginFactory() = 0; virtual WasmHandleExtensionFactory wasmFactory() = 0; virtual WasmHandleExtensionCloneFactory wasmCloneFactory() = 0; @@ -101,8 +100,7 @@ class EnvoyWasm : public WasmExtension { ~EnvoyWasm() override = default; void initialize() override {} std::unique_ptr - createEnvoyWasmVmIntegration(const Stats::ScopeSharedPtr& scope, absl::string_view runtime, - absl::string_view short_runtime) override; + createEnvoyWasmVmIntegration(absl::string_view runtime) override; PluginHandleExtensionFactory pluginFactory() override; WasmHandleExtensionFactory wasmFactory() override; WasmHandleExtensionCloneFactory wasmCloneFactory() override; diff --git a/source/extensions/common/wasm/wasm_runtime_factory.h b/source/extensions/common/wasm/wasm_runtime_factory.h index d0d589705f91..018300bb22d3 100644 --- a/source/extensions/common/wasm/wasm_runtime_factory.h +++ b/source/extensions/common/wasm/wasm_runtime_factory.h @@ -18,7 +18,6 @@ class WasmRuntimeFactory { virtual WasmVmPtr createWasmVm() PURE; virtual absl::string_view name() PURE; - virtual absl::string_view shortName() PURE; std::string category() { return "envoy.wasm.runtime"; } }; diff --git a/source/extensions/common/wasm/wasm_vm.cc b/source/extensions/common/wasm/wasm_vm.cc index b7b8ec0c6734..56cfda3c8f5e 100644 --- a/source/extensions/common/wasm/wasm_vm.cc +++ b/source/extensions/common/wasm/wasm_vm.cc @@ -55,7 +55,7 @@ bool EnvoyWasmVmIntegration::getNullVmFunction(absl::string_view function_name, return false; } -WasmVmPtr createWasmVm(absl::string_view runtime, const Stats::ScopeSharedPtr& scope) { +WasmVmPtr createWasmVm(absl::string_view runtime) { if (runtime.empty()) { ENVOY_LOG_TO_LOGGER(Envoy::Logger::Registry::getLog(Envoy::Logger::Id::wasm), warn, "Failed to create Wasm VM with unspecified runtime"); @@ -72,8 +72,7 @@ WasmVmPtr createWasmVm(absl::string_view runtime, const Stats::ScopeSharedPtr& s } auto wasm = runtime_factory->createWasmVm(); - wasm->integration() = getWasmExtension()->createEnvoyWasmVmIntegration( - scope, runtime_factory->name(), runtime_factory->shortName()); + wasm->integration() = getWasmExtension()->createEnvoyWasmVmIntegration(runtime_factory->name()); return wasm; } diff --git a/source/extensions/common/wasm/wasm_vm.h b/source/extensions/common/wasm/wasm_vm.h index 0099e63d1144..fee77510a65d 100644 --- a/source/extensions/common/wasm/wasm_vm.h +++ b/source/extensions/common/wasm/wasm_vm.h @@ -5,7 +5,6 @@ #include "envoy/common/exception.h" #include "envoy/stats/scope.h" #include "envoy/stats/stats.h" -#include "envoy/stats/stats_macros.h" #include "common/common/logger.h" @@ -18,41 +17,14 @@ namespace Extensions { namespace Common { namespace Wasm { -/** - * Wasm host stats. - */ -#define ALL_VM_STATS(COUNTER, GAUGE) \ - COUNTER(created) \ - COUNTER(cloned) \ - GAUGE(active, NeverImport) - -struct VmStats { - ALL_VM_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT) -}; - // Wasm VM data providing stats. class EnvoyWasmVmIntegration : public proxy_wasm::WasmVmIntegration, Logger::Loggable { public: - EnvoyWasmVmIntegration(const Stats::ScopeSharedPtr& scope, absl::string_view runtime, - absl::string_view short_runtime) - : scope_(scope), runtime_(std::string(runtime)), short_runtime_(std::string(short_runtime)), - runtime_prefix_(absl::StrCat("wasm_vm.", short_runtime, ".")), - stats_(VmStats{ALL_VM_STATS(POOL_COUNTER_PREFIX(*scope_, runtime_prefix_), - POOL_GAUGE_PREFIX(*scope_, runtime_prefix_))}) { - stats_.created_.inc(); - stats_.active_.inc(); - ENVOY_LOG(debug, "WasmVm created {} now active", runtime_, stats_.active_.value()); - } - ~EnvoyWasmVmIntegration() override { - stats_.active_.dec(); - ENVOY_LOG(debug, "~WasmVm {} {} remaining active", runtime_, stats_.active_.value()); - } + EnvoyWasmVmIntegration(absl::string_view runtime) : runtime_(std::string(runtime)) {} // proxy_wasm::WasmVmIntegration - proxy_wasm::WasmVmIntegration* clone() override { - return new EnvoyWasmVmIntegration(scope_, runtime_, short_runtime_); - } + proxy_wasm::WasmVmIntegration* clone() override { return new EnvoyWasmVmIntegration(runtime_); } bool getNullVmFunction(absl::string_view function_name, bool returns_word, int number_of_arguments, proxy_wasm::NullPlugin* plugin, void* ptr_to_function_return) override; @@ -61,11 +33,7 @@ class EnvoyWasmVmIntegration : public proxy_wasm::WasmVmIntegration, const std::string& runtime() const { return runtime_; } protected: - const Stats::ScopeSharedPtr scope_; const std::string runtime_; - const std::string short_runtime_; - const std::string runtime_prefix_; - VmStats stats_; }; // namespace Wasm inline EnvoyWasmVmIntegration& getEnvoyWasmIntegration(proxy_wasm::WasmVm& wasm_vm) { @@ -81,7 +49,7 @@ class WasmException : public EnvoyException { using WasmVmPtr = std::unique_ptr; // Create a new low-level Wasm VM using runtime of the given type (e.g. "envoy.wasm.runtime.wavm"). -WasmVmPtr createWasmVm(absl::string_view runtime, const Stats::ScopeSharedPtr& scope); +WasmVmPtr createWasmVm(absl::string_view runtime); } // namespace Wasm } // namespace Common diff --git a/source/extensions/wasm_runtime/null/config.cc b/source/extensions/wasm_runtime/null/config.cc index 3515c9462ce1..e281a24deb74 100644 --- a/source/extensions/wasm_runtime/null/config.cc +++ b/source/extensions/wasm_runtime/null/config.cc @@ -14,7 +14,6 @@ class NullRuntimeFactory : public WasmRuntimeFactory { WasmVmPtr createWasmVm() override { return proxy_wasm::createNullVm(); } absl::string_view name() override { return "envoy.wasm.runtime.null"; } - absl::string_view shortName() override { return "null"; } }; REGISTER_FACTORY(NullRuntimeFactory, WasmRuntimeFactory); diff --git a/source/extensions/wasm_runtime/v8/config.cc b/source/extensions/wasm_runtime/v8/config.cc index 1061b17b2b9d..0aabca1fdd08 100644 --- a/source/extensions/wasm_runtime/v8/config.cc +++ b/source/extensions/wasm_runtime/v8/config.cc @@ -14,7 +14,6 @@ class V8RuntimeFactory : public WasmRuntimeFactory { WasmVmPtr createWasmVm() override { return proxy_wasm::createV8Vm(); } absl::string_view name() override { return "envoy.wasm.runtime.v8"; } - absl::string_view shortName() override { return "v8"; } }; #if defined(ENVOY_WASM_V8) diff --git a/source/extensions/wasm_runtime/wasmtime/config.cc b/source/extensions/wasm_runtime/wasmtime/config.cc index a407d847bdfd..9e25ce8e1548 100644 --- a/source/extensions/wasm_runtime/wasmtime/config.cc +++ b/source/extensions/wasm_runtime/wasmtime/config.cc @@ -14,7 +14,6 @@ class WasmtimeRuntimeFactory : public WasmRuntimeFactory { WasmVmPtr createWasmVm() override { return proxy_wasm::createWasmtimeVm(); } absl::string_view name() override { return "envoy.wasm.runtime.wasmtime"; } - absl::string_view shortName() override { return "wasmtime"; } }; #if defined(ENVOY_WASM_WASMTIME) diff --git a/source/extensions/wasm_runtime/wavm/config.cc b/source/extensions/wasm_runtime/wavm/config.cc index d50119cf784d..be53e31f523b 100644 --- a/source/extensions/wasm_runtime/wavm/config.cc +++ b/source/extensions/wasm_runtime/wavm/config.cc @@ -14,7 +14,6 @@ class WavmRuntimeFactory : public WasmRuntimeFactory { WasmVmPtr createWasmVm() override { return proxy_wasm::createWavmVm(); } absl::string_view name() override { return "envoy.wasm.runtime.wavm"; } - absl::string_view shortName() override { return "wavm"; } }; #if defined(ENVOY_WASM_WAVM) diff --git a/test/extensions/common/wasm/wasm_vm_test.cc b/test/extensions/common/wasm/wasm_vm_test.cc index dd32f0a1ed2b..728d15b07709 100644 --- a/test/extensions/common/wasm/wasm_vm_test.cc +++ b/test/extensions/common/wasm/wasm_vm_test.cc @@ -49,14 +49,12 @@ class BaseVmTest : public testing::Test { Stats::ScopeSharedPtr scope_; }; -TEST_F(BaseVmTest, NoRuntime) { EXPECT_EQ(createWasmVm("", scope_), nullptr); } +TEST_F(BaseVmTest, NoRuntime) { EXPECT_EQ(createWasmVm(""), nullptr); } -TEST_F(BaseVmTest, BadRuntime) { - EXPECT_EQ(createWasmVm("envoy.wasm.runtime.invalid", scope_), nullptr); -} +TEST_F(BaseVmTest, BadRuntime) { EXPECT_EQ(createWasmVm("envoy.wasm.runtime.invalid"), nullptr); } TEST_F(BaseVmTest, NullVmStartup) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.null", scope_); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.null"); EXPECT_TRUE(wasm_vm != nullptr); EXPECT_TRUE(wasm_vm->runtime() == "null"); EXPECT_TRUE(wasm_vm->cloneable() == Cloneable::InstantiatedModule); @@ -70,7 +68,7 @@ TEST_F(BaseVmTest, NullVmStartup) { } TEST_F(BaseVmTest, NullVmMemory) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.null", scope_); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.null"); EXPECT_EQ(wasm_vm->getMemorySize(), std::numeric_limits::max()); std::string d = "data"; auto m = wasm_vm->getMemory(reinterpret_cast(d.data()), d.size()).value(); @@ -137,14 +135,14 @@ class WasmVmTest : public testing::TestWithParam { INSTANTIATE_TEST_SUITE_P(AllowPrecompiled, WasmVmTest, testing::Values(false, true)); TEST_P(WasmVmTest, V8BadCode) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); ASSERT_TRUE(wasm_vm != nullptr); EXPECT_FALSE(wasm_vm->load("bad code", GetParam())); } TEST_P(WasmVmTest, V8Code) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); ASSERT_TRUE(wasm_vm != nullptr); EXPECT_TRUE(wasm_vm->runtime() == "v8"); @@ -163,7 +161,7 @@ TEST_P(WasmVmTest, V8Code) { } TEST_P(WasmVmTest, V8BadHostFunctions) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); ASSERT_TRUE(wasm_vm != nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -184,7 +182,7 @@ TEST_P(WasmVmTest, V8BadHostFunctions) { } TEST_P(WasmVmTest, V8BadModuleFunctions) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); ASSERT_TRUE(wasm_vm != nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -212,7 +210,7 @@ TEST_P(WasmVmTest, V8BadModuleFunctions) { } TEST_P(WasmVmTest, V8FunctionCalls) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); ASSERT_TRUE(wasm_vm != nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -250,7 +248,7 @@ TEST_P(WasmVmTest, V8FunctionCalls) { } TEST_P(WasmVmTest, V8Memory) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); ASSERT_TRUE(wasm_vm != nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( From 33bd2ea79002e969cb094e3637791bda936b7829 Mon Sep 17 00:00:00 2001 From: Kuat Date: Tue, 15 Dec 2020 02:10:27 -0800 Subject: [PATCH 30/49] wasm: dead code (#14407) Signed-off-by: Kuat Yessenov --- source/extensions/common/wasm/wasm_vm_base.h | 55 -------------------- 1 file changed, 55 deletions(-) delete mode 100644 source/extensions/common/wasm/wasm_vm_base.h diff --git a/source/extensions/common/wasm/wasm_vm_base.h b/source/extensions/common/wasm/wasm_vm_base.h deleted file mode 100644 index a780af5c8dcc..000000000000 --- a/source/extensions/common/wasm/wasm_vm_base.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "envoy/stats/scope.h" -#include "envoy/stats/stats.h" -#include "envoy/stats/stats_macros.h" - -#include "extensions/common/wasm/wasm_vm.h" - -#include "absl/strings/str_cat.h" - -namespace Envoy { -namespace Extensions { -namespace Common { -namespace Wasm { - -/** - * Wasm host stats. - */ -#define ALL_VM_STATS(COUNTER, GAUGE) \ - COUNTER(created) \ - COUNTER(cloned) \ - GAUGE(active, NeverImport) - -struct VmStats { - ALL_VM_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT) -}; - -// Wasm VM base instance. Provides common behavior (e.g. Stats). -class WasmVmBase : public WasmVm { -public: - WasmVmBase(const Stats::ScopeSharedPtr& scope, absl::string_view runtime) - : scope_(scope), runtime_prefix_(absl::StrCat("wasm_vm.", runtime, ".")), - runtime_(std::string(runtime)), - stats_(VmStats{ALL_VM_STATS(POOL_COUNTER_PREFIX(*scope_, runtime_prefix_), - POOL_GAUGE_PREFIX(*scope_, runtime_prefix_))}) { - stats_.created_.inc(); - stats_.active_.inc(); - ENVOY_LOG(debug, "WasmVm created {} now active", runtime_, stats_.active_.value()); - } - ~WasmVmBase() override { - stats_.active_.dec(); - ENVOY_LOG(debug, "~WasmVm {} {} remaining active", runtime_, stats_.active_.value()); - } - -protected: - const Stats::ScopeSharedPtr scope_; - const std::string runtime_prefix_; - const std::string runtime_; // The runtime e.g. "v8". - VmStats stats_; -}; - -} // namespace Wasm -} // namespace Common -} // namespace Extensions -} // namespace Envoy From ff3ef1294a73d36b16a1057b9aaf1d297b7c3090 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 15 Dec 2020 08:36:31 -0500 Subject: [PATCH 31/49] config: making protocol config explicit (#14362) Commit Message: Making the recently added ProtocolOptionsConfig require explicit configuration Risk Level: Medium (config breaking, for config which is 7 days old) Testing: n/a Docs Changes: inline Release Notes: n/a Signed-off-by: Alyssa Wilk --- .../http/v3/http_protocol_options.proto | 9 ++++++--- .../http/v4alpha/http_protocol_options.proto | 9 ++++++--- .../http/v3/http_protocol_options.proto | 9 ++++++--- .../http/v4alpha/http_protocol_options.proto | 9 ++++++--- source/extensions/upstreams/http/config.h | 13 ++++++++----- test/common/upstream/upstream_impl_test.cc | 17 +++++++++++++++++ test/integration/base_integration_test.cc | 8 ++++++++ test/integration/http2_integration_test.cc | 1 + test/integration/tcp_proxy_integration_test.cc | 1 + .../tcp_tunneling_integration_test.cc | 1 + 10 files changed, 60 insertions(+), 17 deletions(-) diff --git a/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto b/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto index d3cd59bbc26f..7c5dce7854ee 100644 --- a/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto +++ b/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto @@ -5,6 +5,7 @@ package envoy.extensions.upstreams.http.v3; import "envoy/config/core/v3/protocol.proto"; import "udpa/annotations/status.proto"; +import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.v3"; option java_outer_classname = "HttpProtocolOptionsProto"; @@ -57,10 +58,11 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // .... [further cluster config] message HttpProtocolOptions { // If this is used, the cluster will only operate on one of the possible upstream protocols (HTTP/1.1, HTTP/2). - // If :ref:`http2_protocol_options ` are - // present, HTTP2 will be used, otherwise HTTP1.1 will be used. + // Note that HTTP/2 should generally be used for upstream clusters doing gRPC. message ExplicitHttpConfig { oneof protocol_config { + option (validate.required) = true; + config.core.v3.Http1ProtocolOptions http_protocol_options = 1; config.core.v3.Http2ProtocolOptions http2_protocol_options = 2; @@ -82,8 +84,9 @@ message HttpProtocolOptions { config.core.v3.UpstreamHttpProtocolOptions upstream_http_protocol_options = 2; // This controls the actual protocol to be used upstream. - // If none of the *upstream_protocol_options* are chosen, the default is *explicit_http_config*. oneof upstream_protocol_options { + option (validate.required) = true; + // To explicitly configure either HTTP/1 or HTTP/2 (but not both!) use *explicit_http_config*. // If the *explicit_http_config* is empty, HTTP/1.1 is used. ExplicitHttpConfig explicit_http_config = 3; diff --git a/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto b/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto index 3b18b128c412..4c97b8a69f8f 100644 --- a/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto +++ b/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto @@ -6,6 +6,7 @@ import "envoy/config/core/v4alpha/protocol.proto"; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.v4alpha"; option java_outer_classname = "HttpProtocolOptionsProto"; @@ -61,13 +62,14 @@ message HttpProtocolOptions { "envoy.extensions.upstreams.http.v3.HttpProtocolOptions"; // If this is used, the cluster will only operate on one of the possible upstream protocols (HTTP/1.1, HTTP/2). - // If :ref:`http2_protocol_options ` are - // present, HTTP2 will be used, otherwise HTTP1.1 will be used. + // Note that HTTP/2 should generally be used for upstream clusters doing gRPC. message ExplicitHttpConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.upstreams.http.v3.HttpProtocolOptions.ExplicitHttpConfig"; oneof protocol_config { + option (validate.required) = true; + config.core.v4alpha.Http1ProtocolOptions http_protocol_options = 1; config.core.v4alpha.Http2ProtocolOptions http2_protocol_options = 2; @@ -92,8 +94,9 @@ message HttpProtocolOptions { config.core.v4alpha.UpstreamHttpProtocolOptions upstream_http_protocol_options = 2; // This controls the actual protocol to be used upstream. - // If none of the *upstream_protocol_options* are chosen, the default is *explicit_http_config*. oneof upstream_protocol_options { + option (validate.required) = true; + // To explicitly configure either HTTP/1 or HTTP/2 (but not both!) use *explicit_http_config*. // If the *explicit_http_config* is empty, HTTP/1.1 is used. ExplicitHttpConfig explicit_http_config = 3; diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto b/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto index d3cd59bbc26f..7c5dce7854ee 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto @@ -5,6 +5,7 @@ package envoy.extensions.upstreams.http.v3; import "envoy/config/core/v3/protocol.proto"; import "udpa/annotations/status.proto"; +import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.v3"; option java_outer_classname = "HttpProtocolOptionsProto"; @@ -57,10 +58,11 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // .... [further cluster config] message HttpProtocolOptions { // If this is used, the cluster will only operate on one of the possible upstream protocols (HTTP/1.1, HTTP/2). - // If :ref:`http2_protocol_options ` are - // present, HTTP2 will be used, otherwise HTTP1.1 will be used. + // Note that HTTP/2 should generally be used for upstream clusters doing gRPC. message ExplicitHttpConfig { oneof protocol_config { + option (validate.required) = true; + config.core.v3.Http1ProtocolOptions http_protocol_options = 1; config.core.v3.Http2ProtocolOptions http2_protocol_options = 2; @@ -82,8 +84,9 @@ message HttpProtocolOptions { config.core.v3.UpstreamHttpProtocolOptions upstream_http_protocol_options = 2; // This controls the actual protocol to be used upstream. - // If none of the *upstream_protocol_options* are chosen, the default is *explicit_http_config*. oneof upstream_protocol_options { + option (validate.required) = true; + // To explicitly configure either HTTP/1 or HTTP/2 (but not both!) use *explicit_http_config*. // If the *explicit_http_config* is empty, HTTP/1.1 is used. ExplicitHttpConfig explicit_http_config = 3; diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto b/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto index 3b18b128c412..4c97b8a69f8f 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto @@ -6,6 +6,7 @@ import "envoy/config/core/v4alpha/protocol.proto"; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.v4alpha"; option java_outer_classname = "HttpProtocolOptionsProto"; @@ -61,13 +62,14 @@ message HttpProtocolOptions { "envoy.extensions.upstreams.http.v3.HttpProtocolOptions"; // If this is used, the cluster will only operate on one of the possible upstream protocols (HTTP/1.1, HTTP/2). - // If :ref:`http2_protocol_options ` are - // present, HTTP2 will be used, otherwise HTTP1.1 will be used. + // Note that HTTP/2 should generally be used for upstream clusters doing gRPC. message ExplicitHttpConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.upstreams.http.v3.HttpProtocolOptions.ExplicitHttpConfig"; oneof protocol_config { + option (validate.required) = true; + config.core.v4alpha.Http1ProtocolOptions http_protocol_options = 1; config.core.v4alpha.Http2ProtocolOptions http2_protocol_options = 2; @@ -92,8 +94,9 @@ message HttpProtocolOptions { config.core.v4alpha.UpstreamHttpProtocolOptions upstream_http_protocol_options = 2; // This controls the actual protocol to be used upstream. - // If none of the *upstream_protocol_options* are chosen, the default is *explicit_http_config*. oneof upstream_protocol_options { + option (validate.required) = true; + // To explicitly configure either HTTP/1 or HTTP/2 (but not both!) use *explicit_http_config*. // If the *explicit_http_config* is empty, HTTP/1.1 is used. ExplicitHttpConfig explicit_http_config = 3; diff --git a/source/extensions/upstreams/http/config.h b/source/extensions/upstreams/http/config.h index f68ffe73f834..e55cb18117f0 100644 --- a/source/extensions/upstreams/http/config.h +++ b/source/extensions/upstreams/http/config.h @@ -12,8 +12,10 @@ #include "envoy/extensions/upstreams/http/v3/http_protocol_options.pb.validate.h" #include "envoy/http/filter.h" #include "envoy/server/filter_config.h" +#include "envoy/server/transport_socket_config.h" #include "common/common/logger.h" +#include "common/protobuf/message_validator_impl.h" namespace Envoy { namespace Extensions { @@ -44,11 +46,12 @@ class ProtocolOptionsConfigImpl : public Upstream::ProtocolOptionsConfig { class ProtocolOptionsConfigFactory : public Server::Configuration::ProtocolOptionsFactory { public: - Upstream::ProtocolOptionsConfigConstSharedPtr - createProtocolOptionsConfig(const Protobuf::Message& config, - Server::Configuration::ProtocolOptionsFactoryContext&) override { - const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& typed_config = - *dynamic_cast(&config); + Upstream::ProtocolOptionsConfigConstSharedPtr createProtocolOptionsConfig( + const Protobuf::Message& config, + Server::Configuration::ProtocolOptionsFactoryContext& context) override { + const auto& typed_config = MessageUtil::downcastAndValidate< + const envoy::extensions::upstreams::http::v3::HttpProtocolOptions&>( + config, context.messageValidationVisitor()); return std::make_shared(typed_config); } std::string category() const override { return "envoy.upstream_options"; } diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index e565609d214c..f52a5cdb5250 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -2561,6 +2561,16 @@ TEST_F(ClusterInfoImplTest, Timeouts) { )EOF"; const std::string explicit_timeout_new = R"EOF( + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http_protocol_options: {} + common_http_protocol_options: + idle_timeout: 1s + )EOF"; + + const std::string explicit_timeout_bad = R"EOF( typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions @@ -2578,6 +2588,11 @@ TEST_F(ClusterInfoImplTest, Timeouts) { ASSERT_TRUE(cluster2->info()->idleTimeout().has_value()); EXPECT_EQ(std::chrono::seconds(1), cluster2->info()->idleTimeout().value()); } + { + auto cluster2 = makeCluster(yaml + explicit_timeout_new); + EXPECT_THROW_WITH_REGEX(makeCluster(yaml + explicit_timeout_bad, false), EnvoyException, + ".*Proto constraint validation failed.*"); + } const std::string no_timeout = R"EOF( common_http_protocol_options: idle_timeout: 0s @@ -2587,6 +2602,8 @@ TEST_F(ClusterInfoImplTest, Timeouts) { typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http_protocol_options: {} common_http_protocol_options: idle_timeout: 0s )EOF"; diff --git a/test/integration/base_integration_test.cc b/test/integration/base_integration_test.cc index 647faae59874..45a7019ed9ac 100644 --- a/test/integration/base_integration_test.cc +++ b/test/integration/base_integration_test.cc @@ -218,6 +218,14 @@ void BaseIntegrationTest::setUpstreamProtocol(FakeHttpConnection::Type protocol) }); } else { RELEASE_ASSERT(protocol == FakeHttpConnection::Type::HTTP1, ""); + config_helper_.addConfigModifier( + [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() >= 1, ""); + ConfigHelper::HttpProtocolOptions protocol_options; + protocol_options.mutable_explicit_http_config()->mutable_http_protocol_options(); + ConfigHelper::setProtocolOptions( + *bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); + }); } } diff --git a/test/integration/http2_integration_test.cc b/test/integration/http2_integration_test.cc index ef311a7b6d14..a0e73bdbde68 100644 --- a/test/integration/http2_integration_test.cc +++ b/test/integration/http2_integration_test.cc @@ -957,6 +957,7 @@ TEST_P(Http2IntegrationTest, IdleTimeoutWithSimultaneousRequests) { config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { ConfigHelper::HttpProtocolOptions protocol_options; auto* http_protocol_options = protocol_options.mutable_common_http_protocol_options(); + protocol_options.mutable_explicit_http_config()->mutable_http_protocol_options(); auto* idle_time_out = http_protocol_options->mutable_idle_timeout(); std::chrono::milliseconds timeout(1000); auto seconds = std::chrono::duration_cast(timeout); diff --git a/test/integration/tcp_proxy_integration_test.cc b/test/integration/tcp_proxy_integration_test.cc index 8d9857e705db..9682480725ba 100644 --- a/test/integration/tcp_proxy_integration_test.cc +++ b/test/integration/tcp_proxy_integration_test.cc @@ -96,6 +96,7 @@ TEST_P(TcpProxyIntegrationTest, TcpProxyUpstreamWritesFirst) { // Test TLS upstream. TEST_P(TcpProxyIntegrationTest, TcpProxyUpstreamTls) { upstream_tls_ = true; + setUpstreamProtocol(FakeHttpConnection::Type::HTTP1); config_helper_.configureUpstreamTls(); initialize(); IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); diff --git a/test/integration/tcp_tunneling_integration_test.cc b/test/integration/tcp_tunneling_integration_test.cc index d17fc468287f..f8b5dd8273b2 100644 --- a/test/integration/tcp_tunneling_integration_test.cc +++ b/test/integration/tcp_tunneling_integration_test.cc @@ -186,6 +186,7 @@ TEST_P(ConnectTerminationIntegrationTest, BuggyHeaders) { } TEST_P(ConnectTerminationIntegrationTest, BasicMaxStreamDuration) { + setUpstreamProtocol(upstreamProtocol()); config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { ConfigHelper::HttpProtocolOptions protocol_options; protocol_options.mutable_common_http_protocol_options() From 7d348d1b65d3c5ae58d0259e397fbd3df8686f11 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 15 Dec 2020 11:45:19 -0500 Subject: [PATCH 32/49] tcp: improved unit testing (#14415) Signed-off-by: Alyssa Wilk --- test/common/tcp/conn_pool_test.cc | 358 +++++++++++++++++------------- 1 file changed, 204 insertions(+), 154 deletions(-) diff --git a/test/common/tcp/conn_pool_test.cc b/test/common/tcp/conn_pool_test.cc index 1182d9c6d5e4..df1b5a40e0ef 100644 --- a/test/common/tcp/conn_pool_test.cc +++ b/test/common/tcp/conn_pool_test.cc @@ -2,6 +2,7 @@ #include #include "common/event/dispatcher_impl.h" +#include "common/network/transport_socket_options_impl.h" #include "common/network/utility.h" #include "common/tcp/conn_pool.h" #include "common/tcp/original_conn_pool.h" @@ -95,6 +96,8 @@ class ConnPoolBase : public Tcp::ConnectionPool::Instance { public: ConnPoolBase(Event::MockDispatcher& dispatcher, Upstream::HostSharedPtr host, NiceMock* upstream_ready_cb, + Network::ConnectionSocket::OptionsSharedPtr options, + Network::TransportSocketOptionsSharedPtr transport_socket_options, bool test_new_connection_pool); void addDrainedCallback(DrainedCb cb) override { conn_pool_->addDrainedCallback(cb); } @@ -127,7 +130,7 @@ class ConnPoolBase : public Tcp::ConnectionPool::Instance { test_conn.connection_ = new NiceMock(); test_conn.connect_timer_ = new NiceMock(&mock_dispatcher_); - EXPECT_CALL(mock_dispatcher_, createClientConnection_(_, _, _, _)) + EXPECT_CALL(mock_dispatcher_, createClientConnection_(_, _, _, options_)) .WillOnce(Return(test_conn.connection_)); EXPECT_CALL(*test_conn.connection_, addReadFilter(_)) .WillOnce(Invoke( @@ -149,14 +152,18 @@ class ConnPoolBase : public Tcp::ConnectionPool::Instance { std::vector test_conns_; Network::ConnectionCallbacks* callbacks_ = nullptr; bool test_new_connection_pool_; + Network::ConnectionSocket::OptionsSharedPtr options_; + Network::TransportSocketOptionsSharedPtr transport_socket_options_; protected: class ConnPoolImplForTest : public ConnPoolImpl { public: ConnPoolImplForTest(Event::MockDispatcher& dispatcher, Upstream::HostSharedPtr host, + Network::ConnectionSocket::OptionsSharedPtr options, + Network::TransportSocketOptionsSharedPtr transport_socket_options, ConnPoolBase& parent) - : ConnPoolImpl(dispatcher, host, Upstream::ResourcePriority::Default, nullptr, nullptr, - state_), + : ConnPoolImpl(dispatcher, host, Upstream::ResourcePriority::Default, options, + transport_socket_options, state_), parent_(parent) {} void onConnReleased(Envoy::ConnectionPool::ActiveClient& client) override { @@ -179,9 +186,11 @@ class ConnPoolBase : public Tcp::ConnectionPool::Instance { class OriginalConnPoolImplForTest : public OriginalConnPoolImpl { public: OriginalConnPoolImplForTest(Event::MockDispatcher& dispatcher, Upstream::HostSharedPtr host, + Network::ConnectionSocket::OptionsSharedPtr options, + Network::TransportSocketOptionsSharedPtr transport_socket_options, ConnPoolBase& parent) - : OriginalConnPoolImpl(dispatcher, host, Upstream::ResourcePriority::Default, nullptr, - nullptr), + : OriginalConnPoolImpl(dispatcher, host, Upstream::ResourcePriority::Default, options, + transport_socket_options), parent_(parent) {} ~OriginalConnPoolImplForTest() override { @@ -217,13 +226,18 @@ class ConnPoolBase : public Tcp::ConnectionPool::Instance { ConnPoolBase::ConnPoolBase(Event::MockDispatcher& dispatcher, Upstream::HostSharedPtr host, NiceMock* upstream_ready_cb, + Network::ConnectionSocket::OptionsSharedPtr options, + Network::TransportSocketOptionsSharedPtr transport_socket_options, bool test_new_connection_pool) : mock_dispatcher_(dispatcher), mock_upstream_ready_cb_(upstream_ready_cb), - test_new_connection_pool_(test_new_connection_pool) { + test_new_connection_pool_(test_new_connection_pool), options_(options), + transport_socket_options_(transport_socket_options) { if (test_new_connection_pool_) { - conn_pool_ = std::make_unique(dispatcher, host, *this); + conn_pool_ = std::make_unique(dispatcher, host, options, + transport_socket_options, *this); } else { - conn_pool_ = std::make_unique(dispatcher, host, *this); + conn_pool_ = std::make_unique(dispatcher, host, options, + transport_socket_options, *this); } } @@ -250,20 +264,27 @@ class TcpConnPoolImplTest : public Event::TestUsingSimulatedTime, TcpConnPoolImplTest() : test_new_connection_pool_(GetParam()), upstream_ready_cb_(new NiceMock(&dispatcher_)), - host_(Upstream::makeTestHost(cluster_, "tcp://127.0.0.1:9000", simTime())), - conn_pool_(dispatcher_, host_, upstream_ready_cb_, test_new_connection_pool_) {} + host_(Upstream::makeTestHost(cluster_, "tcp://127.0.0.1:9000", simTime())) {} ~TcpConnPoolImplTest() override { EXPECT_TRUE(TestUtility::gaugesZeroed(cluster_->stats_store_.gauges())) << TestUtility::nonZeroedGauges(cluster_->stats_store_.gauges()); } + void initialize() { + conn_pool_ = + std::make_unique(dispatcher_, host_, upstream_ready_cb_, options_, + transport_socket_options_, test_new_connection_pool_); + } + bool test_new_connection_pool_; NiceMock dispatcher_; std::shared_ptr cluster_{new NiceMock()}; NiceMock* upstream_ready_cb_; Upstream::HostSharedPtr host_; - ConnPoolBase conn_pool_; + Network::ConnectionSocket::OptionsSharedPtr options_; + Network::TransportSocketOptionsSharedPtr transport_socket_options_; + std::unique_ptr conn_pool_; NiceMock runtime_; }; @@ -332,13 +353,13 @@ struct ActiveTestConn { ActiveTestConn(TcpConnPoolImplTest& parent, size_t conn_index, Type type) : parent_(parent), conn_index_(conn_index) { if (type == Type::CreateConnection || type == Type::InProgress) { - parent.conn_pool_.expectConnCreate(); + parent.conn_pool_->expectConnCreate(); } if (type == Type::Immediate) { expectNewConn(); } - handle_ = parent.conn_pool_.newConnection(callbacks_); + handle_ = parent.conn_pool_->newConnection(callbacks_); if (type == Type::Immediate) { EXPECT_EQ(nullptr, handle_); @@ -355,9 +376,9 @@ struct ActiveTestConn { void completeConnection() { ASSERT_FALSE(completed_); - EXPECT_CALL(*parent_.conn_pool_.test_conns_[conn_index_].connect_timer_, disableTimer()); + EXPECT_CALL(*parent_.conn_pool_->test_conns_[conn_index_].connect_timer_, disableTimer()); expectNewConn(); - parent_.conn_pool_.test_conns_[conn_index_].connection_->raiseEvent( + parent_.conn_pool_->test_conns_[conn_index_].connection_->raiseEvent( Network::ConnectionEvent::Connected); verifyConn(); completed_ = true; @@ -369,7 +390,7 @@ struct ActiveTestConn { void verifyConn() { EXPECT_EQ(&callbacks_.conn_data_->connection(), - parent_.conn_pool_.test_conns_[conn_index_].connection_); + parent_.conn_pool_->test_conns_[conn_index_].connection_); } TcpConnPoolImplTest& parent_; @@ -379,32 +400,36 @@ struct ActiveTestConn { bool completed_{}; }; -TEST_P(TcpConnPoolImplTest, HostAccessor) { EXPECT_EQ(conn_pool_.host(), host_); } +TEST_P(TcpConnPoolImplTest, Accessors) { + initialize(); + EXPECT_EQ(conn_pool_->host(), host_); +} /** * Verify that connections are drained when requested. */ TEST_P(TcpConnPoolImplTest, DrainConnections) { + initialize(); cluster_->resetResourceManager(3, 1024, 1024, 1, 1); ActiveTestConn c1(*this, 0, ActiveTestConn::Type::CreateConnection); ActiveTestConn c2(*this, 1, ActiveTestConn::Type::CreateConnection); ActiveTestConn c3(*this, 2, ActiveTestConn::Type::InProgress); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); c1.releaseConn(); { // This will destroy the ready connection and set requests remaining to 1 on the busy and // pending connections. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.drainConnections(); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->drainConnections(); dispatcher_.clearDeferredDeleteList(); } { // This will destroy the busy connection when the response finishes. - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); c2.releaseConn(); dispatcher_.clearDeferredDeleteList(); } @@ -412,8 +437,8 @@ TEST_P(TcpConnPoolImplTest, DrainConnections) { // This will destroy the pending connection when the response finishes. c3.completeConnection(); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); c3.releaseConn(); dispatcher_.clearDeferredDeleteList(); } @@ -423,6 +448,7 @@ TEST_P(TcpConnPoolImplTest, DrainConnections) { * Test all timing stats are set. */ TEST_P(TcpConnPoolImplTest, VerifyTimingStats) { + initialize(); EXPECT_CALL(cluster_->stats_store_, deliverHistogramToSinks(Property(&Stats::Metric::name, "upstream_cx_connect_ms"), _)); EXPECT_CALL(cluster_->stats_store_, @@ -430,30 +456,34 @@ TEST_P(TcpConnPoolImplTest, VerifyTimingStats) { ActiveTestConn c1(*this, 0, ActiveTestConn::Type::CreateConnection); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); c1.releaseConn(); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); dispatcher_.clearDeferredDeleteList(); } /** - * Test that buffer limits are set. + * Test that buffer limits and options are respected. */ -TEST_P(TcpConnPoolImplTest, VerifyBufferLimits) { +TEST_P(TcpConnPoolImplTest, VerifyBufferLimitsAndOptions) { + options_ = std::make_shared(); + transport_socket_options_ = std::make_shared(); + + initialize(); ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); + conn_pool_->expectConnCreate(); EXPECT_CALL(*cluster_, perConnectionBufferLimitBytes()).WillOnce(Return(8192)); - EXPECT_CALL(*conn_pool_.test_conns_.back().connection_, setBufferLimits(8192)); + EXPECT_CALL(*conn_pool_->test_conns_.back().connection_, setBufferLimits(8192)); EXPECT_CALL(callbacks.pool_failure_, ready()); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -461,6 +491,7 @@ TEST_P(TcpConnPoolImplTest, VerifyBufferLimits) { * Test that upstream callback fire for assigned connections. */ TEST_P(TcpConnPoolImplTest, UpstreamCallbacks) { + initialize(); Buffer::OwnedImpl buffer; // Create connection, UpstreamCallbacks set automatically @@ -469,24 +500,24 @@ TEST_P(TcpConnPoolImplTest, UpstreamCallbacks) { // Expect invocation when connection's ReadFilter::onData is invoked EXPECT_CALL(c1.callbacks_.callbacks_, onUpstreamData(_, _)); EXPECT_EQ(Network::FilterStatus::StopIteration, - conn_pool_.test_conns_[0].filter_->onData(buffer, false)); + conn_pool_->test_conns_[0].filter_->onData(buffer, false)); EXPECT_CALL(c1.callbacks_.callbacks_, onAboveWriteBufferHighWatermark()); - for (auto* cb : conn_pool_.test_conns_[0].connection_->callbacks_) { + for (auto* cb : conn_pool_->test_conns_[0].connection_->callbacks_) { cb->onAboveWriteBufferHighWatermark(); } EXPECT_CALL(c1.callbacks_.callbacks_, onBelowWriteBufferLowWatermark()); - for (auto* cb : conn_pool_.test_conns_[0].connection_->callbacks_) { + for (auto* cb : conn_pool_->test_conns_[0].connection_->callbacks_) { cb->onBelowWriteBufferLowWatermark(); } // Shutdown normally. - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); c1.releaseConn(); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -495,22 +526,23 @@ TEST_P(TcpConnPoolImplTest, UpstreamCallbacks) { * the same connection. */ TEST_P(TcpConnPoolImplTest, MultipleRequestAndResponse) { + initialize(); // Request 1 should kick off a new connection. ActiveTestConn c1(*this, 0, ActiveTestConn::Type::CreateConnection); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); c1.releaseConn(); // Request 2 should not. ActiveTestConn c2(*this, 0, ActiveTestConn::Type::Immediate); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); c2.releaseConn(); // Cause the connection to go away. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -518,6 +550,7 @@ TEST_P(TcpConnPoolImplTest, MultipleRequestAndResponse) { * Tests ConnectionState assignment, lookup and destruction. */ TEST_P(TcpConnPoolImplTest, ConnectionStateLifecycle) { + initialize(); bool state_destroyed = false; @@ -529,7 +562,7 @@ TEST_P(TcpConnPoolImplTest, ConnectionStateLifecycle) { EXPECT_EQ(state, c1.callbacks_.conn_data_->connectionStateTyped()); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); c1.releaseConn(); EXPECT_FALSE(state_destroyed); @@ -539,14 +572,14 @@ TEST_P(TcpConnPoolImplTest, ConnectionStateLifecycle) { EXPECT_EQ(state, c2.callbacks_.conn_data_->connectionStateTyped()); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); c2.releaseConn(); EXPECT_FALSE(state_destroyed); // Cause the connection to go away. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); EXPECT_TRUE(state_destroyed); @@ -556,22 +589,23 @@ TEST_P(TcpConnPoolImplTest, ConnectionStateLifecycle) { * Test when we overflow max pending requests. */ TEST_P(TcpConnPoolImplTest, MaxPendingRequests) { + initialize(); cluster_->resetResourceManager(1, 1, 1024, 1, 1); ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); ConnPoolCallbacks callbacks2; EXPECT_CALL(callbacks2.pool_failure_, ready()); - Tcp::ConnectionPool::Cancellable* handle2 = conn_pool_.newConnection(callbacks2); + Tcp::ConnectionPool::Cancellable* handle2 = conn_pool_->newConnection(callbacks2); EXPECT_EQ(nullptr, handle2); handle->cancel(ConnectionPool::CancelPolicy::Default); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); EXPECT_EQ(ConnectionPool::PoolFailureReason::Overflow, callbacks2.reason_); @@ -584,18 +618,19 @@ TEST_P(TcpConnPoolImplTest, MaxPendingRequests) { * getting purged. */ TEST_P(TcpConnPoolImplTest, RemoteConnectFailure) { + initialize(); // Request 1 should kick off a new connection. ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); EXPECT_CALL(callbacks.pool_failure_, ready()); - EXPECT_CALL(*conn_pool_.test_conns_[0].connect_timer_, disableTimer()); + EXPECT_CALL(*conn_pool_->test_conns_[0].connect_timer_, disableTimer()); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); EXPECT_EQ(ConnectionPool::PoolFailureReason::RemoteConnectionFailure, callbacks.reason_); @@ -609,18 +644,19 @@ TEST_P(TcpConnPoolImplTest, RemoteConnectFailure) { * getting purged. */ TEST_P(TcpConnPoolImplTest, LocalConnectFailure) { + initialize(); // Request 1 should kick off a new connection. ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); EXPECT_CALL(callbacks.pool_failure_, ready()); - EXPECT_CALL(*conn_pool_.test_conns_[0].connect_timer_, disableTimer()); + EXPECT_CALL(*conn_pool_->test_conns_[0].connect_timer_, disableTimer()); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::LocalClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::LocalClose); dispatcher_.clearDeferredDeleteList(); EXPECT_EQ(ConnectionPool::PoolFailureReason::LocalConnectionFailure, callbacks.reason_); @@ -633,24 +669,25 @@ TEST_P(TcpConnPoolImplTest, LocalConnectFailure) { * Tests a connect timeout. Also test that we can add a new request during ejection processing. */ TEST_P(TcpConnPoolImplTest, ConnectTimeout) { + initialize(); // Request 1 should kick off a new connection. ConnPoolCallbacks callbacks1; - conn_pool_.expectConnCreate(); - EXPECT_NE(nullptr, conn_pool_.newConnection(callbacks1)); + conn_pool_->expectConnCreate(); + EXPECT_NE(nullptr, conn_pool_->newConnection(callbacks1)); ConnPoolCallbacks callbacks2; EXPECT_CALL(callbacks1.pool_failure_, ready()).WillOnce(Invoke([&]() -> void { - conn_pool_.expectConnCreate(); - EXPECT_NE(nullptr, conn_pool_.newConnection(callbacks2)); + conn_pool_->expectConnCreate(); + EXPECT_NE(nullptr, conn_pool_->newConnection(callbacks2)); })); - conn_pool_.test_conns_[0].connect_timer_->invokeCallback(); + conn_pool_->test_conns_[0].connect_timer_->invokeCallback(); EXPECT_CALL(callbacks2.pool_failure_, ready()); - conn_pool_.test_conns_[1].connect_timer_->invokeCallback(); + conn_pool_->test_conns_[1].connect_timer_->invokeCallback(); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()).Times(2); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()).Times(2); dispatcher_.clearDeferredDeleteList(); EXPECT_EQ(ConnectionPool::PoolFailureReason::Timeout, callbacks1.reason_); @@ -664,19 +701,20 @@ TEST_P(TcpConnPoolImplTest, ConnectTimeout) { * Test cancelling before the request is bound to a connection. */ TEST_P(TcpConnPoolImplTest, CancelBeforeBound) { + initialize(); // Request 1 should kick off a new connection. ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); handle->cancel(ConnectionPool::CancelPolicy::Default); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); // Cause the connection to go away. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -684,15 +722,16 @@ TEST_P(TcpConnPoolImplTest, CancelBeforeBound) { * Test cancelling before the request is bound to a connection, with connection close. */ TEST_P(TcpConnPoolImplTest, CancelAndCloseBeforeBound) { + initialize(); // Request 1 should kick off a new connection. ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); // Expect the connection is closed. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); handle->cancel(ConnectionPool::CancelPolicy::CloseExcess); dispatcher_.clearDeferredDeleteList(); @@ -702,20 +741,21 @@ TEST_P(TcpConnPoolImplTest, CancelAndCloseBeforeBound) { * Test an upstream disconnection while there is a bound request. */ TEST_P(TcpConnPoolImplTest, DisconnectWhileBound) { + initialize(); // Request 1 should kick off a new connection. ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); EXPECT_CALL(callbacks.pool_ready_, ready()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); // Kill the connection while it has an active request. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -723,41 +763,42 @@ TEST_P(TcpConnPoolImplTest, DisconnectWhileBound) { * Test upstream disconnection of one request while another is pending. */ TEST_P(TcpConnPoolImplTest, DisconnectWhilePending) { + initialize(); cluster_->resetResourceManager(1, 1024, 1024, 1, 1); // First request connected. ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); - EXPECT_CALL(*conn_pool_.test_conns_[0].connect_timer_, disableTimer()); + EXPECT_CALL(*conn_pool_->test_conns_[0].connect_timer_, disableTimer()); EXPECT_CALL(callbacks.pool_ready_, ready()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); // Second request pending. ConnPoolCallbacks callbacks2; - ConnectionPool::Cancellable* handle2 = conn_pool_.newConnection(callbacks2); + ConnectionPool::Cancellable* handle2 = conn_pool_->newConnection(callbacks2); EXPECT_NE(nullptr, handle2); // Connection closed, triggering new connection for pending request. - conn_pool_.expectConnCreate(); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::LocalClose); + conn_pool_->expectConnCreate(); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::LocalClose); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); dispatcher_.clearDeferredDeleteList(); // test_conns_[1] is the new connection - EXPECT_CALL(*conn_pool_.test_conns_[1].connect_timer_, disableTimer()); + EXPECT_CALL(*conn_pool_->test_conns_[1].connect_timer_, disableTimer()); EXPECT_CALL(callbacks2.pool_ready_, ready()); - conn_pool_.test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::Connected); + conn_pool_->test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::Connected); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); callbacks2.conn_data_.reset(); // Disconnect - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -765,37 +806,38 @@ TEST_P(TcpConnPoolImplTest, DisconnectWhilePending) { * Test that we correctly handle reaching max connections. */ TEST_P(TcpConnPoolImplTest, MaxConnections) { + initialize(); // Request 1 should kick off a new connection. ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); // Request 2 should not kick off a new connection. ConnPoolCallbacks callbacks2; - handle = conn_pool_.newConnection(callbacks2); + handle = conn_pool_->newConnection(callbacks2); EXPECT_EQ(1U, cluster_->stats_.upstream_cx_overflow_.value()); EXPECT_NE(nullptr, handle); // Connect event will bind to request 1. EXPECT_CALL(callbacks.pool_ready_, ready()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); // Finishing request 1 will immediately bind to request 2. - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); - conn_pool_.expectEnableUpstreamReady(false); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); + conn_pool_->expectEnableUpstreamReady(false); EXPECT_CALL(callbacks2.pool_ready_, ready()); callbacks.conn_data_.reset(); - conn_pool_.expectEnableUpstreamReady(true); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + conn_pool_->expectEnableUpstreamReady(true); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); callbacks2.conn_data_.reset(); // Cause the connection to go away. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -803,21 +845,22 @@ TEST_P(TcpConnPoolImplTest, MaxConnections) { * Test when we reach max requests per connection. */ TEST_P(TcpConnPoolImplTest, MaxRequestsPerConnection) { + initialize(); cluster_->max_requests_per_connection_ = 1; // Request 1 should kick off a new connection. ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); EXPECT_CALL(callbacks.pool_ready_, ready()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); callbacks.conn_data_.reset(); dispatcher_.clearDeferredDeleteList(); @@ -829,6 +872,7 @@ TEST_P(TcpConnPoolImplTest, MaxRequestsPerConnection) { * Test that multiple connections can be assigned at once. */ TEST_P(TcpConnPoolImplTest, ConcurrentConnections) { + initialize(); cluster_->resetResourceManager(2, 1024, 1024, 1, 1); ActiveTestConn c1(*this, 0, ActiveTestConn::Type::CreateConnection); @@ -836,20 +880,20 @@ TEST_P(TcpConnPoolImplTest, ConcurrentConnections) { ActiveTestConn c3(*this, 0, ActiveTestConn::Type::Pending); // Finish c1, which gets c3 going. - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); - conn_pool_.expectEnableUpstreamReady(false); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); + conn_pool_->expectEnableUpstreamReady(false); c3.expectNewConn(); c1.releaseConn(); - conn_pool_.expectEnableUpstreamReady(true); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()).Times(2); + conn_pool_->expectEnableUpstreamReady(true); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()).Times(2); c2.releaseConn(); c3.releaseConn(); // Disconnect both connections. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()).Times(2); - conn_pool_.test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()).Times(2); + conn_pool_->test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -857,6 +901,7 @@ TEST_P(TcpConnPoolImplTest, ConcurrentConnections) { * Tests ConnectionState lifecycle with multiple concurrent connections. */ TEST_P(TcpConnPoolImplTest, ConnectionStateWithConcurrentConnections) { + initialize(); int state_destroyed = 0; auto* s1 = new TestConnectionState(1, [&]() -> void { state_destroyed |= 1; }); @@ -873,12 +918,12 @@ TEST_P(TcpConnPoolImplTest, ConnectionStateWithConcurrentConnections) { EXPECT_EQ(0, state_destroyed); // Finish c1, which gets c3 going. - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); - conn_pool_.expectEnableUpstreamReady(false); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); + conn_pool_->expectEnableUpstreamReady(false); c3.expectNewConn(); c1.releaseConn(); - conn_pool_.expectEnableUpstreamReady(true); + conn_pool_->expectEnableUpstreamReady(true); // c3 now has the state set by c1. EXPECT_EQ(s1, c3.callbacks_.conn_data_->connectionStateTyped()); @@ -888,16 +933,16 @@ TEST_P(TcpConnPoolImplTest, ConnectionStateWithConcurrentConnections) { c3.callbacks_.conn_data_->setConnectionState(std::unique_ptr(s3)); EXPECT_EQ(1, state_destroyed); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()).Times(2); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()).Times(2); c2.releaseConn(); c3.releaseConn(); EXPECT_EQ(1, state_destroyed); // Disconnect both connections. - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()).Times(2); - conn_pool_.test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()).Times(2); + conn_pool_->test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); EXPECT_EQ(7, state_destroyed); @@ -907,21 +952,22 @@ TEST_P(TcpConnPoolImplTest, ConnectionStateWithConcurrentConnections) { * Tests that the DrainCallback is invoked when the number of connections goes to zero. */ TEST_P(TcpConnPoolImplTest, DrainCallback) { + initialize(); ReadyWatcher drained; EXPECT_CALL(drained, ready()); - conn_pool_.addDrainedCallback([&]() -> void { drained.ready(); }); + conn_pool_->addDrainedCallback([&]() -> void { drained.ready(); }); ActiveTestConn c1(*this, 0, ActiveTestConn::Type::CreateConnection); ActiveTestConn c2(*this, 0, ActiveTestConn::Type::Pending); c2.handle_->cancel(ConnectionPool::CancelPolicy::Default); - EXPECT_CALL(conn_pool_, onConnReleasedForTest()); + EXPECT_CALL(*conn_pool_, onConnReleasedForTest()); EXPECT_CALL(drained, ready()); c1.releaseConn(); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); dispatcher_.clearDeferredDeleteList(); } @@ -929,14 +975,15 @@ TEST_P(TcpConnPoolImplTest, DrainCallback) { * Test draining a connection pool that has a pending connection. */ TEST_P(TcpConnPoolImplTest, DrainWhileConnecting) { + initialize(); ReadyWatcher drained; ConnPoolCallbacks callbacks; - conn_pool_.expectConnCreate(); - Tcp::ConnectionPool::Cancellable* handle = conn_pool_.newConnection(callbacks); + conn_pool_->expectConnCreate(); + Tcp::ConnectionPool::Cancellable* handle = conn_pool_->newConnection(callbacks); EXPECT_NE(nullptr, handle); - conn_pool_.addDrainedCallback([&]() -> void { drained.ready(); }); + conn_pool_->addDrainedCallback([&]() -> void { drained.ready(); }); if (test_new_connection_pool_) { // The shared connection pool removes and closes connecting clients if there are no // pending requests. @@ -944,12 +991,12 @@ TEST_P(TcpConnPoolImplTest, DrainWhileConnecting) { handle->cancel(ConnectionPool::CancelPolicy::Default); } else { handle->cancel(ConnectionPool::CancelPolicy::Default); - EXPECT_CALL(*conn_pool_.test_conns_[0].connection_, + EXPECT_CALL(*conn_pool_->test_conns_[0].connection_, close(Network::ConnectionCloseType::NoFlush)); EXPECT_CALL(drained, ready()); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); } - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); dispatcher_.clearDeferredDeleteList(); } @@ -957,9 +1004,10 @@ TEST_P(TcpConnPoolImplTest, DrainWhileConnecting) { * Test that the DrainCallback is invoked when a connection is closed. */ TEST_P(TcpConnPoolImplTest, DrainOnClose) { + initialize(); ReadyWatcher drained; EXPECT_CALL(drained, ready()); - conn_pool_.addDrainedCallback([&]() -> void { drained.ready(); }); + conn_pool_->addDrainedCallback([&]() -> void { drained.ready(); }); ActiveTestConn c1(*this, 0, ActiveTestConn::Type::CreateConnection); @@ -969,9 +1017,9 @@ TEST_P(TcpConnPoolImplTest, DrainOnClose) { EXPECT_EQ(Network::ConnectionEvent::RemoteClose, event); c1.releaseConn(); })); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); - EXPECT_CALL(conn_pool_, onConnDestroyedForTest()); + EXPECT_CALL(*conn_pool_, onConnDestroyedForTest()); dispatcher_.clearDeferredDeleteList(); } @@ -979,6 +1027,7 @@ TEST_P(TcpConnPoolImplTest, DrainOnClose) { * Test connecting_request_capacity logic. */ TEST_P(TcpConnPoolImplTest, RequestCapacity) { + initialize(); if (!test_new_connection_pool_) { return; } @@ -991,20 +1040,20 @@ TEST_P(TcpConnPoolImplTest, RequestCapacity) { Tcp::ConnectionPool::Cancellable* handle2; { // Request 1 should kick off a new connection. - conn_pool_.expectConnCreate(); - handle1 = conn_pool_.newConnection(callbacks1); + conn_pool_->expectConnCreate(); + handle1 = conn_pool_->newConnection(callbacks1); EXPECT_NE(nullptr, handle1); } { // Request 2 should kick off a new connection. - conn_pool_.expectConnCreate(); - handle2 = conn_pool_.newConnection(callbacks2); + conn_pool_->expectConnCreate(); + handle2 = conn_pool_->newConnection(callbacks2); EXPECT_NE(nullptr, handle2); } // This should set the number of requests remaining to 1 on the active // connections, and the connecting_request_capacity to 2 as well. - conn_pool_.drainConnections(); + conn_pool_->drainConnections(); // Cancel the connections. Because neither used CloseExcess, the two connections should persist. handle1->cancel(ConnectionPool::CancelPolicy::Default); @@ -1020,17 +1069,17 @@ TEST_P(TcpConnPoolImplTest, RequestCapacity) { { // The next two requests will use the connections in progress, bringing // connecting_request_capacity to zero. - handle3 = conn_pool_.newConnection(callbacks3); + handle3 = conn_pool_->newConnection(callbacks3); EXPECT_NE(nullptr, handle3); - handle4 = conn_pool_.newConnection(callbacks4); + handle4 = conn_pool_->newConnection(callbacks4); EXPECT_NE(nullptr, handle4); } { // With connecting_request_capacity zero, a request for a new connection // will kick off connection #3. - conn_pool_.expectConnCreate(); - handle5 = conn_pool_.newConnection(callbacks5); + conn_pool_->expectConnCreate(); + handle5 = conn_pool_->newConnection(callbacks5); EXPECT_NE(nullptr, handle5); } @@ -1038,22 +1087,23 @@ TEST_P(TcpConnPoolImplTest, RequestCapacity) { handle3->cancel(ConnectionPool::CancelPolicy::Default); handle4->cancel(ConnectionPool::CancelPolicy::Default); handle5->cancel(ConnectionPool::CancelPolicy::Default); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); - conn_pool_.test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); - conn_pool_.test_conns_[2].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + conn_pool_->test_conns_[1].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + conn_pool_->test_conns_[2].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); } // Test that maybePrefetch is passed up to the base class implementation. TEST_P(TcpConnPoolImplTest, TestPrefetch) { + initialize(); if (!test_new_connection_pool_) { return; } - EXPECT_FALSE(conn_pool_.maybePrefetch(0)); + EXPECT_FALSE(conn_pool_->maybePrefetch(0)); - conn_pool_.expectConnCreate(); - ASSERT_TRUE(conn_pool_.maybePrefetch(2)); + conn_pool_->expectConnCreate(); + ASSERT_TRUE(conn_pool_->maybePrefetch(2)); - conn_pool_.test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); + conn_pool_->test_conns_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); } /** From 0d89faf73b8d116a450c2e6a4b0d865102184a33 Mon Sep 17 00:00:00 2001 From: Sotiris Nanopoulos Date: Tue, 15 Dec 2020 11:54:44 -0800 Subject: [PATCH 33/49] Mark starttls_integration_test flaky on Windows (#14419) Signed-off-by: Sotiris Nanopoulos --- test/integration/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/BUILD b/test/integration/BUILD index d433a40a9d30..d5cc80103e60 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1229,6 +1229,8 @@ envoy_cc_test( data = [ "//test/config/integration/certs", ], + # TODO(envoyproxy/windows-dev): Investigate timeout + tags = ["flaky_on_windows"], deps = [ ":integration_lib", ":starttls_integration_proto_cc_proto", From 0cb98ff9a0432748d4485c7dadfd182c077065a7 Mon Sep 17 00:00:00 2001 From: Matt Klein Date: Tue, 15 Dec 2020 11:59:39 -0800 Subject: [PATCH 34/49] aggregate cluster: cleanups (#14411) Follow up to #14382. Remove TLS use in aggregate cluster. Move all logic into the thread local load balancers making the implementation less brittle and easier to understand. Signed-off-by: Matt Klein --- .../extensions/clusters/aggregate/cluster.cc | 113 ++++++++++-------- .../extensions/clusters/aggregate/cluster.h | 96 +++++---------- .../clusters/aggregate/cluster_test.cc | 19 ++- 3 files changed, 100 insertions(+), 128 deletions(-) diff --git a/source/extensions/clusters/aggregate/cluster.cc b/source/extensions/clusters/aggregate/cluster.cc index c8b406daadfb..c48ba103c9d7 100644 --- a/source/extensions/clusters/aggregate/cluster.cc +++ b/source/extensions/clusters/aggregate/cluster.cc @@ -17,21 +17,46 @@ Cluster::Cluster(const envoy::config::cluster::v3::Cluster& cluster, Upstream::ClusterManager& cluster_manager, Runtime::Loader& runtime, Random::RandomGenerator& random, Server::Configuration::TransportSocketFactoryContextImpl& factory_context, - Stats::ScopePtr&& stats_scope, ThreadLocal::SlotAllocator& tls, bool added_via_api) + Stats::ScopePtr&& stats_scope, bool added_via_api) : Upstream::ClusterImplBase(cluster, runtime, factory_context, std::move(stats_scope), added_via_api, factory_context.dispatcher().timeSource()), - cluster_manager_(cluster_manager), runtime_(runtime), random_(random), tls_(tls), - clusters_(config.clusters().begin(), config.clusters().end()) { - tls_.set([info = info(), &runtime, &random](Event::Dispatcher&) { - auto per_thread_load_balancer = std::make_unique(); - per_thread_load_balancer->lb_ = std::make_unique( - info->stats(), runtime, random, info->lbConfig()); - return per_thread_load_balancer; - }); + cluster_manager_(cluster_manager), runtime_(runtime), random_(random), + clusters_(std::make_shared(config.clusters().begin(), config.clusters().end())) {} + +AggregateClusterLoadBalancer::AggregateClusterLoadBalancer( + const Upstream::ClusterInfoConstSharedPtr& parent_info, + Upstream::ClusterManager& cluster_manager, Runtime::Loader& runtime, + Random::RandomGenerator& random, const ClusterSetConstSharedPtr& clusters) + : parent_info_(parent_info), cluster_manager_(cluster_manager), runtime_(runtime), + random_(random), clusters_(clusters) { + for (const auto& cluster : *clusters_) { + auto tlc = cluster_manager_.getThreadLocalCluster(cluster); + // It is possible when initializing the cluster, the included cluster doesn't exist. e.g., the + // cluster could be added dynamically by xDS. + if (tlc == nullptr) { + continue; + } + + // Add callback for clusters initialized before aggregate cluster. + addMemberUpdateCallbackForCluster(*tlc); + } + refresh(); + handle_ = cluster_manager_.addThreadLocalClusterUpdateCallbacks(*this); +} + +void AggregateClusterLoadBalancer::addMemberUpdateCallbackForCluster( + Upstream::ThreadLocalCluster& thread_local_cluster) { + thread_local_cluster.prioritySet().addMemberUpdateCb( + [this, target_cluster_info = thread_local_cluster.info()](const Upstream::HostVector&, + const Upstream::HostVector&) { + ENVOY_LOG(debug, "member update for cluster '{}' in aggregate cluster '{}'", + target_cluster_info->name(), parent_info_->name()); + refresh(); + }); } PriorityContextPtr -Cluster::linearizePrioritySet(const std::function& skip_predicate) { +AggregateClusterLoadBalancer::linearizePrioritySet(OptRef excluded_cluster) { PriorityContextPtr priority_context = std::make_unique(); uint32_t next_priority_after_linearizing = 0; @@ -42,15 +67,20 @@ Cluster::linearizePrioritySet(const std::function& ski // The linearization result is: // [C_0.P_0, C_0.P_1, C_0.P_2, C_1.P_0, C_1.P_1, C_2.P_0, C_2.P_1, C_2.P_2, C_2.P_3] // and the traffic will be distributed among these priorities. - for (const auto& cluster : clusters_) { - if (skip_predicate(cluster)) { + for (const auto& cluster : *clusters_) { + if (excluded_cluster.has_value() && excluded_cluster.value().get() == cluster) { continue; } auto tlc = cluster_manager_.getThreadLocalCluster(cluster); // It is possible that the cluster doesn't exist, e.g., the cluster could be deleted or the // cluster hasn't been added by xDS. if (tlc == nullptr) { + ENVOY_LOG(debug, "refresh: cluster '{}' absent in aggregate cluster '{}'", cluster, + parent_info_->name()); continue; + } else { + ENVOY_LOG(debug, "refresh: cluster '{}' found in aggregate cluster '{}'", cluster, + parent_info_->name()); } uint32_t priority_in_current_cluster = 0; @@ -73,57 +103,34 @@ Cluster::linearizePrioritySet(const std::function& ski return priority_context; } -void Cluster::startPreInit() { - for (const auto& cluster : clusters_) { - auto tlc = cluster_manager_.getThreadLocalCluster(cluster); - // It is possible when initializing the cluster, the included cluster doesn't exist. e.g., the - // cluster could be added dynamically by xDS. - if (tlc == nullptr) { - continue; - } - - // Add callback for clusters initialized before aggregate cluster. - tlc->prioritySet().addMemberUpdateCb( - [this, cluster](const Upstream::HostVector&, const Upstream::HostVector&) { - ENVOY_LOG(debug, "member update for cluster '{}' in aggregate cluster '{}'", cluster, - this->info()->name()); - refresh(); - }); +void AggregateClusterLoadBalancer::refresh(OptRef excluded_cluster) { + PriorityContextPtr priority_context = linearizePrioritySet(excluded_cluster); + if (!priority_context->priority_set_.hostSetsPerPriority().empty()) { + load_balancer_ = std::make_unique( + *priority_context, parent_info_->stats(), runtime_, random_, parent_info_->lbConfig()); + } else { + load_balancer_ = nullptr; } - refresh(); - handle_ = cluster_manager_.addThreadLocalClusterUpdateCallbacks(*this); - - onPreInitComplete(); -} - -void Cluster::refresh(const std::function& skip_predicate) { - // Post the priority set to worker threads. - // TODO(mattklein123): Remove "this" capture. - tls_.runOnAllThreads([this, skip_predicate, cluster_name = this->info()->name()]( - OptRef per_thread_load_balancer) { - PriorityContextPtr priority_context = linearizePrioritySet(skip_predicate); - per_thread_load_balancer->get().refresh(std::move(priority_context)); - }); + priority_context_ = std::move(priority_context); } -void Cluster::onClusterAddOrUpdate(Upstream::ThreadLocalCluster& cluster) { - if (std::find(clusters_.begin(), clusters_.end(), cluster.info()->name()) != clusters_.end()) { +void AggregateClusterLoadBalancer::onClusterAddOrUpdate(Upstream::ThreadLocalCluster& cluster) { + if (std::find(clusters_->begin(), clusters_->end(), cluster.info()->name()) != clusters_->end()) { ENVOY_LOG(debug, "adding or updating cluster '{}' for aggregate cluster '{}'", - cluster.info()->name(), info()->name()); + cluster.info()->name(), parent_info_->name()); refresh(); - cluster.prioritySet().addMemberUpdateCb( - [this](const Upstream::HostVector&, const Upstream::HostVector&) { refresh(); }); + addMemberUpdateCallbackForCluster(cluster); } } -void Cluster::onClusterRemoval(const std::string& cluster_name) { +void AggregateClusterLoadBalancer::onClusterRemoval(const std::string& cluster_name) { // The onClusterRemoval callback is called before the thread local cluster is removed. There // will be a dangling pointer to the thread local cluster if the deleted cluster is not skipped // when we refresh the load balancer. - if (std::find(clusters_.begin(), clusters_.end(), cluster_name) != clusters_.end()) { - ENVOY_LOG(debug, "removing cluster '{}' from aggreagte cluster '{}'", cluster_name, - info()->name()); - refresh([cluster_name](const std::string& c) { return cluster_name == c; }); + if (std::find(clusters_->begin(), clusters_->end(), cluster_name) != clusters_->end()) { + ENVOY_LOG(debug, "removing cluster '{}' from aggregate cluster '{}'", cluster_name, + parent_info_->name()); + refresh(cluster_name); } } @@ -182,7 +189,7 @@ ClusterFactory::createClusterWithConfig( auto new_cluster = std::make_shared(cluster, proto_config, context.clusterManager(), context.runtime(), context.api().randomGenerator(), socket_factory_context, - std::move(stats_scope), context.tls(), context.addedViaApi()); + std::move(stats_scope), context.addedViaApi()); auto lb = std::make_unique(*new_cluster); return std::make_pair(new_cluster, std::move(lb)); } diff --git a/source/extensions/clusters/aggregate/cluster.h b/source/extensions/clusters/aggregate/cluster.h index 59f74b08d876..72df0d685824 100644 --- a/source/extensions/clusters/aggregate/cluster.h +++ b/source/extensions/clusters/aggregate/cluster.h @@ -3,8 +3,11 @@ #include "envoy/config/cluster/v3/cluster.pb.h" #include "envoy/extensions/clusters/aggregate/v3/cluster.pb.h" #include "envoy/extensions/clusters/aggregate/v3/cluster.pb.validate.h" +#include "envoy/stream_info/stream_info.h" #include "envoy/thread_local/thread_local_object.h" +#include "envoy/upstream/thread_local_cluster.h" +#include "common/common/logger.h" #include "common/upstream/cluster_factory_impl.h" #include "common/upstream/upstream_impl.h" @@ -29,72 +32,49 @@ struct PriorityContext { using PriorityContextPtr = std::unique_ptr; -class AggregateClusterLoadBalancer; +// Order matters so a vector must be used for rebuilds. If the vector size becomes larger we can +// maintain a parallel set for lookups during cluster update callbacks. +using ClusterSet = std::vector; +using ClusterSetConstSharedPtr = std::shared_ptr; -class Cluster : public Upstream::ClusterImplBase, Upstream::ClusterUpdateCallbacks { +class Cluster : public Upstream::ClusterImplBase { public: Cluster(const envoy::config::cluster::v3::Cluster& cluster, const envoy::extensions::clusters::aggregate::v3::ClusterConfig& config, Upstream::ClusterManager& cluster_manager, Runtime::Loader& runtime, Random::RandomGenerator& random, Server::Configuration::TransportSocketFactoryContextImpl& factory_context, - Stats::ScopePtr&& stats_scope, ThreadLocal::SlotAllocator& tls, bool added_via_api); - - struct PerThreadLoadBalancer : public ThreadLocal::ThreadLocalObject { - AggregateClusterLoadBalancer& get() { - // We can refresh before the per-worker LB is created. One of these variants should hold - // a non-null value. - if (absl::holds_alternative>(lb_)) { - ASSERT(absl::get>(lb_) != nullptr); - return *absl::get>(lb_); - } else { - ASSERT(absl::get(lb_) != nullptr); - return *absl::get(lb_); - } - } - - // For aggregate cluster the per-thread LB is only created once. We need to own it so we - // can pre-populate it before the LB is created and handed to the cluster. - absl::variant, AggregateClusterLoadBalancer*> lb_; - }; + Stats::ScopePtr&& stats_scope, bool added_via_api); // Upstream::Cluster Upstream::Cluster::InitializePhase initializePhase() const override { return Upstream::Cluster::InitializePhase::Secondary; } - // Upstream::ClusterUpdateCallbacks - void onClusterAddOrUpdate(Upstream::ThreadLocalCluster& cluster) override; - void onClusterRemoval(const std::string& cluster_name) override; - - void refresh() { - refresh([](const std::string&) { return false; }); - } - - Upstream::ClusterUpdateCallbacksHandlePtr handle_; Upstream::ClusterManager& cluster_manager_; Runtime::Loader& runtime_; Random::RandomGenerator& random_; - ThreadLocal::TypedSlot tls_; - const std::vector clusters_; + const ClusterSetConstSharedPtr clusters_; private: // Upstream::ClusterImplBase - void startPreInit() override; - - void refresh(const std::function& skip_predicate); - PriorityContextPtr - linearizePrioritySet(const std::function& skip_predicate); + void startPreInit() override { onPreInitComplete(); } }; // Load balancer used by each worker thread. It will be refreshed when clusters, hosts or priorities // are updated. -class AggregateClusterLoadBalancer : public Upstream::LoadBalancer { +class AggregateClusterLoadBalancer : public Upstream::LoadBalancer, + Upstream::ClusterUpdateCallbacks, + Logger::Loggable { public: - AggregateClusterLoadBalancer( - Upstream::ClusterStats& stats, Runtime::Loader& runtime, Random::RandomGenerator& random, - const envoy::config::cluster::v3::Cluster::CommonLbConfig& common_config) - : stats_(stats), runtime_(runtime), random_(random), common_config_(common_config) {} + AggregateClusterLoadBalancer(const Upstream::ClusterInfoConstSharedPtr& parent_info, + Upstream::ClusterManager& cluster_manager, Runtime::Loader& runtime, + Random::RandomGenerator& random, + const ClusterSetConstSharedPtr& clusters); + + // Upstream::ClusterUpdateCallbacks + void onClusterAddOrUpdate(Upstream::ThreadLocalCluster& cluster) override; + void onClusterRemoval(const std::string& cluster_name) override; // Upstream::LoadBalancer Upstream::HostConstSharedPtr chooseHost(Upstream::LoadBalancerContext* context) override; @@ -135,23 +115,18 @@ class AggregateClusterLoadBalancer : public Upstream::LoadBalancer { using LoadBalancerImplPtr = std::unique_ptr; + void addMemberUpdateCallbackForCluster(Upstream::ThreadLocalCluster& thread_local_cluster); + PriorityContextPtr linearizePrioritySet(OptRef excluded_cluster); + void refresh(OptRef excluded_cluster = OptRef()); + LoadBalancerImplPtr load_balancer_; - Upstream::ClusterStats& stats_; + Upstream::ClusterInfoConstSharedPtr parent_info_; + Upstream::ClusterManager& cluster_manager_; Runtime::Loader& runtime_; Random::RandomGenerator& random_; - const envoy::config::cluster::v3::Cluster::CommonLbConfig& common_config_; PriorityContextPtr priority_context_; - -public: - void refresh(PriorityContextPtr priority_context) { - if (!priority_context->priority_set_.hostSetsPerPriority().empty()) { - load_balancer_ = std::make_unique(*priority_context, stats_, runtime_, - random_, common_config_); - } else { - load_balancer_ = nullptr; - } - priority_context_ = std::move(priority_context); - } + const ClusterSetConstSharedPtr clusters_; + Upstream::ClusterUpdateCallbacksHandlePtr handle_; }; // Load balancer factory created by the main thread and will be called in each worker thread to @@ -160,14 +135,9 @@ struct AggregateLoadBalancerFactory : public Upstream::LoadBalancerFactory { AggregateLoadBalancerFactory(const Cluster& cluster) : cluster_(cluster) {} // Upstream::LoadBalancerFactory Upstream::LoadBalancerPtr create() override { - // See comments in PerThreadLoadBalancer above for why the follow is done. - auto per_thread_local_balancer = cluster_.tls_.get(); - ASSERT(absl::get>( - per_thread_local_balancer->lb_) != nullptr); - auto to_return = std::move( - absl::get>(per_thread_local_balancer->lb_)); - per_thread_local_balancer->lb_ = to_return.get(); - return to_return; + return std::make_unique( + cluster_.info(), cluster_.cluster_manager_, cluster_.runtime_, cluster_.random_, + cluster_.clusters_); } const Cluster& cluster_; diff --git a/test/extensions/clusters/aggregate/cluster_test.cc b/test/extensions/clusters/aggregate/cluster_test.cc index 1d78f5e8779e..303f9d841190 100644 --- a/test/extensions/clusters/aggregate/cluster_test.cc +++ b/test/extensions/clusters/aggregate/cluster_test.cc @@ -72,7 +72,6 @@ class AggregateClusterTest : public Event::TestUsingSimulatedTime, public testin Upstream::HostSetImpl::partitionHosts(std::make_shared(hosts), Upstream::HostsPerLocalityImpl::empty()), nullptr, hosts, {}, 100); - cluster_->refresh(); } void setupSecondary(int priority, int healthy_hosts, int degraded_hosts, int unhealthy_hosts) { @@ -83,7 +82,6 @@ class AggregateClusterTest : public Event::TestUsingSimulatedTime, public testin Upstream::HostSetImpl::partitionHosts(std::make_shared(hosts), Upstream::HostsPerLocalityImpl::empty()), nullptr, hosts, {}, 100); - cluster_->refresh(); } void setupPrioritySet() { @@ -107,25 +105,22 @@ class AggregateClusterTest : public Event::TestUsingSimulatedTime, public testin cluster_ = std::make_shared(cluster_config, config, cm_, runtime_, api_->randomGenerator(), - factory_context, std::move(scope), tls_, false); + factory_context, std::move(scope), false); - thread_aware_lb_ = std::make_unique(*cluster_); - lb_factory_ = thread_aware_lb_->factory(); - lb_ = lb_factory_->create(); - - EXPECT_CALL(cm_, getThreadLocalCluster(Eq("aggregate_cluster"))) - .WillRepeatedly(Return(&aggregate_cluster_)); + cm_.initializeThreadLocalClusters({"primary", "secondary"}); EXPECT_CALL(cm_, getThreadLocalCluster(Eq("primary"))).WillRepeatedly(Return(&primary_)); EXPECT_CALL(cm_, getThreadLocalCluster(Eq("secondary"))).WillRepeatedly(Return(&secondary_)); - EXPECT_CALL(cm_, getThreadLocalCluster(Eq("tertiary"))).WillRepeatedly(Return(nullptr)); ON_CALL(primary_, prioritySet()).WillByDefault(ReturnRef(primary_ps_)); ON_CALL(secondary_, prioritySet()).WillByDefault(ReturnRef(secondary_ps_)); - ON_CALL(aggregate_cluster_, loadBalancer()).WillByDefault(ReturnRef(*lb_)); setupPrioritySet(); ON_CALL(primary_, loadBalancer()).WillByDefault(ReturnRef(primary_load_balancer_)); ON_CALL(secondary_, loadBalancer()).WillByDefault(ReturnRef(secondary_load_balancer_)); + + thread_aware_lb_ = std::make_unique(*cluster_); + lb_factory_ = thread_aware_lb_->factory(); + lb_ = lb_factory_->create(); } Stats::TestUtil::TestStore stats_store_; @@ -150,7 +145,7 @@ class AggregateClusterTest : public Event::TestUsingSimulatedTime, public testin new NiceMock()}; std::shared_ptr secondary_info_{ new NiceMock()}; - NiceMock aggregate_cluster_, primary_, secondary_; + NiceMock primary_, secondary_; Upstream::PrioritySetImpl primary_ps_, secondary_ps_; NiceMock primary_load_balancer_, secondary_load_balancer_; From 5dc58a6a98ae781020f3742c3ee13b48cee670fe Mon Sep 17 00:00:00 2001 From: Taylor Barrella Date: Tue, 15 Dec 2020 12:43:20 -0800 Subject: [PATCH 35/49] tls: disable TLS inspector injection (#14404) Signed-off-by: Taylor Barrella tabarr@google.com Additional Description: See the issue for context Risk Level: Medium Testing: Unit tests Docs Changes: I didn't see injection mentioned at https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listener_filters/tls_inspector or from grepping for "inject" in docs/ Release Notes: Runtime guard: envoy.reloadable_features.disable_tls_inspector_injection Fixes #13601 Signed-off-by: Taylor Barrella --- docs/root/version_history/current.rst | 1 + source/common/runtime/runtime_features.cc | 1 + source/server/listener_impl.cc | 3 +++ test/server/listener_manager_impl_test.cc | 31 +++++++++++++++++++++++ test/server/listener_manager_impl_test.h | 8 ++++++ 5 files changed, 44 insertions(+) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 43fc32a15e14..f3f3b5e51dd9 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -22,6 +22,7 @@ Minor Behavior Changes * http: upstream protocol will now only be logged if an upstream stream was established. * jwt_authn filter: added support of Jwt time constraint verification with a clock skew (default to 60 seconds) and added a filter config field :ref:`clock_skew_seconds ` to configure it. * kill_request: enable a way to configure kill header name in KillRequest proto. +* listener: injection of the :ref:`TLS inspector ` has been disabled by default. This feature is controlled by the runtime guard `envoy.reloadable_features.disable_tls_inspector_injection`. * memory: enable new tcmalloc with restartable sequences for aarch64 builds. * mongo proxy metrics: swapped network connection remote and local closed counters previously set reversed (`cx_destroy_local_with_active_rq` and `cx_destroy_remote_with_active_rq`). * performance: improve performance when handling large HTTP/1 bodies. diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 74a3ce75f845..1ac9652de1b4 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -63,6 +63,7 @@ constexpr const char* runtime_features[] = { "envoy.reloadable_features.allow_response_for_timeout", "envoy.reloadable_features.consume_all_retry_headers", "envoy.reloadable_features.check_ocsp_policy", + "envoy.reloadable_features.disable_tls_inspector_injection", "envoy.reloadable_features.disallow_unbounded_access_logs", "envoy.reloadable_features.early_errors_via_hcm", "envoy.reloadable_features.enable_dns_cache_circuit_breakers", diff --git a/source/server/listener_impl.cc b/source/server/listener_impl.cc index 03d156734c53..879386ce1c04 100644 --- a/source/server/listener_impl.cc +++ b/source/server/listener_impl.cc @@ -46,6 +46,9 @@ bool anyFilterChain( } bool needTlsInspector(const envoy::config::listener::v3::Listener& config) { + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.disable_tls_inspector_injection")) { + return false; + } return anyFilterChain(config, [](const auto& filter_chain) { const auto& matcher = filter_chain.filter_chain_match(); diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index 98a6977d19b4..1d5da4a8eeab 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -3296,7 +3296,33 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithOverlappi "overlapping matching rules are defined"); } +TEST_F(ListenerManagerImplWithRealFiltersTest, TlsFilterChainWithTlsInspectorInjectionDisabled) { + const std::string yaml = TestEnvironment::substitute(R"EOF( + address: + socket_address: { address: 127.0.0.1, port_value: 1234 } + filter_chains: + - filter_chain_match: + transport_protocol: "tls" + - filter_chain_match: + # empty + )EOF", + Network::Address::IpVersion::v4); + + EXPECT_CALL(server_.api_.random_, uuid()); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); + manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); + EXPECT_EQ(1U, manager_->listeners().size()); + + // Make sure there are no listener filters (i.e. no automatically injected TLS Inspector). + Network::ListenerConfig& listener = manager_->listeners().back().get(); + Network::FilterChainFactory& filterChainFactory = listener.filterChainFactory(); + Network::MockListenerFilterManager manager; + EXPECT_CALL(manager, addAcceptFilter_(_, _)).Times(0); + EXPECT_TRUE(filterChainFactory.createListenerFilterChain(manager)); +} + TEST_F(ListenerManagerImplWithRealFiltersTest, TlsFilterChainWithoutTlsInspector) { + auto tls_inspector_injection_enabled_guard = enableTlsInspectorInjectionForThisTest(); const std::string yaml = TestEnvironment::substitute(R"EOF( address: socket_address: { address: 127.0.0.1, port_value: 1234 } @@ -3327,6 +3353,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TlsFilterChainWithoutTlsInspector // Test the tls inspector is not injected twice when the deprecated name is used. TEST_F(ListenerManagerImplWithRealFiltersTest, DEPRECATED_FEATURE_TEST(TlsFilterChainWithDeprecatedTlsInspectorName)) { + auto tls_inspector_injection_enabled_guard = enableTlsInspectorInjectionForThisTest(); const std::string yaml = TestEnvironment::substitute(R"EOF( address: socket_address: { address: 127.0.0.1, port_value: 1234 } @@ -3358,6 +3385,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, } TEST_F(ListenerManagerImplWithRealFiltersTest, SniFilterChainWithoutTlsInspector) { + auto tls_inspector_injection_enabled_guard = enableTlsInspectorInjectionForThisTest(); const std::string yaml = TestEnvironment::substitute(R"EOF( address: socket_address: { address: 127.0.0.1, port_value: 1234 } @@ -3386,6 +3414,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SniFilterChainWithoutTlsInspector } TEST_F(ListenerManagerImplWithRealFiltersTest, AlpnFilterChainWithoutTlsInspector) { + auto tls_inspector_injection_enabled_guard = enableTlsInspectorInjectionForThisTest(); const std::string yaml = TestEnvironment::substitute(R"EOF( address: socket_address: { address: 127.0.0.1, port_value: 1234 } @@ -3414,6 +3443,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, AlpnFilterChainWithoutTlsInspecto } TEST_F(ListenerManagerImplWithRealFiltersTest, CustomTransportProtocolWithSniWithoutTlsInspector) { + auto tls_inspector_injection_enabled_guard = enableTlsInspectorInjectionForThisTest(); const std::string yaml = TestEnvironment::substitute(R"EOF( address: socket_address: { address: 127.0.0.1, port_value: 1234 } @@ -4689,6 +4719,7 @@ TEST_F(ListenerManagerImplForInPlaceFilterChainUpdateTest, TraditionalUpdateIfAn TEST_F(ListenerManagerImplForInPlaceFilterChainUpdateTest, TraditionalUpdateIfImplicitTlsInspectorChanges) { + auto tls_inspector_injection_enabled_guard = enableTlsInspectorInjectionForThisTest(); EXPECT_CALL(*worker_, start(_)); manager_->startWorkers(guard_dog_); diff --git a/test/server/listener_manager_impl_test.h b/test/server/listener_manager_impl_test.h index 9b22f3109bf8..01104f1729e4 100644 --- a/test/server/listener_manager_impl_test.h +++ b/test/server/listener_manager_impl_test.h @@ -279,6 +279,14 @@ class ListenerManagerImplTest : public testing::Test { return scoped_runtime; } + ABSL_MUST_USE_RESULT + auto enableTlsInspectorInjectionForThisTest() { + auto scoped_runtime = std::make_unique(); + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.disable_tls_inspector_injection", "false"}}); + return scoped_runtime; + } + NiceMock os_sys_calls_; TestThreadsafeSingletonInjector os_calls_{&os_sys_calls_}; Api::OsSysCallsImpl os_sys_calls_actual_; From ca7250def62f1f276f67838576a3b48fc546487f Mon Sep 17 00:00:00 2001 From: Piotr Sikora Date: Tue, 15 Dec 2020 15:02:39 -0800 Subject: [PATCH 36/49] wasm: clear route cache when modifying HTTP request headers. (#14318) Proxy-Wasm ABI v0.1.0 has a dedicated call for clearing route cache (proxy_clear_route_cache). Proxy-Wasm ABI v0.2.0 removed it in favor of automatically clearing route cache, but that part was never implemented in Envoy. Partially fixes proxy-wasm/spec#16. Signed-off-by: Piotr Sikora --- source/extensions/common/wasm/context.cc | 12 ++++++++++++ .../filters/http/wasm/test_data/headers_rust.rs | 5 +++++ .../filters/http/wasm/test_data/test_cpp.cc | 10 ++++++++++ .../filters/http/wasm/wasm_filter_test.cc | 17 +++++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc index 8651b98ef4b3..b7e404bcffe5 100644 --- a/source/extensions/common/wasm/context.cc +++ b/source/extensions/common/wasm/context.cc @@ -708,6 +708,9 @@ WasmResult Context::addHeaderMapValue(WasmHeaderMapType type, absl::string_view } const Http::LowerCaseString lower_key{std::string(key)}; map->addCopy(lower_key, std::string(value)); + if (type == WasmHeaderMapType::RequestHeaders) { + decoder_callbacks_->clearRouteCache(); + } return WasmResult::Ok; } @@ -769,6 +772,9 @@ WasmResult Context::setHeaderMapPairs(WasmHeaderMapType type, const Pairs& pairs const Http::LowerCaseString lower_key{std::string(p.first)}; map->addCopy(lower_key, std::string(p.second)); } + if (type == WasmHeaderMapType::RequestHeaders) { + decoder_callbacks_->clearRouteCache(); + } return WasmResult::Ok; } @@ -779,6 +785,9 @@ WasmResult Context::removeHeaderMapValue(WasmHeaderMapType type, absl::string_vi } const Http::LowerCaseString lower_key{std::string(key)}; map->remove(lower_key); + if (type == WasmHeaderMapType::RequestHeaders) { + decoder_callbacks_->clearRouteCache(); + } return WasmResult::Ok; } @@ -790,6 +799,9 @@ WasmResult Context::replaceHeaderMapValue(WasmHeaderMapType type, absl::string_v } const Http::LowerCaseString lower_key{std::string(key)}; map->setCopy(lower_key, value); + if (type == WasmHeaderMapType::RequestHeaders) { + decoder_callbacks_->clearRouteCache(); + } return WasmResult::Ok; } diff --git a/test/extensions/filters/http/wasm/test_data/headers_rust.rs b/test/extensions/filters/http/wasm/test_data/headers_rust.rs index 6d9fc94a7a9c..dbdaff136878 100644 --- a/test/extensions/filters/http/wasm/test_data/headers_rust.rs +++ b/test/extensions/filters/http/wasm/test_data/headers_rust.rs @@ -36,6 +36,11 @@ impl HttpContext for TestStream { Action::Continue } + fn on_http_response_headers(&mut self, _: usize) -> Action { + self.set_http_response_header("test-status", Some("OK")); + Action::Continue + } + fn on_http_response_trailers(&mut self, _: usize) -> Action { Action::Pause } diff --git a/test/extensions/filters/http/wasm/test_data/test_cpp.cc b/test/extensions/filters/http/wasm/test_data/test_cpp.cc index 3e009a3da8c3..f09b6198f835 100644 --- a/test/extensions/filters/http/wasm/test_data/test_cpp.cc +++ b/test/extensions/filters/http/wasm/test_data/test_cpp.cc @@ -35,6 +35,7 @@ class TestContext : public Context { FilterHeadersStatus onRequestHeaders(uint32_t, bool) override; FilterTrailersStatus onRequestTrailers(uint32_t) override; + FilterHeadersStatus onResponseHeaders(uint32_t, bool) override; FilterTrailersStatus onResponseTrailers(uint32_t) override; FilterDataStatus onRequestBody(size_t body_buffer_length, bool end_of_stream) override; void onLog() override; @@ -277,6 +278,15 @@ FilterTrailersStatus TestContext::onRequestTrailers(uint32_t) { return FilterTrailersStatus::Continue; } +FilterHeadersStatus TestContext::onResponseHeaders(uint32_t, bool) { + root()->stream_context_id_ = id(); + auto test = root()->test_; + if (test == "headers") { + CHECK_RESULT(addResponseHeader("test-status", "OK")); + } + return FilterHeadersStatus::Continue; +} + FilterTrailersStatus TestContext::onResponseTrailers(uint32_t) { auto value = getResponseTrailer("bogus-trailer"); if (value && value->view() != "") { diff --git a/test/extensions/filters/http/wasm/wasm_filter_test.cc b/test/extensions/filters/http/wasm/wasm_filter_test.cc index fe6932f1c75c..e7d41558991c 100644 --- a/test/extensions/filters/http/wasm/wasm_filter_test.cc +++ b/test/extensions/filters/http/wasm/wasm_filter_test.cc @@ -114,6 +114,12 @@ TEST_P(WasmHttpFilterTest, HeadersOnlyRequestHeadersOnly) { log_(spdlog::level::debug, Eq(absl::string_view("onRequestHeaders 2 headers")))); EXPECT_CALL(filter(), log_(spdlog::level::info, Eq(absl::string_view("header path /")))); EXPECT_CALL(filter(), log_(spdlog::level::warn, Eq(absl::string_view("onDone 2")))); + + // Verify that route cache is cleared when modifying HTTP request headers. + Http::MockStreamDecoderFilterCallbacks decoder_callbacks; + filter().setDecoderFilterCallbacks(decoder_callbacks); + EXPECT_CALL(decoder_callbacks, clearRouteCache()).Times(2); + Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}, {"server", "envoy"}}; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter().decodeHeaders(request_headers, true)); EXPECT_THAT(request_headers.get_("newheader"), Eq("newheadervalue")); @@ -137,6 +143,12 @@ TEST_P(WasmHttpFilterTest, AllHeadersAndTrailers) { log_(spdlog::level::debug, Eq(absl::string_view("onRequestHeaders 2 headers")))); EXPECT_CALL(filter(), log_(spdlog::level::info, Eq(absl::string_view("header path /")))); EXPECT_CALL(filter(), log_(spdlog::level::warn, Eq(absl::string_view("onDone 2")))); + + // Verify that route cache is cleared when modifying HTTP request headers. + Http::MockStreamDecoderFilterCallbacks decoder_callbacks; + filter().setDecoderFilterCallbacks(decoder_callbacks); + EXPECT_CALL(decoder_callbacks, clearRouteCache()).Times(2); + Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}, {"server", "envoy"}}; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter().decodeHeaders(request_headers, false)); EXPECT_THAT(request_headers.get_("newheader"), Eq("newheadervalue")); @@ -145,8 +157,13 @@ TEST_P(WasmHttpFilterTest, AllHeadersAndTrailers) { EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter().decodeTrailers(request_trailers)); Http::MetadataMap request_metadata{}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter().decodeMetadata(request_metadata)); + + // Verify that route cache is NOT cleared when modifying HTTP response headers. + EXPECT_CALL(decoder_callbacks, clearRouteCache()).Times(0); + Http::TestResponseHeaderMapImpl response_headers{}; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter().encodeHeaders(response_headers, false)); + EXPECT_THAT(response_headers.get_("test-status"), Eq("OK")); Http::TestResponseTrailerMapImpl response_trailers{}; EXPECT_EQ(Http::FilterTrailersStatus::StopIteration, filter().encodeTrailers(response_trailers)); Http::MetadataMap response_metadata{}; From f752cffca2b3372567a8ac40b94962bea6121958 Mon Sep 17 00:00:00 2001 From: Lizan Zhou Date: Tue, 15 Dec 2020 15:16:58 -0800 Subject: [PATCH 37/49] wasm: add mathetake to CODEOWNERS (#14427) Signed-off-by: Lizan Zhou --- CODEOWNERS | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 4b3f453b6ccc..7d1e02896b5a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -87,17 +87,17 @@ extensions/filters/common/original_src @snowp @klarose # attribute context /*/extensions/filters/common/expr @kyessenov @yangminzhu @lizan # webassembly access logger extensions -/*/extensions/access_loggers/wasm @PiotrSikora @lizan +/*/extensions/access_loggers/wasm @PiotrSikora @mathetake @lizan # webassembly bootstrap extensions -/*/extensions/bootstrap/wasm @PiotrSikora @lizan +/*/extensions/bootstrap/wasm @PiotrSikora @mathetake @lizan # webassembly http extensions -/*/extensions/filters/http/wasm @PiotrSikora @lizan +/*/extensions/filters/http/wasm @PiotrSikora @mathetake @lizan # webassembly network extensions -/*/extensions/filters/network/wasm @PiotrSikora @lizan +/*/extensions/filters/network/wasm @PiotrSikora @mathetake @lizan # webassembly common extension -/*/extensions/common/wasm @PiotrSikora @lizan +/*/extensions/common/wasm @PiotrSikora @mathetake @lizan # webassembly runtimes -/*/extensions/wasm_runtime/ @PiotrSikora @lizan +/*/extensions/wasm_runtime/ @PiotrSikora @mathetake @lizan # common matcher /*/extensions/common/matcher @mattklein123 @yangminzhu # common crypto extension @@ -124,7 +124,7 @@ extensions/filters/common/original_src @snowp @klarose /*/extensions/stat_sinks/hystrix @trabetti @jmarantz /*/extensions/stat_sinks/metrics_service @ramaraochavali @jmarantz # webassembly stat-sink extensions -/*/extensions/stat_sinks/wasm @PiotrSikora @lizan +/*/extensions/stat_sinks/wasm @PiotrSikora @mathetake @lizan /*/extensions/resource_monitors/injected_resource @eziskind @htuch /*/extensions/resource_monitors/common @eziskind @htuch /*/extensions/resource_monitors/fixed_heap @eziskind @htuch From f19d0256f901f009489c03b72f643373a0733555 Mon Sep 17 00:00:00 2001 From: Matt Klein Date: Tue, 15 Dec 2020 15:17:26 -0800 Subject: [PATCH 38/49] generic conn pool: directly use thread local cluster (#14423) Further cleanup from previous PRs. In the happy path this further eliminates map lookups and simplifies error handling. Signed-off-by: Matt Klein --- include/envoy/router/router.h | 3 ++- include/envoy/tcp/upstream.h | 6 ++--- source/common/router/router.cc | 16 +++++++++---- source/common/router/router.h | 3 ++- source/common/tcp_proxy/tcp_proxy.cc | 8 +++---- source/common/tcp_proxy/tcp_proxy.h | 2 +- source/common/tcp_proxy/upstream.cc | 23 ++++--------------- source/common/tcp_proxy/upstream.h | 4 ++-- .../upstreams/http/generic/config.cc | 11 +++++---- .../upstreams/http/generic/config.h | 2 +- .../extensions/upstreams/http/http/config.cc | 6 +++-- .../extensions/upstreams/http/http/config.h | 2 +- .../upstreams/http/http/upstream_request.h | 13 ++++------- .../extensions/upstreams/http/tcp/config.cc | 6 +++-- source/extensions/upstreams/http/tcp/config.h | 2 +- .../upstreams/http/tcp/upstream_request.h | 13 ++++------- .../upstreams/tcp/generic/config.cc | 19 +++++++-------- .../extensions/upstreams/tcp/generic/config.h | 2 +- .../http/tcp/upstream_request_test.cc | 4 ++-- .../upstreams/tcp/generic/config_test.cc | 11 ++++----- .../upstreams/per_host_upstream_config.h | 8 +++---- 21 files changed, 76 insertions(+), 88 deletions(-) diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index 91d8feb4267e..9d8fd7bc1aa5 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -37,6 +37,7 @@ namespace Envoy { namespace Upstream { class ClusterManager; class LoadBalancerContext; +class ThreadLocalCluster; } // namespace Upstream namespace Router { @@ -1306,7 +1307,7 @@ class GenericConnPoolFactory : public Envoy::Config::TypedFactory { * @return may be null */ virtual GenericConnPoolPtr - createGenericConnPool(Upstream::ClusterManager& cm, bool is_connect, + createGenericConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, const RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const PURE; diff --git a/include/envoy/tcp/upstream.h b/include/envoy/tcp/upstream.h index 71d266b9f1fc..aeb711a08c0e 100644 --- a/include/envoy/tcp/upstream.h +++ b/include/envoy/tcp/upstream.h @@ -10,6 +10,7 @@ namespace Envoy { namespace Upstream { class LoadBalancerContext; +class ThreadLocalCluster; } // namespace Upstream namespace TcpProxy { @@ -116,15 +117,14 @@ class GenericConnPoolFactory : public Envoy::Config::TypedFactory { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig; /* - * @param cluster_name the name of the cluster to use - * @param cm the cluster manager to get the connection pool from + * @param thread_local_cluster the thread local cluster to use for conn pool creation. * @param config the tunneling config, if doing connect tunneling. * @param context the load balancing context for this connection. * @param upstream_callbacks the callbacks to provide to the connection if successfully created. * @return may be null if there is no cluster with the given name. */ virtual GenericConnPoolPtr - createGenericConnPool(const std::string& cluster_name, Upstream::ClusterManager& cm, + createGenericConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, const absl::optional& config, Upstream::LoadBalancerContext* context, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks) const PURE; diff --git a/source/common/router/router.cc b/source/common/router/router.cc index ef3ddd025a46..439a33997b43 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -491,7 +491,7 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, transport_socket_options_ = Network::TransportSocketOptionsUtility::fromFilterState( *callbacks_->streamInfo().filterState()); - std::unique_ptr generic_conn_pool = createConnPool(); + std::unique_ptr generic_conn_pool = createConnPool(*cluster); if (!generic_conn_pool) { sendNoHealthyUpstreamResponse(); @@ -595,7 +595,8 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, return Http::FilterHeadersStatus::StopIteration; } -std::unique_ptr Filter::createConnPool() { +std::unique_ptr +Filter::createConnPool(Upstream::ThreadLocalCluster& thread_local_cluster) { GenericConnPoolFactory* factory = nullptr; if (cluster_->upstreamConfig().has_value()) { factory = &Envoy::Config::Utility::getAndCheckFactory( @@ -607,7 +608,7 @@ std::unique_ptr Filter::createConnPool() { const bool should_tcp_proxy = route_entry_->connectConfig().has_value() && downstream_headers_->getMethodValue() == Http::Headers::get().MethodValues.Connect; - return factory->createGenericConnPool(config_.cm_, should_tcp_proxy, *route_entry_, + return factory->createGenericConnPool(thread_local_cluster, should_tcp_proxy, *route_entry_, callbacks_->streamInfo().protocol(), this); } @@ -1533,7 +1534,14 @@ void Filter::doRetry() { ASSERT(pending_retries_ > 0); pending_retries_--; - std::unique_ptr generic_conn_pool = createConnPool(); + // Clusters can technically get removed by CDS during a retry. Make sure it still exists. + const auto cluster = config_.cm_.getThreadLocalCluster(route_entry_->clusterName()); + std::unique_ptr generic_conn_pool; + if (cluster != nullptr) { + cluster_ = cluster->info(); + generic_conn_pool = createConnPool(*cluster); + } + if (!generic_conn_pool) { sendNoHealthyUpstreamResponse(); cleanup(); diff --git a/source/common/router/router.h b/source/common/router/router.h index 6688d973098d..24cfff0dcb7a 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -462,7 +462,8 @@ class Filter : Logger::Loggable, Event::Dispatcher& dispatcher, TimeSource& time_source, Upstream::ResourcePriority priority) PURE; - std::unique_ptr createConnPool(); + std::unique_ptr + createConnPool(Upstream::ThreadLocalCluster& thread_local_cluster); UpstreamRequestPtr createUpstreamRequest(); void maybeDoShadowing(); diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index 20d6fa297ab0..fe64127a0aa1 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -434,7 +434,7 @@ Network::FilterStatus Filter::initializeUpstreamConnection() { downstreamConnection()->streamInfo().filterState()); } - if (!maybeTunnel(*thread_local_cluster, cluster_name)) { + if (!maybeTunnel(*thread_local_cluster)) { // Either cluster is unknown or there are no healthy hosts. tcpConnPool() increments // cluster->stats().upstream_cx_none_healthy in the latter case. getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::NoHealthyUpstream); @@ -443,7 +443,7 @@ Network::FilterStatus Filter::initializeUpstreamConnection() { return Network::FilterStatus::StopIteration; } -bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster, const std::string& cluster_name) { +bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { GenericConnPoolFactory* factory = nullptr; if (cluster.info()->upstreamConfig().has_value()) { factory = Envoy::Config::Utility::getFactory( @@ -456,8 +456,8 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster, const std::strin return false; } - generic_conn_pool_ = factory->createGenericConnPool( - cluster_name, cluster_manager_, config_->tunnelingConfig(), this, *upstream_callbacks_); + generic_conn_pool_ = factory->createGenericConnPool(cluster, config_->tunnelingConfig(), this, + *upstream_callbacks_); if (generic_conn_pool_) { connecting_ = true; connect_attempts_++; diff --git a/source/common/tcp_proxy/tcp_proxy.h b/source/common/tcp_proxy/tcp_proxy.h index de61eb665139..a5e43e6e8ca5 100644 --- a/source/common/tcp_proxy/tcp_proxy.h +++ b/source/common/tcp_proxy/tcp_proxy.h @@ -345,7 +345,7 @@ class Filter : public Network::ReadFilter, void initialize(Network::ReadFilterCallbacks& callbacks, bool set_connection_stats); Network::FilterStatus initializeUpstreamConnection(); - bool maybeTunnel(Upstream::ThreadLocalCluster& cluster, const std::string& cluster_name); + bool maybeTunnel(Upstream::ThreadLocalCluster& cluster); void onConnectTimeout(); void onDownstreamEvent(Network::ConnectionEvent event); void onUpstreamData(Buffer::Instance& data, bool end_stream); diff --git a/source/common/tcp_proxy/upstream.cc b/source/common/tcp_proxy/upstream.cc index c5bbce249cc7..5b286f64dbc7 100644 --- a/source/common/tcp_proxy/upstream.cc +++ b/source/common/tcp_proxy/upstream.cc @@ -135,17 +135,11 @@ void HttpUpstream::doneWriting() { } } -TcpConnPool::TcpConnPool(const std::string& cluster_name, Upstream::ClusterManager& cluster_manager, +TcpConnPool::TcpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks) : upstream_callbacks_(upstream_callbacks) { - // TODO(mattklein123): Pass thread local cluster into this function, removing an additional - // map lookup and moving the error handling closer to the source (where it is likely already - // done). - const auto thread_local_cluster = cluster_manager.getThreadLocalCluster(cluster_name); - if (thread_local_cluster != nullptr) { - conn_pool_ = thread_local_cluster->tcpConnPool(Upstream::ResourcePriority::Default, context); - } + conn_pool_ = thread_local_cluster.tcpConnPool(Upstream::ResourcePriority::Default, context); } TcpConnPool::~TcpConnPool() { @@ -185,20 +179,13 @@ void TcpConnPool::onPoolReady(Tcp::ConnectionPool::ConnectionDataPtr&& conn_data latched_data->connection().streamInfo().downstreamSslConnection()); } -HttpConnPool::HttpConnPool(const std::string& cluster_name, - Upstream::ClusterManager& cluster_manager, +HttpConnPool::HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, const TunnelingConfig& config, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, Http::CodecClient::Type type) : hostname_(config.hostname()), type_(type), upstream_callbacks_(upstream_callbacks) { - // TODO(mattklein123): Pass thread local cluster into this function, removing an additional - // map lookup and moving the error handling closer to the source (where it is likely already - // done). - const auto thread_local_cluster = cluster_manager.getThreadLocalCluster(cluster_name); - if (thread_local_cluster != nullptr) { - conn_pool_ = thread_local_cluster->httpConnPool(Upstream::ResourcePriority::Default, - absl::nullopt, context); - } + conn_pool_ = thread_local_cluster.httpConnPool(Upstream::ResourcePriority::Default, absl::nullopt, + context); } HttpConnPool::~HttpConnPool() { diff --git a/source/common/tcp_proxy/upstream.h b/source/common/tcp_proxy/upstream.h index 8d8a1ad8ef18..1aed24c8093e 100644 --- a/source/common/tcp_proxy/upstream.h +++ b/source/common/tcp_proxy/upstream.h @@ -14,7 +14,7 @@ namespace TcpProxy { class TcpConnPool : public GenericConnPool, public Tcp::ConnectionPool::Callbacks { public: - TcpConnPool(const std::string& cluster_name, Upstream::ClusterManager& cluster_manager, + TcpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks); ~TcpConnPool() override; @@ -44,7 +44,7 @@ class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callba using TunnelingConfig = envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig; - HttpConnPool(const std::string& cluster_name, Upstream::ClusterManager& cluster_manager, + HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, const TunnelingConfig& config, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, Http::CodecClient::Type type); diff --git a/source/extensions/upstreams/http/generic/config.cc b/source/extensions/upstreams/http/generic/config.cc index 3404f49bf46a..caedb4babd78 100644 --- a/source/extensions/upstreams/http/generic/config.cc +++ b/source/extensions/upstreams/http/generic/config.cc @@ -10,16 +10,17 @@ namespace Http { namespace Generic { Router::GenericConnPoolPtr GenericGenericConnPoolFactory::createGenericConnPool( - Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, + Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, + const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const { if (is_connect) { - auto ret = std::make_unique(cm, is_connect, route_entry, - downstream_protocol, ctx); + auto ret = std::make_unique( + thread_local_cluster, is_connect, route_entry, downstream_protocol, ctx); return (ret->valid() ? std::move(ret) : nullptr); } - auto ret = std::make_unique(cm, is_connect, route_entry, - downstream_protocol, ctx); + auto ret = std::make_unique( + thread_local_cluster, is_connect, route_entry, downstream_protocol, ctx); return (ret->valid() ? std::move(ret) : nullptr); } diff --git a/source/extensions/upstreams/http/generic/config.h b/source/extensions/upstreams/http/generic/config.h index 1c2f1a2f16d3..62f9d72602c7 100644 --- a/source/extensions/upstreams/http/generic/config.h +++ b/source/extensions/upstreams/http/generic/config.h @@ -18,7 +18,7 @@ class GenericGenericConnPoolFactory : public Router::GenericConnPoolFactory { std::string name() const override { return "envoy.filters.connection_pools.http.generic"; } std::string category() const override { return "envoy.upstreams"; } Router::GenericConnPoolPtr - createGenericConnPool(Upstream::ClusterManager& cm, bool is_connect, + createGenericConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const override; diff --git a/source/extensions/upstreams/http/http/config.cc b/source/extensions/upstreams/http/http/config.cc index e8c933f45216..a6084a1219d8 100644 --- a/source/extensions/upstreams/http/http/config.cc +++ b/source/extensions/upstreams/http/http/config.cc @@ -9,10 +9,12 @@ namespace Http { namespace Http { Router::GenericConnPoolPtr HttpGenericConnPoolFactory::createGenericConnPool( - Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, + Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, + const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const { - auto ret = std::make_unique(cm, is_connect, route_entry, downstream_protocol, ctx); + auto ret = std::make_unique(thread_local_cluster, is_connect, route_entry, + downstream_protocol, ctx); return (ret->valid() ? std::move(ret) : nullptr); } diff --git a/source/extensions/upstreams/http/http/config.h b/source/extensions/upstreams/http/http/config.h index 4c6036ddf3b5..e438999e7dac 100644 --- a/source/extensions/upstreams/http/http/config.h +++ b/source/extensions/upstreams/http/http/config.h @@ -18,7 +18,7 @@ class HttpGenericConnPoolFactory : public Router::GenericConnPoolFactory { std::string name() const override { return "envoy.filters.connection_pools.http.http"; } std::string category() const override { return "envoy.upstreams"; } Router::GenericConnPoolPtr - createGenericConnPool(Upstream::ClusterManager& cm, bool is_connect, + createGenericConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const override; diff --git a/source/extensions/upstreams/http/http/upstream_request.h b/source/extensions/upstreams/http/http/upstream_request.h index c2c2755f31fc..a83cbddb3dba 100644 --- a/source/extensions/upstreams/http/http/upstream_request.h +++ b/source/extensions/upstreams/http/http/upstream_request.h @@ -20,18 +20,13 @@ namespace Http { class HttpConnPool : public Router::GenericConnPool, public Envoy::Http::ConnectionPool::Callbacks { public: // GenericConnPool - HttpConnPool(Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, + HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, + const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) { ASSERT(!is_connect); - // TODO(mattklein123): Pass thread local cluster into this function, removing an additional - // map lookup and moving the error handling closer to the source (where it is likely already - // done). - const auto thread_local_cluster = cm.getThreadLocalCluster(route_entry.clusterName()); - if (thread_local_cluster != nullptr) { - conn_pool_ = - thread_local_cluster->httpConnPool(route_entry.priority(), downstream_protocol, ctx); - } + conn_pool_ = + thread_local_cluster.httpConnPool(route_entry.priority(), downstream_protocol, ctx); } ~HttpConnPool() override { ASSERT(conn_pool_stream_handle_ == nullptr, "conn_pool_stream_handle not null"); diff --git a/source/extensions/upstreams/http/tcp/config.cc b/source/extensions/upstreams/http/tcp/config.cc index 15c01f524af7..8f0df971dc9f 100644 --- a/source/extensions/upstreams/http/tcp/config.cc +++ b/source/extensions/upstreams/http/tcp/config.cc @@ -9,10 +9,12 @@ namespace Http { namespace Tcp { Router::GenericConnPoolPtr TcpGenericConnPoolFactory::createGenericConnPool( - Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, + Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, + const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const { - auto ret = std::make_unique(cm, is_connect, route_entry, downstream_protocol, ctx); + auto ret = std::make_unique(thread_local_cluster, is_connect, route_entry, + downstream_protocol, ctx); return (ret->valid() ? std::move(ret) : nullptr); } diff --git a/source/extensions/upstreams/http/tcp/config.h b/source/extensions/upstreams/http/tcp/config.h index 5ff4df42f5b3..784e8f15b502 100644 --- a/source/extensions/upstreams/http/tcp/config.h +++ b/source/extensions/upstreams/http/tcp/config.h @@ -18,7 +18,7 @@ class TcpGenericConnPoolFactory : public Router::GenericConnPoolFactory { std::string name() const override { return "envoy.filters.connection_pools.http.tcp"; } std::string category() const override { return "envoy.upstreams"; } Router::GenericConnPoolPtr - createGenericConnPool(Upstream::ClusterManager& cm, bool is_connect, + createGenericConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const override; diff --git a/source/extensions/upstreams/http/tcp/upstream_request.h b/source/extensions/upstreams/http/tcp/upstream_request.h index 1b9f6a238680..2739620e179a 100644 --- a/source/extensions/upstreams/http/tcp/upstream_request.h +++ b/source/extensions/upstreams/http/tcp/upstream_request.h @@ -21,16 +21,11 @@ namespace Tcp { class TcpConnPool : public Router::GenericConnPool, public Envoy::Tcp::ConnectionPool::Callbacks { public: - TcpConnPool(Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, - absl::optional, Upstream::LoadBalancerContext* ctx) { + TcpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, + const Router::RouteEntry& route_entry, absl::optional, + Upstream::LoadBalancerContext* ctx) { ASSERT(is_connect); - // TODO(mattklein123): Pass thread local cluster into this function, removing an additional - // map lookup and moving the error handling closer to the source (where it is likely already - // done). - const auto thread_local_cluster = cm.getThreadLocalCluster(route_entry.clusterName()); - if (thread_local_cluster != nullptr) { - conn_pool_ = thread_local_cluster->tcpConnPool(Upstream::ResourcePriority::Default, ctx); - } + conn_pool_ = thread_local_cluster.tcpConnPool(route_entry.priority(), ctx); } void newStream(Router::GenericConnectionPoolCallbacks* callbacks) override { callbacks_ = callbacks; diff --git a/source/extensions/upstreams/tcp/generic/config.cc b/source/extensions/upstreams/tcp/generic/config.cc index c8c3a53cfdba..1a71c3c5bc04 100644 --- a/source/extensions/upstreams/tcp/generic/config.cc +++ b/source/extensions/upstreams/tcp/generic/config.cc @@ -12,23 +12,20 @@ namespace Tcp { namespace Generic { TcpProxy::GenericConnPoolPtr GenericConnPoolFactory::createGenericConnPool( - const std::string& cluster_name, Upstream::ClusterManager& cluster_manager, + Upstream::ThreadLocalCluster& thread_local_cluster, const absl::optional& config, Upstream::LoadBalancerContext* context, Envoy::Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks) const { if (config.has_value()) { - auto* cluster = cluster_manager.getThreadLocalCluster(cluster_name); - if (!cluster) { - return nullptr; - } - auto pool_type = ((cluster->info()->features() & Upstream::ClusterInfo::Features::HTTP2) != 0) - ? Http::CodecClient::Type::HTTP2 - : Http::CodecClient::Type::HTTP1; + auto pool_type = + ((thread_local_cluster.info()->features() & Upstream::ClusterInfo::Features::HTTP2) != 0) + ? Http::CodecClient::Type::HTTP2 + : Http::CodecClient::Type::HTTP1; auto ret = std::make_unique( - cluster_name, cluster_manager, context, config.value(), upstream_callbacks, pool_type); + thread_local_cluster, context, config.value(), upstream_callbacks, pool_type); return (ret->valid() ? std::move(ret) : nullptr); } - auto ret = std::make_unique(cluster_name, cluster_manager, context, - upstream_callbacks); + auto ret = + std::make_unique(thread_local_cluster, context, upstream_callbacks); return (ret->valid() ? std::move(ret) : nullptr); } diff --git a/source/extensions/upstreams/tcp/generic/config.h b/source/extensions/upstreams/tcp/generic/config.h index 5ba6171ac691..36d1d76cb2e0 100644 --- a/source/extensions/upstreams/tcp/generic/config.h +++ b/source/extensions/upstreams/tcp/generic/config.h @@ -18,7 +18,7 @@ class GenericConnPoolFactory : public TcpProxy::GenericConnPoolFactory { std::string name() const override { return "envoy.filters.connection_pools.tcp.generic"; } std::string category() const override { return "envoy.upstreams"; } TcpProxy::GenericConnPoolPtr createGenericConnPool( - const std::string& cluster_name, Upstream::ClusterManager& cm, + Upstream::ThreadLocalCluster& thread_local_cluster, const absl::optional& config, Upstream::LoadBalancerContext* context, Envoy::Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks) const override; diff --git a/test/extensions/upstreams/http/tcp/upstream_request_test.cc b/test/extensions/upstreams/http/tcp/upstream_request_test.cc index f921bcbf6336..e0ee478f9306 100644 --- a/test/extensions/upstreams/http/tcp/upstream_request_test.cc +++ b/test/extensions/upstreams/http/tcp/upstream_request_test.cc @@ -40,8 +40,8 @@ class TcpConnPoolTest : public ::testing::Test { NiceMock cm; cm.initializeThreadLocalClusters({"fake_cluster"}); EXPECT_CALL(cm.thread_local_cluster_, tcpConnPool(_, _)).WillOnce(Return(&mock_pool_)); - conn_pool_ = std::make_unique(cm, true, route_entry, Envoy::Http::Protocol::Http11, - nullptr); + conn_pool_ = std::make_unique(cm.thread_local_cluster_, true, route_entry, + Envoy::Http::Protocol::Http11, nullptr); } std::unique_ptr conn_pool_; diff --git a/test/extensions/upstreams/tcp/generic/config_test.cc b/test/extensions/upstreams/tcp/generic/config_test.cc index 844431466216..ffc603614491 100644 --- a/test/extensions/upstreams/tcp/generic/config_test.cc +++ b/test/extensions/upstreams/tcp/generic/config_test.cc @@ -18,18 +18,17 @@ namespace Generic { class TcpConnPoolTest : public ::testing::Test { public: - NiceMock cluster_manager_; - const std::string cluster_name_{"cluster_name"}; + NiceMock thread_local_cluster_; GenericConnPoolFactory factory_; NiceMock callbacks_; }; -TEST_F(TcpConnPoolTest, TestNoMatchingClusterName) { +TEST_F(TcpConnPoolTest, TestNoConnPool) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config; config.set_hostname("host"); - EXPECT_CALL(cluster_manager_, getThreadLocalCluster(_)).WillOnce(Return(nullptr)); - EXPECT_EQ(nullptr, factory_.createGenericConnPool(cluster_name_, cluster_manager_, config, - nullptr, callbacks_)); + EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(nullptr)); + EXPECT_EQ(nullptr, + factory_.createGenericConnPool(thread_local_cluster_, config, nullptr, callbacks_)); } } // namespace Generic diff --git a/test/integration/upstreams/per_host_upstream_config.h b/test/integration/upstreams/per_host_upstream_config.h index 06095235a4a5..bfa8ba595033 100644 --- a/test/integration/upstreams/per_host_upstream_config.h +++ b/test/integration/upstreams/per_host_upstream_config.h @@ -70,11 +70,11 @@ class PerHostHttpUpstream : public Extensions::Upstreams::Http::Http::HttpUpstre class PerHostHttpConnPool : public Extensions::Upstreams::Http::Http::HttpConnPool { public: - PerHostHttpConnPool(Upstream::ClusterManager& cm, bool is_connect, + PerHostHttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) - : HttpConnPool(cm, is_connect, route_entry, downstream_protocol, ctx) {} + : HttpConnPool(thread_local_cluster, is_connect, route_entry, downstream_protocol, ctx) {} void onPoolReady(Envoy::Http::RequestEncoder& callbacks_encoder, Upstream::HostDescriptionConstSharedPtr host, const StreamInfo::StreamInfo& info, @@ -95,7 +95,7 @@ class PerHostGenericConnPoolFactory : public Router::GenericConnPoolFactory { std::string name() const override { return "envoy.filters.connection_pools.http.per_host"; } std::string category() const override { return "envoy.upstreams"; } Router::GenericConnPoolPtr - createGenericConnPool(Upstream::ClusterManager& cm, bool is_connect, + createGenericConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, bool is_connect, const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const override { @@ -104,7 +104,7 @@ class PerHostGenericConnPoolFactory : public Router::GenericConnPoolFactory { return nullptr; } auto upstream_http_conn_pool = std::make_unique( - cm, is_connect, route_entry, downstream_protocol, ctx); + thread_local_cluster, is_connect, route_entry, downstream_protocol, ctx); return (upstream_http_conn_pool->valid() ? std::move(upstream_http_conn_pool) : nullptr); } From 004296ba47b0366c0b21ea9a9a80a71c18c5f852 Mon Sep 17 00:00:00 2001 From: Christoph Pakulski Date: Tue, 15 Dec 2020 18:35:27 -0500 Subject: [PATCH 39/49] Moved starttls integration test to test/extensions/transport_sockets/starttls. (#14425) Signed-off-by: Christoph Pakulski --- .../transport_sockets/starttls/BUILD | 28 +++++++++++++++++++ .../starttls}/starttls_integration_test.cc | 4 +-- .../starttls}/starttls_integration_test.proto | 0 test/integration/BUILD | 26 ----------------- 4 files changed, 30 insertions(+), 28 deletions(-) rename test/{integration => extensions/transport_sockets/starttls}/starttls_integration_test.cc (98%) rename test/{integration => extensions/transport_sockets/starttls}/starttls_integration_test.proto (100%) diff --git a/test/extensions/transport_sockets/starttls/BUILD b/test/extensions/transport_sockets/starttls/BUILD index 6f114205648a..0b38b0b7aa56 100644 --- a/test/extensions/transport_sockets/starttls/BUILD +++ b/test/extensions/transport_sockets/starttls/BUILD @@ -1,6 +1,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_package", + "envoy_proto_library", ) load( "//test/extensions:extensions_build_system.bzl", @@ -24,3 +25,30 @@ envoy_extension_cc_test( "@envoy_api//envoy/extensions/transport_sockets/starttls/v3:pkg_cc_proto", ], ) + +envoy_proto_library( + name = "starttls_integration_proto", + srcs = [":starttls_integration_test.proto"], +) + +envoy_extension_cc_test( + name = "starttls_integration_test", + srcs = [ + "starttls_integration_test.cc", + ], + data = [ + "//test/config/integration/certs", + ], + extension_name = "envoy.transport_sockets.starttls", + # TODO(envoyproxy/windows-dev): Investigate timeout + tags = ["flaky_on_windows"], + deps = [ + ":starttls_integration_proto_cc_proto", + "//source/extensions/filters/network/tcp_proxy:config", + "//source/extensions/transport_sockets/raw_buffer:config", + "//source/extensions/transport_sockets/starttls:config", + "//test/integration:integration_lib", + "//test/test_common:registry_lib", + "@envoy_api//envoy/extensions/transport_sockets/raw_buffer/v3:pkg_cc_proto", + ], +) diff --git a/test/integration/starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc similarity index 98% rename from test/integration/starttls_integration_test.cc rename to test/extensions/transport_sockets/starttls/starttls_integration_test.cc index b4468726bbb7..b88d4b4d8b72 100644 --- a/test/integration/starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc @@ -9,10 +9,10 @@ #include "extensions/transport_sockets/raw_buffer/config.h" #include "test/config/utility.h" +#include "test/extensions/transport_sockets/starttls/starttls_integration_test.pb.h" +#include "test/extensions/transport_sockets/starttls/starttls_integration_test.pb.validate.h" #include "test/integration/integration.h" #include "test/integration/ssl_utility.h" -#include "test/integration/starttls_integration_test.pb.h" -#include "test/integration/starttls_integration_test.pb.validate.h" #include "test/test_common/registry.h" #include "gtest/gtest.h" diff --git a/test/integration/starttls_integration_test.proto b/test/extensions/transport_sockets/starttls/starttls_integration_test.proto similarity index 100% rename from test/integration/starttls_integration_test.proto rename to test/extensions/transport_sockets/starttls/starttls_integration_test.proto diff --git a/test/integration/BUILD b/test/integration/BUILD index d5cc80103e60..94e78b9c5e4a 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1216,32 +1216,6 @@ envoy_cc_test( ], ) -envoy_proto_library( - name = "starttls_integration_proto", - srcs = [":starttls_integration_test.proto"], -) - -envoy_cc_test( - name = "starttls_integration_test", - srcs = [ - "starttls_integration_test.cc", - ], - data = [ - "//test/config/integration/certs", - ], - # TODO(envoyproxy/windows-dev): Investigate timeout - tags = ["flaky_on_windows"], - deps = [ - ":integration_lib", - ":starttls_integration_proto_cc_proto", - "//source/extensions/filters/network/tcp_proxy:config", - "//source/extensions/transport_sockets/raw_buffer:config", - "//source/extensions/transport_sockets/starttls:config", - "//test/test_common:registry_lib", - "@envoy_api//envoy/extensions/transport_sockets/raw_buffer/v3:pkg_cc_proto", - ], -) - envoy_cc_test( name = "tcp_tunneling_integration_test", srcs = [ From 93ee668a690d297ab5e8bd2cbf03771d852ebbda Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 15 Dec 2020 18:40:57 -0500 Subject: [PATCH 40/49] http: alpn upstream (#13922) Signed-off-by: Alyssa Wilk --- .../http/v3/http_protocol_options.proto | 19 ++++ .../http/v4alpha/http_protocol_options.proto | 22 ++++ docs/root/version_history/current.rst | 1 + .../http/v3/http_protocol_options.proto | 19 ++++ .../http/v4alpha/http_protocol_options.proto | 22 ++++ include/envoy/network/transport_socket.h | 6 + include/envoy/upstream/cluster_manager.h | 2 +- include/envoy/upstream/upstream.h | 7 +- source/common/conn_pool/conn_pool_base.cc | 14 ++- source/common/conn_pool/conn_pool_base.h | 19 +++- source/common/http/BUILD | 12 ++ source/common/http/codec_client.cc | 11 +- source/common/http/conn_pool_base.h | 8 ++ source/common/http/http1/conn_pool.cc | 10 +- source/common/http/http1/conn_pool.h | 1 + source/common/http/http2/conn_pool.cc | 10 ++ source/common/http/http2/conn_pool.h | 2 + source/common/http/mixed_conn_pool.cc | 78 +++++++++++++ source/common/http/mixed_conn_pool.h | 32 ++++++ source/common/network/connection_impl_base.cc | 3 - source/common/tcp/conn_pool.cc | 3 +- source/common/tcp/conn_pool.h | 2 + source/common/upstream/BUILD | 1 + .../common/upstream/cluster_manager_impl.cc | 37 +++++-- source/common/upstream/cluster_manager_impl.h | 2 +- source/common/upstream/upstream_impl.cc | 23 +++- source/common/upstream/upstream_impl.h | 2 +- .../quic_filter_manager_connection_impl.h | 6 +- .../transport_sockets/tls/ssl_socket.h | 1 + source/extensions/upstreams/http/config.cc | 11 ++ source/extensions/upstreams/http/config.h | 1 + test/common/conn_pool/conn_pool_base_test.cc | 1 + test/common/http/BUILD | 4 +- test/common/http/codec_client_test.cc | 20 +++- test/common/http/common.h | 4 +- test/common/http/http1/conn_pool_test.cc | 12 +- test/common/http/http2/conn_pool_test.cc | 2 - test/common/http/mixed_conn_pool_test.cc | 58 ++++++++-- test/common/network/connection_impl_test.cc | 46 ++++++++ test/common/upstream/BUILD | 1 + .../upstream/cluster_manager_impl_test.cc | 40 +++++-- test/common/upstream/test_cluster_manager.h | 3 +- test/common/upstream/upstream_impl_test.cc | 31 ++++-- test/config/utility.cc | 27 ++++- test/config/utility.h | 2 +- test/integration/BUILD | 6 + test/integration/alpn_integration_test.cc | 103 ++++++++++++++++++ .../http2_upstream_integration_test.cc | 11 ++ .../http2_upstream_integration_test.h | 5 + test/mocks/network/transport_socket.h | 1 + test/mocks/upstream/cluster_info.cc | 3 +- test/mocks/upstream/cluster_info.h | 3 +- test/mocks/upstream/cluster_manager_factory.h | 3 +- 53 files changed, 682 insertions(+), 91 deletions(-) create mode 100644 source/common/http/mixed_conn_pool.cc create mode 100644 source/common/http/mixed_conn_pool.h create mode 100644 test/integration/alpn_integration_test.cc diff --git a/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto b/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto index 7c5dce7854ee..e7cf42df2387 100644 --- a/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto +++ b/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto @@ -56,6 +56,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // http2_protocol_options: // max_concurrent_streams: 100 // .... [further cluster config] +// [#next-free-field: 6] message HttpProtocolOptions { // If this is used, the cluster will only operate on one of the possible upstream protocols (HTTP/1.1, HTTP/2). // Note that HTTP/2 should generally be used for upstream clusters doing gRPC. @@ -77,6 +78,21 @@ message HttpProtocolOptions { config.core.v3.Http2ProtocolOptions http2_protocol_options = 2; } + // If this is used, the cluster can use either HTTP/1 or HTTP/2, and will use whichever + // protocol is negotiated by ALPN with the upstream. + // Clusters configured with *AutoHttpConfig* will use the highest available + // protocol; HTTP/2 if supported, otherwise HTTP/1. + // If the upstream does not support ALPN, *AutoHttpConfig* will fail over to HTTP/1. + // This can only be used with transport sockets which support ALPN. Using a + // transport socket which does not support ALPN will result in configuration + // failure. The transport layer may be configured with custom ALPN, but the default ALPN + // for the cluster (or if custom ALPN fails) will be "h2,http/1.1". + message AutoHttpConfig { + config.core.v3.Http1ProtocolOptions http_protocol_options = 1; + + config.core.v3.Http2ProtocolOptions http2_protocol_options = 2; + } + // This contains options common across HTTP/1 and HTTP/2 config.core.v3.HttpProtocolOptions common_http_protocol_options = 1; @@ -94,5 +110,8 @@ message HttpProtocolOptions { // This allows switching on protocol based on what protocol the downstream // connection used. UseDownstreamHttpConfig use_downstream_protocol_config = 4; + + // This allows switching on protocol based on ALPN + AutoHttpConfig auto_config = 5; } } diff --git a/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto b/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto index 4c97b8a69f8f..277ceb9aa989 100644 --- a/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto +++ b/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto @@ -57,6 +57,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // http2_protocol_options: // max_concurrent_streams: 100 // .... [further cluster config] +// [#next-free-field: 6] message HttpProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.upstreams.http.v3.HttpProtocolOptions"; @@ -87,6 +88,24 @@ message HttpProtocolOptions { config.core.v4alpha.Http2ProtocolOptions http2_protocol_options = 2; } + // If this is used, the cluster can use either HTTP/1 or HTTP/2, and will use whichever + // protocol is negotiated by ALPN with the upstream. + // Clusters configured with *AutoHttpConfig* will use the highest available + // protocol; HTTP/2 if supported, otherwise HTTP/1. + // If the upstream does not support ALPN, *AutoHttpConfig* will fail over to HTTP/1. + // This can only be used with transport sockets which support ALPN. Using a + // transport socket which does not support ALPN will result in configuration + // failure. The transport layer may be configured with custom ALPN, but the default ALPN + // for the cluster (or if custom ALPN fails) will be "h2,http/1.1". + message AutoHttpConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions.AutoHttpConfig"; + + config.core.v4alpha.Http1ProtocolOptions http_protocol_options = 1; + + config.core.v4alpha.Http2ProtocolOptions http2_protocol_options = 2; + } + // This contains options common across HTTP/1 and HTTP/2 config.core.v4alpha.HttpProtocolOptions common_http_protocol_options = 1; @@ -104,5 +123,8 @@ message HttpProtocolOptions { // This allows switching on protocol based on what protocol the downstream // connection used. UseDownstreamHttpConfig use_downstream_protocol_config = 4; + + // This allows switching on protocol based on ALPN + AutoHttpConfig auto_config = 5; } } diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index f3f3b5e51dd9..a48430ad6f1c 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -69,6 +69,7 @@ New Features * health_check: added option to use :ref:`no_traffic_healthy_interval ` which allows a different no traffic interval when the host is healthy. * http: added HCM :ref:`timeout config field ` to control how long a downstream has to finish sending headers before the stream is cancelled. * http: added frame flood and abuse checks to the upstream HTTP/2 codec. This check is off by default and can be enabled by setting the `envoy.reloadable_features.upstream_http2_flood_checks` runtime key to true. +* http: clusters now support selecting HTTP/1 or HTTP/2 based on ALPN, configurable via :ref:`alpn_config ` in the :ref:`http_protocol_options ` message. * jwt_authn: added support for :ref:`per-route config `. * kill_request: added new :ref:`HTTP kill request filter `. * listener: added an optional :ref:`default filter chain `. If this field is supplied, and none of the :ref:`filter_chains ` matches, this default filter chain is used to serve the connection. diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto b/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto index 7c5dce7854ee..e7cf42df2387 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto @@ -56,6 +56,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // http2_protocol_options: // max_concurrent_streams: 100 // .... [further cluster config] +// [#next-free-field: 6] message HttpProtocolOptions { // If this is used, the cluster will only operate on one of the possible upstream protocols (HTTP/1.1, HTTP/2). // Note that HTTP/2 should generally be used for upstream clusters doing gRPC. @@ -77,6 +78,21 @@ message HttpProtocolOptions { config.core.v3.Http2ProtocolOptions http2_protocol_options = 2; } + // If this is used, the cluster can use either HTTP/1 or HTTP/2, and will use whichever + // protocol is negotiated by ALPN with the upstream. + // Clusters configured with *AutoHttpConfig* will use the highest available + // protocol; HTTP/2 if supported, otherwise HTTP/1. + // If the upstream does not support ALPN, *AutoHttpConfig* will fail over to HTTP/1. + // This can only be used with transport sockets which support ALPN. Using a + // transport socket which does not support ALPN will result in configuration + // failure. The transport layer may be configured with custom ALPN, but the default ALPN + // for the cluster (or if custom ALPN fails) will be "h2,http/1.1". + message AutoHttpConfig { + config.core.v3.Http1ProtocolOptions http_protocol_options = 1; + + config.core.v3.Http2ProtocolOptions http2_protocol_options = 2; + } + // This contains options common across HTTP/1 and HTTP/2 config.core.v3.HttpProtocolOptions common_http_protocol_options = 1; @@ -94,5 +110,8 @@ message HttpProtocolOptions { // This allows switching on protocol based on what protocol the downstream // connection used. UseDownstreamHttpConfig use_downstream_protocol_config = 4; + + // This allows switching on protocol based on ALPN + AutoHttpConfig auto_config = 5; } } diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto b/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto index 4c97b8a69f8f..277ceb9aa989 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto @@ -57,6 +57,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // http2_protocol_options: // max_concurrent_streams: 100 // .... [further cluster config] +// [#next-free-field: 6] message HttpProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.upstreams.http.v3.HttpProtocolOptions"; @@ -87,6 +88,24 @@ message HttpProtocolOptions { config.core.v4alpha.Http2ProtocolOptions http2_protocol_options = 2; } + // If this is used, the cluster can use either HTTP/1 or HTTP/2, and will use whichever + // protocol is negotiated by ALPN with the upstream. + // Clusters configured with *AutoHttpConfig* will use the highest available + // protocol; HTTP/2 if supported, otherwise HTTP/1. + // If the upstream does not support ALPN, *AutoHttpConfig* will fail over to HTTP/1. + // This can only be used with transport sockets which support ALPN. Using a + // transport socket which does not support ALPN will result in configuration + // failure. The transport layer may be configured with custom ALPN, but the default ALPN + // for the cluster (or if custom ALPN fails) will be "h2,http/1.1". + message AutoHttpConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions.AutoHttpConfig"; + + config.core.v4alpha.Http1ProtocolOptions http_protocol_options = 1; + + config.core.v4alpha.Http2ProtocolOptions http2_protocol_options = 2; + } + // This contains options common across HTTP/1 and HTTP/2 config.core.v4alpha.HttpProtocolOptions common_http_protocol_options = 1; @@ -104,5 +123,8 @@ message HttpProtocolOptions { // This allows switching on protocol based on what protocol the downstream // connection used. UseDownstreamHttpConfig use_downstream_protocol_config = 4; + + // This allows switching on protocol based on ALPN + AutoHttpConfig auto_config = 5; } } diff --git a/include/envoy/network/transport_socket.h b/include/envoy/network/transport_socket.h index d90661beb7d6..e71bcbed89d2 100644 --- a/include/envoy/network/transport_socket.h +++ b/include/envoy/network/transport_socket.h @@ -241,6 +241,12 @@ class TransportSocketFactory { * @return bool whether the transport socket will use proxy protocol options. */ virtual bool usesProxyProtocolOptions() const PURE; + + /** + * Returns true if the transport socket created by this factory supports some form of ALPN + * negotiation. + */ + virtual bool supportsAlpn() const { return false; } }; using TransportSocketFactoryPtr = std::unique_ptr; diff --git a/include/envoy/upstream/cluster_manager.h b/include/envoy/upstream/cluster_manager.h index ca4b69a56718..ecaf617b3c70 100644 --- a/include/envoy/upstream/cluster_manager.h +++ b/include/envoy/upstream/cluster_manager.h @@ -353,7 +353,7 @@ class ClusterManagerFactory { */ virtual Http::ConnectionPool::InstancePtr allocateConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host, - ResourcePriority priority, Http::Protocol protocol, + ResourcePriority priority, std::vector& protocol, const Network::ConnectionSocket::OptionsSharedPtr& options, const Network::TransportSocketOptionsSharedPtr& transport_socket_options, ClusterConnectivityState& state) PURE; diff --git a/include/envoy/upstream/upstream.h b/include/envoy/upstream/upstream.h index 5c5c64cf1e4a..64e309e62f96 100644 --- a/include/envoy/upstream/upstream.h +++ b/include/envoy/upstream/upstream.h @@ -717,6 +717,9 @@ class ClusterInfo { static const uint64_t USE_DOWNSTREAM_PROTOCOL = 0x2; // Whether connections should be immediately closed upon health failure. static const uint64_t CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE = 0x4; + // If USE_ALPN and HTTP2 are true, the upstream protocol will be negotiated using ALPN. + // If ALPN is attempted but not supported by the upstream HTTP/1.1 is used. + static const uint64_t USE_ALPN = 0x8; }; virtual ~ClusterInfo() = default; @@ -966,9 +969,9 @@ class ClusterInfo { virtual void createNetworkFilterChain(Network::Connection& connection) const PURE; /** - * Calculate upstream protocol based on features. + * Calculate upstream protocol(s) based on features. */ - virtual Http::Protocol + virtual std::vector upstreamHttpProtocol(absl::optional downstream_protocol) const PURE; /** diff --git a/source/common/conn_pool/conn_pool_base.cc b/source/common/conn_pool/conn_pool_base.cc index 9bd90c190983..b9667b77525e 100644 --- a/source/common/conn_pool/conn_pool_base.cc +++ b/source/common/conn_pool/conn_pool_base.cc @@ -329,6 +329,11 @@ void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view connecting_stream_capacity_ -= client.effectiveConcurrentStreamLimit(); } + if (client.connect_timer_) { + client.connect_timer_->disableTimer(); + client.connect_timer_.reset(); + } + if (event == Network::ConnectionEvent::RemoteClose || event == Network::ConnectionEvent::LocalClose) { state_.decrConnectingStreamCapacity(client.currentUnusedCapacity()); @@ -387,18 +392,15 @@ void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view } else if (event == Network::ConnectionEvent::Connected) { client.conn_connect_ms_->complete(); client.conn_connect_ms_.reset(); - ASSERT(client.state_ == ActiveClient::State::CONNECTING); transitionActiveClientState(client, ActiveClient::State::READY); + // At this point, for the mixed ALPN pool, the client may be deleted. Do not + // refer to client after this point. + onConnected(client); onUpstreamReady(); checkForDrained(); } - - if (client.connect_timer_) { - client.connect_timer_->disableTimer(); - client.connect_timer_.reset(); - } } PendingStream::PendingStream(ConnPoolImplBase& parent) : parent_(parent) { diff --git a/source/common/conn_pool/conn_pool_base.h b/source/common/conn_pool/conn_pool_base.h index 084681fc4cd9..9c911f3948de 100644 --- a/source/common/conn_pool/conn_pool_base.h +++ b/source/common/conn_pool/conn_pool_base.h @@ -49,6 +49,8 @@ class ActiveClient : public LinkedObject, return std::min(remaining_streams_, concurrent_stream_limit_); } + // Returns the application protocol, or absl::nullopt for TCP. + virtual absl::optional protocol() const PURE; uint32_t currentUnusedCapacity() const { return std::min(remaining_streams_, concurrent_stream_limit_ - numActiveStreams()); } @@ -183,6 +185,8 @@ class ConnPoolImplBase : protected Logger::Loggable { bool hasPendingStreams() const { return !pending_streams_.empty(); } protected: + virtual void onConnected(Envoy::ConnectionPool::ActiveClient&) {} + // Creates up to 3 connections, based on the prefetch ratio. void tryCreateNewConnections(); @@ -211,8 +215,13 @@ class ConnPoolImplBase : protected Logger::Loggable { bool hasActiveStreams() const { return num_active_streams_ > 0; } - void decrConnectingStreamCapacity(int32_t delta) { + void incrConnectingStreamCapacity(uint32_t delta) { + state_.incrConnectingStreamCapacity(delta); + connecting_stream_capacity_ += delta; + } + void decrConnectingStreamCapacity(uint32_t delta) { state_.decrConnectingStreamCapacity(delta); + ASSERT(connecting_stream_capacity_ > delta); connecting_stream_capacity_ -= delta; } @@ -241,16 +250,16 @@ class ConnPoolImplBase : protected Logger::Loggable { // Clients that are not ready to handle additional streams because they are CONNECTING. std::list connecting_clients_; + // The number of streams that can be immediately dispatched + // if all CONNECTING connections become connected. + uint32_t connecting_stream_capacity_{0}; + private: std::list pending_streams_; // The number of streams currently attached to clients. uint32_t num_active_streams_{0}; - // The number of streams that can be immediately dispatched - // if all CONNECTING connections become connected. - uint32_t connecting_stream_capacity_{0}; - void onUpstreamReady(); Event::SchedulableCallbackPtr upstream_ready_cb_; }; diff --git a/source/common/http/BUILD b/source/common/http/BUILD index b63f3ce45b80..97963015c4cf 100644 --- a/source/common/http/BUILD +++ b/source/common/http/BUILD @@ -141,6 +141,18 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "mixed_conn_pool", + srcs = ["mixed_conn_pool.cc"], + hdrs = ["mixed_conn_pool.h"], + deps = [ + ":conn_pool_base_lib", + "//source/common/http/http1:conn_pool_lib", + "//source/common/http/http2:conn_pool_lib", + "//source/common/tcp:conn_pool_lib", + ], +) + envoy_cc_library( name = "conn_manager_config_interface", hdrs = ["conn_manager_config.h"], diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index 3c5c16aed654..761cf879a5f0 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -36,8 +36,15 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, connection_->addConnectionCallbacks(*this); connection_->addReadFilter(Network::ReadFilterSharedPtr{new CodecReadFilter(*this)}); - ENVOY_CONN_LOG(debug, "connecting", *connection_); - connection_->connect(); + // In general, codecs are handed new not-yet-connected connections, but in the + // case of ALPN, the codec may be handed an already connected connection. + if (!connection_->connecting()) { + ASSERT(connection_->state() == Network::Connection::State::Open); + connected_ = true; + } else { + ENVOY_CONN_LOG(debug, "connecting", *connection_); + connection_->connect(); + } if (idle_timeout_) { idle_timer_ = dispatcher.createTimer([this]() -> void { onIdleTimeout(); }); diff --git a/source/common/http/conn_pool_base.h b/source/common/http/conn_pool_base.h index 152dc6df86d5..29290e74f496 100644 --- a/source/common/http/conn_pool_base.h +++ b/source/common/http/conn_pool_base.h @@ -101,6 +101,13 @@ class ActiveClient : public Envoy::ConnectionPool::ActiveClient { initialize(data, parent); } + ActiveClient(HttpConnPoolImplBase& parent, uint64_t lifetime_stream_limit, + uint64_t concurrent_stream_limit, Upstream::Host::CreateConnectionData& data) + : Envoy::ConnectionPool::ActiveClient(parent, lifetime_stream_limit, + concurrent_stream_limit) { + initialize(data, parent); + } + void initialize(Upstream::Host::CreateConnectionData& data, HttpConnPoolImplBase& parent) { real_host_description_ = data.host_description_; codec_client_ = parent.createCodecClient(data); @@ -113,6 +120,7 @@ class ActiveClient : public Envoy::ConnectionPool::ActiveClient { &parent_.host()->cluster().stats().bind_errors_, nullptr}); } + absl::optional protocol() const override { return codec_client_->protocol(); } void close() override { codec_client_->close(); } virtual Http::RequestEncoder& newStreamEncoder(Http::ResponseDecoder& response_decoder) PURE; void onEvent(Network::ConnectionEvent event) override { diff --git a/source/common/http/http1/conn_pool.cc b/source/common/http/http1/conn_pool.cc index 61060533cb5c..0a070c28395d 100644 --- a/source/common/http/http1/conn_pool.cc +++ b/source/common/http/http1/conn_pool.cc @@ -33,7 +33,7 @@ ActiveClient::StreamWrapper::~StreamWrapper() { // Upstream connection might be closed right after response is complete. Setting delay=true // here to attach pending requests in next dispatcher loop to handle that case. // https://github.com/envoyproxy/envoy/issues/2715 - parent_.parent().onStreamClosed(parent_, true); + parent_.parent_.onStreamClosed(parent_, true); } void ActiveClient::StreamWrapper::onEncodeComplete() { encode_complete_ = true; } @@ -95,6 +95,14 @@ ActiveClient::ActiveClient(HttpConnPoolImplBase& parent) parent.host()->cluster().stats().upstream_cx_http1_total_.inc(); } +ActiveClient::ActiveClient(HttpConnPoolImplBase& parent, Upstream::Host::CreateConnectionData& data) + : Envoy::Http::ActiveClient( + parent, parent.host()->cluster().maxRequestsPerConnection(), + 1, // HTTP1 always has a concurrent-request-limit of 1 per connection. + data) { + parent.host()->cluster().stats().upstream_cx_http1_total_.inc(); +} + bool ActiveClient::closingWithIncompleteStream() const { return (stream_wrapper_ != nullptr) && (!stream_wrapper_->decode_complete_); } diff --git a/source/common/http/http1/conn_pool.h b/source/common/http/http1/conn_pool.h index e8c5b0705587..d45109ac9327 100644 --- a/source/common/http/http1/conn_pool.h +++ b/source/common/http/http1/conn_pool.h @@ -17,6 +17,7 @@ namespace Http1 { class ActiveClient : public Envoy::Http::ActiveClient { public: ActiveClient(HttpConnPoolImplBase& parent); + ActiveClient(HttpConnPoolImplBase& parent, Upstream::Host::CreateConnectionData& data); // ConnPoolImplBase::ActiveClient bool closingWithIncompleteStream() const override; diff --git a/source/common/http/http2/conn_pool.cc b/source/common/http/http2/conn_pool.cc index 492ebb62e4f6..93af8a2c8fcb 100644 --- a/source/common/http/http2/conn_pool.cc +++ b/source/common/http/http2/conn_pool.cc @@ -64,6 +64,16 @@ ActiveClient::ActiveClient(HttpConnPoolImplBase& parent) parent.host()->cluster().stats().upstream_cx_http2_total_.inc(); } +ActiveClient::ActiveClient(Envoy::Http::HttpConnPoolImplBase& parent, + Upstream::Host::CreateConnectionData& data) + : Envoy::Http::ActiveClient( + parent, maxStreamsPerConnection(parent.host()->cluster().maxRequestsPerConnection()), + parent.host()->cluster().http2Options().max_concurrent_streams().value(), data) { + codec_client_->setCodecClientCallbacks(*this); + codec_client_->setCodecConnectionCallbacks(*this); + parent.host()->cluster().stats().upstream_cx_http2_total_.inc(); +} + bool ActiveClient::closingWithIncompleteStream() const { return closed_with_active_rq_; } RequestEncoder& ActiveClient::newStreamEncoder(ResponseDecoder& response_decoder) { diff --git a/source/common/http/http2/conn_pool.h b/source/common/http/http2/conn_pool.h index 2a277a55ea6c..c4c1b14801e7 100644 --- a/source/common/http/http2/conn_pool.h +++ b/source/common/http/http2/conn_pool.h @@ -19,6 +19,8 @@ class ActiveClient : public CodecClientCallbacks, public Envoy::Http::ActiveClient { public: ActiveClient(HttpConnPoolImplBase& parent); + ActiveClient(Envoy::Http::HttpConnPoolImplBase& parent, + Upstream::Host::CreateConnectionData& data); ~ActiveClient() override = default; // ConnPoolImpl::ActiveClient diff --git a/source/common/http/mixed_conn_pool.cc b/source/common/http/mixed_conn_pool.cc new file mode 100644 index 000000000000..9f1f7992827e --- /dev/null +++ b/source/common/http/mixed_conn_pool.cc @@ -0,0 +1,78 @@ +#include "common/http/mixed_conn_pool.h" + +#include "common/http/codec_client.h" +#include "common/http/http1/conn_pool.h" +#include "common/http/http2/conn_pool.h" +#include "common/http/utility.h" +#include "common/tcp/conn_pool.h" + +namespace Envoy { +namespace Http { + +Envoy::ConnectionPool::ActiveClientPtr HttpConnPoolImplMixed::instantiateActiveClient() { + return std::make_unique(*this, + Envoy::ConnectionPool::ConnPoolImplBase::host(), 1); +} + +CodecClientPtr +HttpConnPoolImplMixed::createCodecClient(Upstream::Host::CreateConnectionData& data) { + auto protocol = + protocol_ == Protocol::Http11 ? CodecClient::Type::HTTP1 : CodecClient::Type::HTTP2; + CodecClientPtr codec{new CodecClientProd(protocol, std::move(data.connection_), + data.host_description_, dispatcher_, random_generator_)}; + return codec; +} + +void HttpConnPoolImplMixed::onConnected(Envoy::ConnectionPool::ActiveClient& client) { + // onConnected is called under the stack of the Network::Connection raising + // the Connected event. The first time it is called, it's called for a TCP + // client, the TCP client is detached from the connection and discarded, and an + // HTTP client is associated with that connection. When the first call returns, the + // Network::Connection will inform the new callback (the HTTP client) that it + // is connected. The early return is to ignore that second call. + if (client.protocol() != absl::nullopt) { + return; + } + + // If an old TLS stack does not negotiate alpn, it likely does not support + // HTTP/2. Fail over to HTTP/1. + protocol_ = Protocol::Http11; + ASSERT(dynamic_cast(&client) != nullptr); + auto tcp_client = static_cast(&client); + std::string alpn = tcp_client->connection_->nextProtocol(); + if (!alpn.empty()) { + if (alpn == Http::Utility::AlpnNames::get().Http11) { + protocol_ = Http::Protocol::Http11; + } else if (alpn == Http::Utility::AlpnNames::get().Http2) { + protocol_ = Http::Protocol::Http2; + } + } + + Upstream::Host::CreateConnectionData data{std::move(tcp_client->connection_), + client.real_host_description_}; + data.connection_->removeConnectionCallbacks(*tcp_client); + data.connection_->removeReadFilter(tcp_client->read_filter_handle_); + dispatcher_.deferredDelete(client.removeFromList(owningList(client.state_))); + + std::unique_ptr new_client; + if (protocol_ == Http::Protocol::Http11) { + new_client = std::make_unique(*this, data); + } else { + new_client = std::make_unique(*this, data); + } + // When we switch from TCP to HTTP clients, the base class onConnectionEvent + // will be called for both, so add to the connecting stream capacity to + // balance it being decremented. + connecting_stream_capacity_ += new_client->effectiveConcurrentStreamLimit(); + // The global capacity is not adjusted in onConnectionEvent, so simply update + // it to reflect any difference between the TCP stream limits and HTTP/2 + // stream limits. + if (new_client->effectiveConcurrentStreamLimit() > 1) { + state_.incrConnectingStreamCapacity(new_client->effectiveConcurrentStreamLimit() - 1); + } + new_client->state_ = ActiveClient::State::CONNECTING; + LinkedList::moveIntoList(std::move(new_client), owningList(new_client->state_)); +} + +} // namespace Http +} // namespace Envoy diff --git a/source/common/http/mixed_conn_pool.h b/source/common/http/mixed_conn_pool.h new file mode 100644 index 000000000000..fe548a348312 --- /dev/null +++ b/source/common/http/mixed_conn_pool.h @@ -0,0 +1,32 @@ +#pragma once + +#include "common/http/conn_pool_base.h" + +namespace Envoy { +namespace Http { + +// An HTTP connection pool which supports both HTTP/1 and HTTP/2 based on ALPN +class HttpConnPoolImplMixed : public HttpConnPoolImplBase { +public: + HttpConnPoolImplMixed(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator, + Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options, + Upstream::ClusterConnectivityState& state) + : HttpConnPoolImplBase(std::move(host), std::move(priority), dispatcher, options, + transport_socket_options, random_generator, state, + {Protocol::Http2, Protocol::Http11}) {} + + Envoy::ConnectionPool::ActiveClientPtr instantiateActiveClient() override; + CodecClientPtr createCodecClient(Upstream::Host::CreateConnectionData& data) override; + + void onConnected(Envoy::ConnectionPool::ActiveClient& client) override; + Http::Protocol protocol() { return protocol_; } + +private: + // Default to HTTP/1, as servers which don't support ALPN are probably HTTP/1 only. + Http::Protocol protocol_ = Protocol::Http11; +}; + +} // namespace Http +} // namespace Envoy diff --git a/source/common/network/connection_impl_base.cc b/source/common/network/connection_impl_base.cc index 775b09be13e4..22803ec56642 100644 --- a/source/common/network/connection_impl_base.cc +++ b/source/common/network/connection_impl_base.cc @@ -31,9 +31,6 @@ void ConnectionImplBase::removeConnectionCallbacks(ConnectionCallbacks& callback void ConnectionImplBase::hashKey(std::vector& hash) const { addIdToHashKey(hash, id()); } void ConnectionImplBase::setConnectionStats(const ConnectionStats& stats) { - ASSERT(!connection_stats_, - "Two network filters are attempting to set connection stats. This indicates an issue " - "with the configured filter chain."); connection_stats_ = std::make_unique(stats); } diff --git a/source/common/tcp/conn_pool.cc b/source/common/tcp/conn_pool.cc index 6ff3f05e03d4..1022b4f7473c 100644 --- a/source/common/tcp/conn_pool.cc +++ b/source/common/tcp/conn_pool.cc @@ -24,7 +24,8 @@ ActiveTcpClient::ActiveTcpClient(Envoy::ConnectionPool::ConnPoolImplBase& parent connection_ = std::move(data.connection_); connection_->addConnectionCallbacks(*this); connection_->detectEarlyCloseWhenReadDisabled(false); - connection_->addReadFilter(std::make_shared(*this)); + read_filter_handle_ = std::make_shared(*this); + connection_->addReadFilter(read_filter_handle_); connection_->setConnectionStats({host->cluster().stats().upstream_cx_rx_bytes_total_, host->cluster().stats().upstream_cx_rx_bytes_buffered_, host->cluster().stats().upstream_cx_tx_bytes_total_, diff --git a/source/common/tcp/conn_pool.h b/source/common/tcp/conn_pool.h index 15fa4bd9aca4..2aa59ea95b14 100644 --- a/source/common/tcp/conn_pool.h +++ b/source/common/tcp/conn_pool.h @@ -94,6 +94,7 @@ class ActiveTcpClient : public Envoy::ConnectionPool::ActiveClient { void onAboveWriteBufferHighWatermark() override { callbacks_->onAboveWriteBufferHighWatermark(); } void onBelowWriteBufferLowWatermark() override { callbacks_->onBelowWriteBufferLowWatermark(); } + absl::optional protocol() const override { return {}; } void close() override { connection_->close(Network::ConnectionCloseType::NoFlush); } uint32_t numActiveStreams() const override { return callbacks_ ? 1 : 0; } bool closingWithIncompleteStream() const override { return false; } @@ -108,6 +109,7 @@ class ActiveTcpClient : public Envoy::ConnectionPool::ActiveClient { } virtual void clearCallbacks(); + std::shared_ptr read_filter_handle_; Envoy::ConnectionPool::ConnPoolImplBase& parent_; ConnectionPool::UpstreamCallbacks* callbacks_{}; Network::ClientConnectionPtr connection_; diff --git a/source/common/upstream/BUILD b/source/common/upstream/BUILD index a3d9738384e1..edfe095f1f8a 100644 --- a/source/common/upstream/BUILD +++ b/source/common/upstream/BUILD @@ -58,6 +58,7 @@ envoy_cc_library( "//source/common/config:version_converter_lib", "//source/common/grpc:async_client_manager_lib", "//source/common/http:async_client_lib", + "//source/common/http:mixed_conn_pool", "//source/common/http/http1:conn_pool_lib", "//source/common/http/http2:conn_pool_lib", "//source/common/network:resolver_lib", diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index b1950599c0dd..9a443640b408 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -28,6 +28,7 @@ #include "common/http/async_client_impl.h" #include "common/http/http1/conn_pool.h" #include "common/http/http2/conn_pool.h" +#include "common/http/mixed_conn_pool.h" #include "common/network/resolver_impl.h" #include "common/network/utility.h" #include "common/protobuf/utility.h" @@ -1361,8 +1362,16 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool( return nullptr; } - auto upstream_protocol = host->cluster().upstreamHttpProtocol(downstream_protocol); - std::vector hash_key = {uint8_t(upstream_protocol)}; + // Right now, HTTP, HTTP/2 and ALPN pools are considered separate. + // We could do better here, and always use the ALPN pool and simply make sure + // we end up on a connection of the correct protocol, but for simplicity we're + // starting with something simpler. + auto upstream_protocols = host->cluster().upstreamHttpProtocol(downstream_protocol); + std::vector hash_key; + hash_key.reserve(upstream_protocols.size()); + for (auto protocol : upstream_protocols) { + hash_key.push_back(uint8_t(protocol)); + } Network::Socket::OptionsSharedPtr upstream_options(std::make_shared()); if (context) { @@ -1399,7 +1408,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool( ConnPoolsContainer::ConnPools::PoolOptRef pool = container.pools_->getPool(priority, hash_key, [&]() { return parent_.parent_.factory_.allocateConnPool( - parent_.thread_local_dispatcher_, host, priority, upstream_protocol, + parent_.thread_local_dispatcher_, host, priority, upstream_protocols, !upstream_options->empty() ? upstream_options : nullptr, have_transport_socket_options ? context->upstreamTransportSocketOptions() : nullptr, parent_.cluster_manager_state_); @@ -1467,20 +1476,26 @@ ClusterManagerPtr ProdClusterManagerFactory::clusterManagerFromProto( Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool( Event::Dispatcher& dispatcher, HostConstSharedPtr host, ResourcePriority priority, - Http::Protocol protocol, const Network::ConnectionSocket::OptionsSharedPtr& options, + std::vector& protocols, + const Network::ConnectionSocket::OptionsSharedPtr& options, const Network::TransportSocketOptionsSharedPtr& transport_socket_options, ClusterConnectivityState& state) { - if (protocol == Http::Protocol::Http2 && + if (protocols.size() == 2 && + ((protocols[0] == Http::Protocol::Http2 && protocols[1] == Http::Protocol::Http11) || + (protocols[1] == Http::Protocol::Http2 && protocols[0] == Http::Protocol::Http11))) { + return std::make_unique(dispatcher, api_.randomGenerator(), host, + priority, options, + transport_socket_options, state); + } + + if (protocols.size() == 1 && protocols[0] == Http::Protocol::Http2 && runtime_.snapshot().featureEnabled("upstream.use_http2", 100)) { return Http::Http2::allocateConnPool(dispatcher, api_.randomGenerator(), host, priority, options, transport_socket_options, state); - } else if (protocol == Http::Protocol::Http3) { - // Quic connection pool is not implemented. - NOT_IMPLEMENTED_GCOVR_EXCL_LINE; - } else { - return Http::Http1::allocateConnPool(dispatcher, api_.randomGenerator(), host, priority, - options, transport_socket_options, state); } + ASSERT(protocols.size() == 1 && protocols[0] == Http::Protocol::Http11); + return Http::Http1::allocateConnPool(dispatcher, api_.randomGenerator(), host, priority, options, + transport_socket_options, state); } Tcp::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateTcpConnPool( diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index e75cc5f7ebc9..2f3fc30f288d 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -61,7 +61,7 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { clusterManagerFromProto(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) override; Http::ConnectionPool::InstancePtr allocateConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host, - ResourcePriority priority, Http::Protocol protocol, + ResourcePriority priority, std::vector& protocol, const Network::ConnectionSocket::OptionsSharedPtr& options, const Network::TransportSocketOptionsSharedPtr& transport_socket_options, ClusterConnectivityState& state) override; diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 6e377579fb88..78df761e6b40 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -101,6 +101,9 @@ parseFeatures(const envoy::config::cluster::v3::Cluster& config, if (config.close_connections_on_host_health_failure()) { features |= ClusterInfoImpl::Features::CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE; } + if (options->use_alpn_) { + features |= ClusterInfoImpl::Features::USE_ALPN; + } return features; } @@ -916,14 +919,16 @@ void ClusterInfoImpl::createNetworkFilterChain(Network::Connection& connection) } } -Http::Protocol +std::vector ClusterInfoImpl::upstreamHttpProtocol(absl::optional downstream_protocol) const { if (downstream_protocol.has_value() && features_ & Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL) { - return downstream_protocol.value(); + return {downstream_protocol.value()}; + } else if (features_ & Upstream::ClusterInfo::Features::USE_ALPN) { + return {Http::Protocol::Http2, Http::Protocol::Http11}; } else { - return (features_ & Upstream::ClusterInfo::Features::HTTP2) ? Http::Protocol::Http2 - : Http::Protocol::Http11; + return {(features_ & Upstream::ClusterInfo::Features::HTTP2) ? Http::Protocol::Http2 + : Http::Protocol::Http11}; } } @@ -940,11 +945,21 @@ ClusterImplBase::ClusterImplBase( factory_context.singletonManager(), factory_context.dispatcher())) { factory_context.setInitManager(init_manager_); auto socket_factory = createTransportSocketFactory(cluster, factory_context); + auto* raw_factory_pointer = socket_factory.get(); + auto socket_matcher = std::make_unique( cluster.transport_socket_matches(), factory_context, socket_factory, *stats_scope); info_ = std::make_unique(cluster, factory_context.clusterManager().bindConfig(), runtime, std::move(socket_matcher), std::move(stats_scope), added_via_api, factory_context); + + if ((info_->features() & ClusterInfoImpl::Features::USE_ALPN) && + !raw_factory_pointer->supportsAlpn()) { + throw EnvoyException( + fmt::format("ALPN configured for cluster {} which has a non-ALPN transport socket: {}", + cluster.name(), cluster.DebugString())); + } + // Create the default (empty) priority set before registering callbacks to // avoid getting an update the first time it is accessed. priority_set_.getOrCreateHostSet(0); diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 8f8f0fea1049..818bbf496afe 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -644,7 +644,7 @@ class ClusterInfoImpl : public ClusterInfo, protected Logger::Loggable edsServiceName() const override { return eds_service_name_; } void createNetworkFilterChain(Network::Connection&) const override; - Http::Protocol + std::vector upstreamHttpProtocol(absl::optional downstream_protocol) const override; Http::Http1::CodecStats& http1CodecStats() const override; diff --git a/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h b/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h index 53535c961423..7057c3edeedf 100644 --- a/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h +++ b/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h @@ -66,10 +66,10 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase { return Network::Connection::State::Closed; } bool connecting() const override { - if (quic_connection_ != nullptr && quic_connection_->connected()) { - return false; + if (quic_connection_ != nullptr && !quic_connection_->IsHandshakeComplete()) { + return true; } - return true; + return false; } void write(Buffer::Instance& /*data*/, bool /*end_stream*/) override { // All writes should be handled by Quic internally. diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index 08c78a0ec8fe..47b13970026f 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -109,6 +109,7 @@ class ClientSslSocketFactory : public Network::TransportSocketFactory, createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override; bool implementsSecureTransport() const override; bool usesProxyProtocolOptions() const override { return false; } + bool supportsAlpn() const override { return true; } // Secret::SecretCallbacks void onAddOrUpdateSecret() override; diff --git a/source/extensions/upstreams/http/config.cc b/source/extensions/upstreams/http/config.cc index 61b5a51c1b35..7ea60a9f4362 100644 --- a/source/extensions/upstreams/http/config.cc +++ b/source/extensions/upstreams/http/config.cc @@ -22,6 +22,9 @@ getHttpOptions(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions if (options.has_use_downstream_protocol_config()) { return options.use_downstream_protocol_config().http_protocol_options(); } + if (options.has_auto_config()) { + return options.auto_config().http_protocol_options(); + } return options.explicit_http_config().http_protocol_options(); } @@ -30,6 +33,9 @@ getHttp2Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOption if (options.has_use_downstream_protocol_config()) { return options.use_downstream_protocol_config().http2_protocol_options(); } + if (options.has_auto_config()) { + return options.auto_config().http2_protocol_options(); + } return options.explicit_http_config().http2_protocol_options(); } @@ -55,7 +61,12 @@ ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl( } use_downstream_protocol_ = true; } + if (options.has_auto_config()) { + use_http2_ = true; + use_alpn_ = true; + } } + ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl( const envoy::config::core::v3::Http1ProtocolOptions& http1_settings, const envoy::config::core::v3::Http2ProtocolOptions& http2_options, diff --git a/source/extensions/upstreams/http/config.h b/source/extensions/upstreams/http/config.h index e55cb18117f0..03ab635cc7c7 100644 --- a/source/extensions/upstreams/http/config.h +++ b/source/extensions/upstreams/http/config.h @@ -42,6 +42,7 @@ class ProtocolOptionsConfigImpl : public Upstream::ProtocolOptionsConfig { bool use_downstream_protocol_{}; bool use_http2_{}; + bool use_alpn_{}; }; class ProtocolOptionsConfigFactory : public Server::Configuration::ProtocolOptionsFactory { diff --git a/test/common/conn_pool/conn_pool_base_test.cc b/test/common/conn_pool/conn_pool_base_test.cc index 0dcc2a02b043..c70632faa871 100644 --- a/test/common/conn_pool/conn_pool_base_test.cc +++ b/test/common/conn_pool/conn_pool_base_test.cc @@ -23,6 +23,7 @@ class TestActiveClient : public ActiveClient { uint64_t id() const override { return 1; } bool closingWithIncompleteStream() const override { return false; } uint32_t numActiveStreams() const override { return active_streams_; } + absl::optional protocol() const override { return absl::nullopt; } uint32_t active_streams_{}; }; diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 4f415a518ba3..5528633aa800 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -403,12 +403,14 @@ envoy_cc_test( name = "mixed_conn_pool_test", srcs = ["mixed_conn_pool_test.cc"], deps = [ - "//source/common/http:conn_pool_base_lib", + ":common_lib", + "//source/common/http:mixed_conn_pool", "//test/common/upstream:utility_lib", "//test/mocks:common_lib", "//test/mocks/buffer:buffer_mocks", "//test/mocks/http:http_mocks", "//test/mocks/local_info:local_info_mocks", + "//test/mocks/network:connection_mocks", "//test/mocks/router:router_mocks", "//test/mocks/runtime:runtime_mocks", "//test/mocks/stats:stats_mocks", diff --git a/test/common/http/codec_client_test.cc b/test/common/http/codec_client_test.cc index 0a16b2d31158..ae92e74fe6f0 100644 --- a/test/common/http/codec_client_test.cc +++ b/test/common/http/codec_client_test.cc @@ -42,9 +42,10 @@ namespace { class CodecClientTest : public Event::TestUsingSimulatedTime, public testing::Test { public: - CodecClientTest() { + void initialize() { connection_ = new NiceMock(); + EXPECT_CALL(*connection_, connecting()).WillOnce(Return(true)); EXPECT_CALL(*connection_, detectEarlyCloseWhenReadDisabled(false)); EXPECT_CALL(*connection_, addConnectionCallbacks(_)).WillOnce(SaveArgAddress(&connection_cb_)); EXPECT_CALL(*connection_, connect()); @@ -79,6 +80,7 @@ class CodecClientTest : public Event::TestUsingSimulatedTime, public testing::Te TEST_F(CodecClientTest, NotCallDetectEarlyCloseWhenReadDiabledUsingHttp3) { auto connection = std::make_unique>(); + EXPECT_CALL(*connection, connecting()).WillOnce(Return(true)); EXPECT_CALL(*connection, detectEarlyCloseWhenReadDisabled(false)).Times(0); EXPECT_CALL(*connection, addConnectionCallbacks(_)).WillOnce(SaveArgAddress(&connection_cb_)); EXPECT_CALL(*connection, connect()); @@ -86,11 +88,13 @@ TEST_F(CodecClientTest, NotCallDetectEarlyCloseWhenReadDiabledUsingHttp3) { auto codec = new Http::MockClientConnection(); EXPECT_CALL(dispatcher_, createTimer_(_)); - auto client = std::make_unique( - CodecClient::Type::HTTP3, std::move(connection), codec, nullptr, host_, dispatcher_); + client_ = std::make_unique(CodecClient::Type::HTTP3, std::move(connection), + codec, nullptr, host_, dispatcher_); } TEST_F(CodecClientTest, BasicHeaderOnlyResponse) { + initialize(); + ResponseDecoder* inner_decoder; NiceMock inner_encoder; EXPECT_CALL(*codec_, newStream(_)) @@ -108,6 +112,7 @@ TEST_F(CodecClientTest, BasicHeaderOnlyResponse) { } TEST_F(CodecClientTest, BasicResponseWithBody) { + initialize(); ResponseDecoder* inner_decoder; NiceMock inner_encoder; EXPECT_CALL(*codec_, newStream(_)) @@ -129,6 +134,7 @@ TEST_F(CodecClientTest, BasicResponseWithBody) { } TEST_F(CodecClientTest, DisconnectBeforeHeaders) { + initialize(); ResponseDecoder* inner_decoder; NiceMock inner_encoder; EXPECT_CALL(*codec_, newStream(_)) @@ -151,6 +157,7 @@ TEST_F(CodecClientTest, DisconnectBeforeHeaders) { } TEST_F(CodecClientTest, IdleTimerWithNoActiveRequests) { + initialize(); ResponseDecoder* inner_decoder; NiceMock inner_encoder; EXPECT_CALL(*codec_, newStream(_)) @@ -183,6 +190,7 @@ TEST_F(CodecClientTest, IdleTimerWithNoActiveRequests) { } TEST_F(CodecClientTest, IdleTimerClientRemoteCloseWithActiveRequests) { + initialize(); ResponseDecoder* inner_decoder; NiceMock inner_encoder; EXPECT_CALL(*codec_, newStream(_)) @@ -207,6 +215,7 @@ TEST_F(CodecClientTest, IdleTimerClientRemoteCloseWithActiveRequests) { } TEST_F(CodecClientTest, IdleTimerClientLocalCloseWithActiveRequests) { + initialize(); ResponseDecoder* inner_decoder; NiceMock inner_encoder; EXPECT_CALL(*codec_, newStream(_)) @@ -230,6 +239,7 @@ TEST_F(CodecClientTest, IdleTimerClientLocalCloseWithActiveRequests) { } TEST_F(CodecClientTest, ProtocolError) { + initialize(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Return(codecProtocolError("protocol error"))); EXPECT_CALL(*connection_, close(Network::ConnectionCloseType::NoFlush)); @@ -240,6 +250,7 @@ TEST_F(CodecClientTest, ProtocolError) { } TEST_F(CodecClientTest, 408Response) { + initialize(); EXPECT_CALL(*codec_, dispatch(_)) .WillOnce(Return(prematureResponseError("", Code::RequestTimeout))); EXPECT_CALL(*connection_, close(Network::ConnectionCloseType::NoFlush)); @@ -251,6 +262,7 @@ TEST_F(CodecClientTest, 408Response) { } TEST_F(CodecClientTest, PrematureResponse) { + initialize(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Return(prematureResponseError("", Code::OK))); EXPECT_CALL(*connection_, close(Network::ConnectionCloseType::NoFlush)); @@ -261,6 +273,7 @@ TEST_F(CodecClientTest, PrematureResponse) { } TEST_F(CodecClientTest, WatermarkPassthrough) { + initialize(); EXPECT_CALL(*codec_, onUnderlyingConnectionAboveWriteBufferHighWatermark()); connection_cb_->onAboveWriteBufferHighWatermark(); @@ -269,6 +282,7 @@ TEST_F(CodecClientTest, WatermarkPassthrough) { } TEST_F(CodecClientTest, SSLConnectionInfo) { + initialize(); std::string session_id = "D62A523A65695219D46FE1FFE285A4C371425ACE421B110B5B8D11D3EB4D5F0B"; auto connection_info = std::make_shared>(); ON_CALL(*connection_info, sessionId()).WillByDefault(ReturnRef(session_id)); diff --git a/test/common/http/common.h b/test/common/http/common.h index dd07fc25d898..0be3d9743a6f 100644 --- a/test/common/http/common.h +++ b/test/common/http/common.h @@ -53,8 +53,8 @@ struct ConnPoolCallbacks : public Http::ConnectionPool::Callbacks { } ConnectionPool::PoolFailureReason reason_; - ReadyWatcher pool_failure_; - ReadyWatcher pool_ready_; + testing::NiceMock pool_failure_; + testing::NiceMock pool_ready_; Http::RequestEncoder* outer_encoder_{}; Upstream::HostDescriptionConstSharedPtr host_; }; diff --git a/test/common/http/http1/conn_pool_test.cc b/test/common/http/http1/conn_pool_test.cc index 9b6937abe3b4..9efa5bb181c7 100644 --- a/test/common/http/http1/conn_pool_test.cc +++ b/test/common/http/http1/conn_pool_test.cc @@ -184,8 +184,8 @@ struct ActiveTestRequest { } if (type == Type::CreateConnection) { - expectNewStream(); EXPECT_CALL(*parent_.conn_pool_->test_clients_[client_index_].connect_timer_, disableTimer()); + expectNewStream(); parent.conn_pool_->test_clients_[client_index_].connection_->raiseEvent( Network::ConnectionEvent::Connected); } @@ -443,8 +443,8 @@ TEST_F(Http1ConnPoolImplTest, ConnectFailure) { Http::ConnectionPool::Cancellable* handle = conn_pool_->newStream(outer_decoder, callbacks); EXPECT_NE(nullptr, handle); - EXPECT_CALL(callbacks.pool_failure_, ready()); EXPECT_CALL(*conn_pool_->test_clients_[0].connect_timer_, disableTimer()); + EXPECT_CALL(callbacks.pool_failure_, ready()); conn_pool_->test_clients_[0].connection_->raiseEvent(Network::ConnectionEvent::RemoteClose); EXPECT_CALL(*conn_pool_, onClientDestroy()); dispatcher_.clearDeferredDeleteList(); @@ -480,22 +480,22 @@ TEST_F(Http1ConnPoolImplTest, MeasureConnectTime) { // Move time forward, signal that the first connect completed and verify the time to connect. uint64_t upstream_cx_connect_ms1 = 0; simulated_time.advanceTimeWait(std::chrono::milliseconds(sleep2_ms)); + EXPECT_CALL(*conn_pool_->test_clients_[0].connect_timer_, disableTimer()); EXPECT_CALL(cluster_->stats_store_, deliverHistogramToSinks(Property(&Stats::Metric::name, "upstream_cx_connect_ms"), _)) .WillOnce(SaveArg<1>(&upstream_cx_connect_ms1)); r1.expectNewStream(); - EXPECT_CALL(*conn_pool_->test_clients_[0].connect_timer_, disableTimer()); conn_pool_->test_clients_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); EXPECT_EQ(sleep1_ms + sleep2_ms, upstream_cx_connect_ms1); // Move time forward, signal that the second connect completed and verify the time to connect. uint64_t upstream_cx_connect_ms2 = 0; simulated_time.advanceTimeWait(std::chrono::milliseconds(sleep3_ms)); + EXPECT_CALL(*conn_pool_->test_clients_[1].connect_timer_, disableTimer()); EXPECT_CALL(cluster_->stats_store_, deliverHistogramToSinks(Property(&Stats::Metric::name, "upstream_cx_connect_ms"), _)) .WillOnce(SaveArg<1>(&upstream_cx_connect_ms2)); r2.expectNewStream(); - EXPECT_CALL(*conn_pool_->test_clients_[1].connect_timer_, disableTimer()); conn_pool_->test_clients_[1].connection_->raiseEvent(Network::ConnectionEvent::Connected); EXPECT_EQ(sleep2_ms + sleep3_ms, upstream_cx_connect_ms2); @@ -1066,10 +1066,10 @@ TEST_F(Http1ConnPoolImplTest, RemoteCloseToCompleteResponse) { NiceMock request_encoder; ResponseDecoder* inner_decoder; + EXPECT_CALL(*conn_pool_->test_clients_[0].connect_timer_, disableTimer()); EXPECT_CALL(*conn_pool_->test_clients_[0].codec_, newStream(_)) .WillOnce(DoAll(SaveArgAddress(&inner_decoder), ReturnRef(request_encoder))); EXPECT_CALL(callbacks.pool_ready_, ready()); - EXPECT_CALL(*conn_pool_->test_clients_[0].connect_timer_, disableTimer()); conn_pool_->test_clients_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); EXPECT_TRUE( @@ -1192,10 +1192,10 @@ TEST_F(Http1ConnPoolDestructImplTest, CbAfterConnPoolDestroyed) { NiceMock request_encoder; ResponseDecoder* inner_decoder; + EXPECT_CALL(*conn_pool_->test_clients_[0].connect_timer_, disableTimer()); EXPECT_CALL(*conn_pool_->test_clients_[0].codec_, newStream(_)) .WillOnce(DoAll(SaveArgAddress(&inner_decoder), ReturnRef(request_encoder))); EXPECT_CALL(callbacks.pool_ready_, ready()); - EXPECT_CALL(*conn_pool_->test_clients_[0].connect_timer_, disableTimer()); conn_pool_->test_clients_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected); EXPECT_TRUE( diff --git a/test/common/http/http2/conn_pool_test.cc b/test/common/http/http2/conn_pool_test.cc index f933b7817930..d8f41b263dd7 100644 --- a/test/common/http/http2/conn_pool_test.cc +++ b/test/common/http/http2/conn_pool_test.cc @@ -242,7 +242,6 @@ class ActiveTestRequest { }; void Http2ConnPoolImplTest::expectClientConnect(size_t index) { - EXPECT_CALL(*test_clients_[index].connect_timer_, disableTimer()); test_clients_[index].connection_->raiseEvent(Network::ConnectionEvent::Connected); } @@ -260,7 +259,6 @@ void Http2ConnPoolImplTest::expectStreamConnect(size_t index, ActiveTestRequest& void Http2ConnPoolImplTest::expectClientReset(size_t index, ActiveTestRequest& r, bool local_failure) { expectStreamReset(r); - EXPECT_CALL(*test_clients_[0].connect_timer_, disableTimer()); if (local_failure) { test_clients_[index].connection_->raiseEvent(Network::ConnectionEvent::LocalClose); EXPECT_EQ(r.callbacks_.reason_, ConnectionPool::PoolFailureReason::LocalConnectionFailure); diff --git a/test/common/http/mixed_conn_pool_test.cc b/test/common/http/mixed_conn_pool_test.cc index 9e0e00a88550..e5fa9c30887b 100644 --- a/test/common/http/mixed_conn_pool_test.cc +++ b/test/common/http/mixed_conn_pool_test.cc @@ -1,11 +1,14 @@ #include -#include "common/http/conn_pool_base.h" +#include "common/http/mixed_conn_pool.h" #include "common/http/utility.h" +#include "test/common/http/common.h" #include "test/common/upstream/utility.h" #include "test/mocks/common.h" #include "test/mocks/event/mocks.h" +#include "test/mocks/http/stream_decoder.h" +#include "test/mocks/network/connection.h" #include "test/mocks/runtime/mocks.h" #include "test/mocks/upstream/cluster_info.h" #include "test/test_common/simulated_time_system.h" @@ -14,23 +17,19 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::Return; + namespace Envoy { namespace Http { namespace { -// TODO(alyssawilk) replace this with the MixedConnectionPool once it lands. -class ConnPoolImplForTest : public Event::TestUsingSimulatedTime, public HttpConnPoolImplBase { +class ConnPoolImplForTest : public Event::TestUsingSimulatedTime, public HttpConnPoolImplMixed { public: ConnPoolImplForTest(Event::MockDispatcher& dispatcher, Upstream::ClusterConnectivityState& state, Random::RandomGenerator& random, Upstream::ClusterInfoConstSharedPtr cluster) - : HttpConnPoolImplBase(Upstream::makeTestHost(cluster, "tcp://127.0.0.1:9000", simTime()), - Upstream::ResourcePriority::Default, dispatcher, nullptr, nullptr, - random, state, {Http::Protocol::Http2, Http::Protocol::Http11}) {} - - Envoy::ConnectionPool::ActiveClientPtr instantiateActiveClient() override { return nullptr; } - CodecClientPtr createCodecClient(Upstream::Host::CreateConnectionData&) override { - return nullptr; - } + : HttpConnPoolImplMixed(dispatcher, random, + Upstream::makeTestHost(cluster, "tcp://127.0.0.1:9000", simTime()), + Upstream::ResourcePriority::Default, nullptr, nullptr, state) {} }; /** @@ -53,6 +52,9 @@ class MixedConnPoolImplTest : public testing::Test { std::unique_ptr conn_pool_; NiceMock runtime_; NiceMock random_; + NiceMock* mock_upstream_ready_cb_; + + void testAlpnHandshake(absl::optional protocol); }; TEST_F(MixedConnPoolImplTest, AlpnTest) { @@ -62,6 +64,40 @@ TEST_F(MixedConnPoolImplTest, AlpnTest) { EXPECT_EQ(fallback[1], Http::Utility::AlpnNames::get().Http11); } +void MixedConnPoolImplTest::testAlpnHandshake(absl::optional protocol) { + NiceMock callbacks_; + + auto* connection = new NiceMock(); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce(Return(connection)); + NiceMock decoder; + conn_pool_->newStream(decoder, callbacks_); + + std::string next_protocol = ""; + if (protocol.has_value()) { + next_protocol = (protocol.value() == Protocol::Http11 ? Http::Utility::AlpnNames::get().Http11 + : Http::Utility::AlpnNames::get().Http2); + } + EXPECT_CALL(*connection, nextProtocol()).WillOnce(Return(next_protocol)); + + connection->raiseEvent(Network::ConnectionEvent::Connected); + if (!protocol.has_value()) { + EXPECT_EQ(Protocol::Http11, conn_pool_->protocol()); + } else { + EXPECT_EQ(protocol.value(), conn_pool_->protocol()); + } + + conn_pool_->drainConnections(); + connection->raiseEvent(Network::ConnectionEvent::RemoteClose); + dispatcher_.clearDeferredDeleteList(); + conn_pool_.reset(); +} + +TEST_F(MixedConnPoolImplTest, BasicNoAlpnHandshake) { testAlpnHandshake({}); } + +TEST_F(MixedConnPoolImplTest, Http1AlpnHandshake) { testAlpnHandshake(Protocol::Http11); } + +TEST_F(MixedConnPoolImplTest, Http2AlpnHandshake) { testAlpnHandshake(Protocol::Http2); } + } // namespace } // namespace Http } // namespace Envoy diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index 6db5f0af50a6..279e2f78d9ab 100644 --- a/test/common/network/connection_impl_test.cc +++ b/test/common/network/connection_impl_test.cc @@ -321,6 +321,52 @@ TEST_P(ConnectionImplTest, CloseDuringConnectCallback) { dispatcher_->run(Event::Dispatcher::RunType::Block); } +TEST_P(ConnectionImplTest, UnregisterRegisterDuringConnectCallback) { + setUpBasicConnection(); + + NiceMock upstream_callbacks_; + // Verify the code path in the mixed connection pool, where the original + // network callback is unregistered when Connected is raised, and a new + // callback is registered. + // event. + int expected_callbacks = 2; + client_connection_->connect(); + read_filter_ = std::make_shared>(); + EXPECT_CALL(listener_callbacks_, onAccept_(_)) + .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { + server_connection_ = dispatcher_->createServerConnection( + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + server_connection_->addConnectionCallbacks(server_callbacks_); + server_connection_->addReadFilter(read_filter_); + + expected_callbacks--; + if (expected_callbacks == 0) { + dispatcher_->exit(); + } + })); + EXPECT_CALL(client_callbacks_, onEvent(ConnectionEvent::Connected)) + .WillOnce(Invoke([&](Network::ConnectionEvent) -> void { + expected_callbacks--; + // Register the new callback. It should immediately get the Connected + // event without an extra dispatch loop. + EXPECT_CALL(upstream_callbacks_, onEvent(ConnectionEvent::Connected)); + client_connection_->addConnectionCallbacks(upstream_callbacks_); + // Remove the old connection callbacks, to regression test removal + // under the stack of onEvent. + client_connection_->removeConnectionCallbacks(client_callbacks_); + if (expected_callbacks == 0) { + dispatcher_->exit(); + } + })); + dispatcher_->run(Event::Dispatcher::RunType::Block); + + // Swap the callbacks back as disconnect() expects client_callbacks_ to be + // registered. + client_connection_->removeConnectionCallbacks(upstream_callbacks_); + client_connection_->addConnectionCallbacks(client_callbacks_); + disconnect(true); +} + TEST_P(ConnectionImplTest, ImmediateConnectError) { dispatcher_ = api_->allocateDispatcher("test_thread"); diff --git a/test/common/upstream/BUILD b/test/common/upstream/BUILD index bdfaf94d290a..b0822e79a1ce 100644 --- a/test/common/upstream/BUILD +++ b/test/common/upstream/BUILD @@ -40,6 +40,7 @@ envoy_cc_test( deps = [ ":test_cluster_manager", "//source/common/router:context_lib", + "//source/extensions/transport_sockets/tls:config", "//test/mocks/upstream:cds_api_mocks", "//test/mocks/upstream:cluster_priority_set_mocks", "//test/mocks/upstream:cluster_real_priority_set_mocks", diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index a52eb1cfbb25..d957311d5e34 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -4,8 +4,11 @@ #include "envoy/config/cluster/v3/cluster.pb.validate.h" #include "envoy/config/core/v3/base.pb.h" +#include "common/network/raw_buffer_socket.h" #include "common/router/context_impl.h" +#include "extensions/transport_sockets/raw_buffer/config.h" + #include "test/common/upstream/test_cluster_manager.h" #include "test/mocks/upstream/cds_api.h" #include "test/mocks/upstream/cluster_priority_set.h" @@ -160,20 +163,43 @@ envoy::config::bootstrap::v3::Bootstrap defaultConfig() { return parseBootstrapFromV3Yaml(yaml); } -TEST_F(ClusterManagerImplTest, MultipleProtocolClusterFail) { +class AlpnSocketFactory : public Network::RawBufferSocketFactory { +public: + bool supportsAlpn() const override { return true; } +}; + +class AlpnTestConfigFactory + : public Envoy::Extensions::TransportSockets::RawBuffer::UpstreamRawBufferSocketFactory { +public: + std::string name() const override { return "envoy.transport_sockets.alpn"; } + Network::TransportSocketFactoryPtr + createTransportSocketFactory(const Protobuf::Message&, + Server::Configuration::TransportSocketFactoryContext&) override { + return std::make_unique(); + } +}; + +TEST_F(ClusterManagerImplTest, MultipleProtocolClusterAlpn) { + AlpnTestConfigFactory alpn_factory; + Registry::InjectFactory + registered_factory(alpn_factory); + const std::string yaml = R"EOF( static_resources: clusters: - name: http12_cluster connect_timeout: 0.250s lb_policy: ROUND_ROBIN - http2_protocol_options: {} - http_protocol_options: {} + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + auto_config: + http2_protocol_options: {} + http_protocol_options: {} + transport_socket: + name: envoy.transport_sockets.alpn )EOF"; - EXPECT_THROW_WITH_MESSAGE( - create(parseBootstrapFromV3Yaml(yaml)), EnvoyException, - "cluster: Both HTTP1 and HTTP2 options may only be configured with non-default " - "'protocol_selection' values"); + create(parseBootstrapFromV3Yaml(yaml)); } TEST_F(ClusterManagerImplTest, MultipleHealthCheckFail) { diff --git a/test/common/upstream/test_cluster_manager.h b/test/common/upstream/test_cluster_manager.h index ea4fafa1bf53..c72023ebac56 100644 --- a/test/common/upstream/test_cluster_manager.h +++ b/test/common/upstream/test_cluster_manager.h @@ -78,7 +78,8 @@ class TestClusterManagerFactory : public ClusterManagerFactory { } Http::ConnectionPool::InstancePtr - allocateConnPool(Event::Dispatcher&, HostConstSharedPtr host, ResourcePriority, Http::Protocol, + allocateConnPool(Event::Dispatcher&, HostConstSharedPtr host, ResourcePriority, + std::vector&, const Network::ConnectionSocket::OptionsSharedPtr& options, const Network::TransportSocketOptionsSharedPtr& transport_socket_options, ClusterConnectivityState& state) override { diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index f52a5cdb5250..27d050861af2 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -3003,9 +3003,12 @@ TEST_F(ClusterInfoImplTest, UseDownstreamHttpProtocol) { auto cluster = makeCluster(yaml); - EXPECT_EQ(Http::Protocol::Http10, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http10)); - EXPECT_EQ(Http::Protocol::Http11, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http11)); - EXPECT_EQ(Http::Protocol::Http2, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http2)); + EXPECT_EQ(Http::Protocol::Http10, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http10})[0]); + EXPECT_EQ(Http::Protocol::Http11, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http11})[0]); + EXPECT_EQ(Http::Protocol::Http2, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http2})[0]); } TEST_F(ClusterInfoImplTest, UpstreamHttp2Protocol) { @@ -3019,10 +3022,13 @@ TEST_F(ClusterInfoImplTest, UpstreamHttp2Protocol) { auto cluster = makeCluster(yaml); - EXPECT_EQ(Http::Protocol::Http2, cluster->info()->upstreamHttpProtocol(absl::nullopt)); - EXPECT_EQ(Http::Protocol::Http2, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http10)); - EXPECT_EQ(Http::Protocol::Http2, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http11)); - EXPECT_EQ(Http::Protocol::Http2, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http2)); + EXPECT_EQ(Http::Protocol::Http2, cluster->info()->upstreamHttpProtocol(absl::nullopt)[0]); + EXPECT_EQ(Http::Protocol::Http2, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http10})[0]); + EXPECT_EQ(Http::Protocol::Http2, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http11})[0]); + EXPECT_EQ(Http::Protocol::Http2, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http2})[0]); } TEST_F(ClusterInfoImplTest, UpstreamHttp11Protocol) { @@ -3035,10 +3041,13 @@ TEST_F(ClusterInfoImplTest, UpstreamHttp11Protocol) { auto cluster = makeCluster(yaml); - EXPECT_EQ(Http::Protocol::Http11, cluster->info()->upstreamHttpProtocol(absl::nullopt)); - EXPECT_EQ(Http::Protocol::Http11, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http10)); - EXPECT_EQ(Http::Protocol::Http11, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http11)); - EXPECT_EQ(Http::Protocol::Http11, cluster->info()->upstreamHttpProtocol(Http::Protocol::Http2)); + EXPECT_EQ(Http::Protocol::Http11, cluster->info()->upstreamHttpProtocol(absl::nullopt)[0]); + EXPECT_EQ(Http::Protocol::Http11, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http10})[0]); + EXPECT_EQ(Http::Protocol::Http11, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http11})[0]); + EXPECT_EQ(Http::Protocol::Http11, + cluster->info()->upstreamHttpProtocol({Http::Protocol::Http2})[0]); } // Validate empty singleton for HostsPerLocalityImpl. diff --git a/test/config/utility.cc b/test/config/utility.cc index 78316eac7d03..07ef93922d87 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -655,14 +655,37 @@ void ConfigHelper::applyConfigModifiers() { config_modifiers_.clear(); } -void ConfigHelper::configureUpstreamTls() { - addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { +void ConfigHelper::configureUpstreamTls(bool use_alpn) { + addConfigModifier([use_alpn](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto* cluster = bootstrap.mutable_static_resources()->mutable_clusters(0); ConfigHelper::HttpProtocolOptions protocol_options; protocol_options.mutable_upstream_http_protocol_options()->set_auto_sni(true); ConfigHelper::setProtocolOptions(*cluster, protocol_options); + if (use_alpn) { + ConfigHelper::HttpProtocolOptions new_protocol_options; + + HttpProtocolOptions old_protocol_options = + MessageUtil::anyConvert( + (*cluster->mutable_typed_extension_protocol_options()) + ["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"]); + protocol_options.MergeFrom(old_protocol_options); + + new_protocol_options = old_protocol_options; + new_protocol_options.clear_explicit_http_config(); + new_protocol_options.mutable_auto_config(); + if (old_protocol_options.explicit_http_config().has_http_protocol_options()) { + new_protocol_options.mutable_auto_config()->mutable_http_protocol_options()->MergeFrom( + old_protocol_options.explicit_http_config().http_protocol_options()); + } else if (old_protocol_options.explicit_http_config().has_http2_protocol_options()) { + new_protocol_options.mutable_auto_config()->mutable_http2_protocol_options()->MergeFrom( + old_protocol_options.explicit_http_config().http2_protocol_options()); + } + (*cluster->mutable_typed_extension_protocol_options()) + ["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"] + .PackFrom(new_protocol_options); + } envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; auto* validation_context = tls_context.mutable_common_tls_context()->mutable_validation_context(); diff --git a/test/config/utility.h b/test/config/utility.h index 0649e1f6d8c6..65e20e0a9052 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -255,7 +255,7 @@ class ConfigHelper { void applyConfigModifiers(); // Configure Envoy to do TLS to upstream. - void configureUpstreamTls(); + void configureUpstreamTls(bool use_alpn = false); // Skip validation that ensures that all upstream ports are referenced by the // configuration generated in ConfigHelper::finalize. diff --git a/test/integration/BUILD b/test/integration/BUILD index 94e78b9c5e4a..036286910f1d 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -70,6 +70,12 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "alpn_integration_test", + srcs = ["alpn_integration_test.cc"], + deps = [":http_integration_lib"], +) + envoy_cc_test( name = "api_listener_integration_test", srcs = ["api_listener_integration_test.cc"], diff --git a/test/integration/alpn_integration_test.cc b/test/integration/alpn_integration_test.cc new file mode 100644 index 000000000000..63c645f1e379 --- /dev/null +++ b/test/integration/alpn_integration_test.cc @@ -0,0 +1,103 @@ +#include "test/integration/autonomous_upstream.h" +#include "test/integration/http_integration.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace { + +class AlpnIntegrationTest : public testing::TestWithParam, + public HttpIntegrationTest { +public: + AlpnIntegrationTest() : HttpIntegrationTest(Http::CodecClient::Type::HTTP2, GetParam()) {} + + void SetUp() override { + autonomous_upstream_ = true; + setUpstreamCount(2); + setDownstreamProtocol(Http::CodecClient::Type::HTTP2); + + upstream_tls_ = true; + config_helper_.configureUpstreamTls(true); + config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* static_resources = bootstrap.mutable_static_resources(); + auto* cluster = static_resources->mutable_clusters(0); + auto* load_assignment = cluster->mutable_load_assignment(); + load_assignment->set_cluster_name(cluster->name()); + auto* locality = load_assignment->add_endpoints(); + locality->set_priority(0); + locality->mutable_locality()->set_region("region"); + locality->add_lb_endpoints()->mutable_endpoint()->MergeFrom( + ConfigHelper::buildEndpoint(Network::Test::getLoopbackAddressString(version_))); + }); + } + void createUpstreams() override { + for (uint32_t i = 0; i < fake_upstreams_count_; ++i) { + setUpstreamProtocol(protocols_[i]); + Network::TransportSocketFactoryPtr factory = createUpstreamTlsContext(); + auto endpoint = upstream_address_fn_(i); + auto config = upstreamConfig(); + config.upstream_protocol_ = protocols_[i]; + fake_upstreams_.emplace_back(new AutonomousUpstream(std::move(factory), endpoint, config, + autonomous_allow_incomplete_streams_)); + } + } + std::vector protocols_; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, AlpnIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +TEST_P(AlpnIntegrationTest, Http2) { + setUpstreamProtocol(FakeHttpConnection::Type::HTTP2); + protocols_ = {FakeHttpConnection::Type::HTTP2, FakeHttpConnection::Type::HTTP2}; + initialize(); + + codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().Status()->value().getStringView()); +} + +TEST_P(AlpnIntegrationTest, Http1) { + setUpstreamProtocol(FakeHttpConnection::Type::HTTP1); + protocols_ = {FakeHttpConnection::Type::HTTP1, FakeHttpConnection::Type::HTTP1}; + initialize(); + + codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().Status()->value().getStringView()); +} + +TEST_P(AlpnIntegrationTest, Mixed) { + protocols_ = {FakeHttpConnection::Type::HTTP1, FakeHttpConnection::Type::HTTP2}; + initialize(); + + codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); + + // Kick off two simultaneous requests, to ensure two upstream connections are + // created. + auto encoder_decoder1 = codec_client_->startRequest(default_request_headers_); + auto& encoder1 = encoder_decoder1.first; + auto& response1 = encoder_decoder1.second; + + auto encoder_decoder2 = codec_client_->startRequest(default_request_headers_); + auto& encoder2 = encoder_decoder2.first; + auto& response2 = encoder_decoder2.second; + + // Finish both streams to ensure both responses come through. + Buffer::OwnedImpl data(""); + encoder1.encodeData(data, true); + encoder2.encodeData(data, true); + + response1->waitForEndStream(); + response2->waitForEndStream(); + EXPECT_EQ("200", response1->headers().Status()->value().getStringView()); + EXPECT_EQ("200", response2->headers().Status()->value().getStringView()); +} + +} // namespace +} // namespace Envoy diff --git a/test/integration/http2_upstream_integration_test.cc b/test/integration/http2_upstream_integration_test.cc index fe2ef4137d7f..fcc52e7132d8 100644 --- a/test/integration/http2_upstream_integration_test.cc +++ b/test/integration/http2_upstream_integration_test.cc @@ -203,6 +203,17 @@ TEST_P(Http2UpstreamIntegrationTest, LargeSimultaneousRequestWithBufferLimits) { simultaneousRequest(1024 * 20, 1024 * 14 + 2, 1024 * 10 + 5, 1024 * 16); } +TEST_P(Http2UpstreamIntegrationTest, SimultaneousRequestAlpn) { + use_alpn_ = true; + simultaneousRequest(1024, 512, 1023, 513); +} + +TEST_P(Http2UpstreamIntegrationTest, LargeSimultaneousRequestWithBufferLimitsAlpn) { + use_alpn_ = true; + config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. + simultaneousRequest(1024 * 20, 1024 * 14 + 2, 1024 * 10 + 5, 1024 * 16); +} + void Http2UpstreamIntegrationTest::manySimultaneousRequests(uint32_t request_bytes, uint32_t) { TestRandomGenerator rand; const uint32_t num_requests = 50; diff --git a/test/integration/http2_upstream_integration_test.h b/test/integration/http2_upstream_integration_test.h index d942f8861846..754c6359907d 100644 --- a/test/integration/http2_upstream_integration_test.h +++ b/test/integration/http2_upstream_integration_test.h @@ -14,6 +14,9 @@ class Http2UpstreamIntegrationTest : public testing::TestWithParam{Http::Protocol::Http11})); } MockClusterInfo::~MockClusterInfo() = default; diff --git a/test/mocks/upstream/cluster_info.h b/test/mocks/upstream/cluster_info.h index 7674b6671a2b..08949a68e9dd 100644 --- a/test/mocks/upstream/cluster_info.h +++ b/test/mocks/upstream/cluster_info.h @@ -142,7 +142,8 @@ class MockClusterInfo : public ClusterInfo { upstreamHttpProtocolOptions, (), (const)); MOCK_METHOD(absl::optional, edsServiceName, (), (const)); MOCK_METHOD(void, createNetworkFilterChain, (Network::Connection&), (const)); - MOCK_METHOD(Http::Protocol, upstreamHttpProtocol, (absl::optional), (const)); + MOCK_METHOD(std::vector, upstreamHttpProtocol, (absl::optional), + (const)); Http::Http1::CodecStats& http1CodecStats() const override; Http::Http2::CodecStats& http2CodecStats() const override; diff --git a/test/mocks/upstream/cluster_manager_factory.h b/test/mocks/upstream/cluster_manager_factory.h index 00e4b6c409be..0de2928c57b1 100644 --- a/test/mocks/upstream/cluster_manager_factory.h +++ b/test/mocks/upstream/cluster_manager_factory.h @@ -22,7 +22,8 @@ class MockClusterManagerFactory : public ClusterManagerFactory { MOCK_METHOD(Http::ConnectionPool::InstancePtr, allocateConnPool, (Event::Dispatcher & dispatcher, HostConstSharedPtr host, ResourcePriority priority, - Http::Protocol protocol, const Network::ConnectionSocket::OptionsSharedPtr& options, + std::vector& protocol, + const Network::ConnectionSocket::OptionsSharedPtr& options, const Network::TransportSocketOptionsSharedPtr& transport_socket_options, ClusterConnectivityState& state)); From ffbf9523e951c8c627ace792a3e2abd0e4c6b565 Mon Sep 17 00:00:00 2001 From: John Esmet Date: Tue, 15 Dec 2020 19:16:25 -0500 Subject: [PATCH 41/49] stream_info: add setResponseCode and update local_reply to take a normal StreamInfo (#14402) Signed-off-by: John Esmet --- include/envoy/stream_info/stream_info.h | 5 +++++ source/common/local_reply/local_reply.cc | 8 ++++---- source/common/local_reply/local_reply.h | 2 +- source/common/stream_info/stream_info_impl.h | 2 ++ test/common/stream_info/test_util.h | 1 + test/mocks/local_reply/mocks.h | 4 ++-- test/mocks/stream_info/mocks.cc | 3 +++ test/mocks/stream_info/mocks.h | 1 + 8 files changed, 19 insertions(+), 7 deletions(-) diff --git a/include/envoy/stream_info/stream_info.h b/include/envoy/stream_info/stream_info.h index dc4a67973976..99e92b3a7241 100644 --- a/include/envoy/stream_info/stream_info.h +++ b/include/envoy/stream_info/stream_info.h @@ -236,6 +236,11 @@ class StreamInfo { */ virtual void setResponseFlag(ResponseFlag response_flag) PURE; + /** + * @param code the HTTP response code to set for this request. + */ + virtual void setResponseCode(uint32_t code) PURE; + /** * @param rc_details the response code details string to set for this request. * See ResponseCodeDetailValues above for well-known constants. diff --git a/source/common/local_reply/local_reply.cc b/source/common/local_reply/local_reply.cc index c3719513f124..42f8d32b0d38 100644 --- a/source/common/local_reply/local_reply.cc +++ b/source/common/local_reply/local_reply.cc @@ -77,7 +77,7 @@ class ResponseMapper { bool matchAndRewrite(const Http::RequestHeaderMap& request_headers, Http::ResponseHeaderMap& response_headers, const Http::ResponseTrailerMap& response_trailers, - StreamInfo::StreamInfoImpl& stream_info, Http::Code& code, std::string& body, + StreamInfo::StreamInfo& stream_info, Http::Code& code, std::string& body, BodyFormatter*& final_formatter) const { // If not matched, just bail out. if (!filter_->evaluate(stream_info, request_headers, response_headers, response_trailers)) { @@ -93,7 +93,7 @@ class ResponseMapper { if (status_code_.has_value() && code != status_code_.value()) { code = status_code_.value(); response_headers.setStatus(std::to_string(enumToInt(code))); - stream_info.response_code_ = static_cast(code); + stream_info.setResponseCode(static_cast(code)); } if (body_formatter_) { @@ -129,14 +129,14 @@ class LocalReplyImpl : public LocalReply { } void rewrite(const Http::RequestHeaderMap* request_headers, - Http::ResponseHeaderMap& response_headers, StreamInfo::StreamInfoImpl& stream_info, + Http::ResponseHeaderMap& response_headers, StreamInfo::StreamInfo& stream_info, Http::Code& code, std::string& body, absl::string_view& content_type) const override { // Set response code to stream_info and response_headers due to: // 1) StatusCode filter is using response_code from stream_info, // 2) %RESP(:status)% is from Status() in response_headers. response_headers.setStatus(std::to_string(enumToInt(code))); - stream_info.response_code_ = static_cast(code); + stream_info.setResponseCode(static_cast(code)); if (request_headers == nullptr) { request_headers = Http::StaticEmptyHeaders::get().request_headers.get(); diff --git a/source/common/local_reply/local_reply.h b/source/common/local_reply/local_reply.h index cafcaf33d307..5db93caa07fd 100644 --- a/source/common/local_reply/local_reply.h +++ b/source/common/local_reply/local_reply.h @@ -24,7 +24,7 @@ class LocalReply { */ virtual void rewrite(const Http::RequestHeaderMap* request_headers, Http::ResponseHeaderMap& response_headers, - StreamInfo::StreamInfoImpl& stream_info, Http::Code& code, std::string& body, + StreamInfo::StreamInfo& stream_info, Http::Code& code, std::string& body, absl::string_view& content_type) const PURE; }; diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h index 1c0614d93cbf..4f37abe3fbf6 100644 --- a/source/common/stream_info/stream_info_impl.h +++ b/source/common/stream_info/stream_info_impl.h @@ -131,6 +131,8 @@ struct StreamInfoImpl : public StreamInfo { return response_code_details_; } + void setResponseCode(uint32_t code) override { response_code_ = code; } + void setResponseCodeDetails(absl::string_view rc_details) override { response_code_details_.emplace(absl::StrReplaceAll(rc_details, emptySpaceReplacement())); } diff --git a/test/common/stream_info/test_util.h b/test/common/stream_info/test_util.h index 560b485e18c0..685607f876c3 100644 --- a/test/common/stream_info/test_util.h +++ b/test/common/stream_info/test_util.h @@ -38,6 +38,7 @@ class TestStreamInfo : public StreamInfo::StreamInfo { const absl::optional& responseCodeDetails() const override { return response_code_details_; } + void setResponseCode(uint32_t code) override { response_code_ = code; } void setResponseCodeDetails(absl::string_view rc_details) override { response_code_details_.emplace(rc_details); } diff --git a/test/mocks/local_reply/mocks.h b/test/mocks/local_reply/mocks.h index 3d0a7ddeab88..913f815d5069 100644 --- a/test/mocks/local_reply/mocks.h +++ b/test/mocks/local_reply/mocks.h @@ -11,9 +11,9 @@ class MockLocalReply : public LocalReply { MOCK_METHOD(void, rewrite, (const Http::RequestHeaderMap* request_headers, - Http::ResponseHeaderMap& response_headers, StreamInfo::StreamInfoImpl& stream_info, + Http::ResponseHeaderMap& response_headers, StreamInfo::StreamInfo& stream_info, Http::Code& code, std::string& body, absl::string_view& content_type), (const)); }; } // namespace LocalReply -} // namespace Envoy \ No newline at end of file +} // namespace Envoy diff --git a/test/mocks/stream_info/mocks.cc b/test/mocks/stream_info/mocks.cc index 8373bdeb3603..066734a71507 100644 --- a/test/mocks/stream_info/mocks.cc +++ b/test/mocks/stream_info/mocks.cc @@ -24,6 +24,9 @@ MockStreamInfo::MockStreamInfo() ON_CALL(*this, setResponseFlag(_)).WillByDefault(Invoke([this](ResponseFlag response_flag) { response_flags_ |= response_flag; })); + ON_CALL(*this, setResponseCode(_)).WillByDefault(Invoke([this](uint32_t code) { + response_code_ = code; + })); ON_CALL(*this, setResponseCodeDetails(_)).WillByDefault(Invoke([this](absl::string_view details) { response_code_details_ = std::string(details); })); diff --git a/test/mocks/stream_info/mocks.h b/test/mocks/stream_info/mocks.h index b02b849c2310..9ed11c0afa96 100644 --- a/test/mocks/stream_info/mocks.h +++ b/test/mocks/stream_info/mocks.h @@ -21,6 +21,7 @@ class MockStreamInfo : public StreamInfo { // StreamInfo::StreamInfo MOCK_METHOD(void, setResponseFlag, (ResponseFlag response_flag)); + MOCK_METHOD(void, setResponseCode, (uint32_t)); MOCK_METHOD(void, setResponseCodeDetails, (absl::string_view)); MOCK_METHOD(void, setConnectionTerminationDetails, (absl::string_view)); MOCK_METHOD(bool, intersectResponseFlags, (uint64_t), (const)); From b3bb0f9847497bc97951172d248fad3c88096829 Mon Sep 17 00:00:00 2001 From: Kuat Date: Tue, 15 Dec 2020 16:37:00 -0800 Subject: [PATCH 42/49] deps: update protobuf to 3.14 (#14253) There is an unfortunate change in 3.14 that changed go_package for WKT, which necessitates updating several go dependencies as well: protoc-gen-validate to 2020-11-30 transitive dependencies from protoc-gen-validate gazelle to 0.22.2 rules_go to 0.25.0 go to 1.15.5 Risk Level: low Signed-off-by: Kuat Yessenov Co-authored-by: Lizan Zhou --- .azure-pipelines/bazel.yml | 2 ++ api/bazel/api_build_system.bzl | 16 +++++++-------- api/bazel/repository_locations.bzl | 19 ++++++++++++------ api/test/build/BUILD | 2 -- api/test/build/go_build_test.go | 2 -- bazel/dependency_imports.bzl | 20 ++++++++++++++++++- bazel/protobuf.patch | 14 +++++++++++++ bazel/repository_locations.bzl | 18 ++++++++--------- .../bazel/api_build_system.bzl | 16 +++++++-------- .../bazel/repository_locations.bzl | 19 ++++++++++++------ source/common/config/protobuf_link_hacks.h | 1 + .../common/access_log/access_log_impl_test.cc | 2 +- .../upstream/cluster_manager_impl_test.cc | 2 +- test/common/upstream/eds_speed_test.cc | 1 + test/common/upstream/upstream_impl_test.cc | 7 ++----- .../filters/http/compressor/config_test.cc | 3 +++ test/integration/BUILD | 1 + test/integration/integration_test.cc | 3 +++ 18 files changed, 99 insertions(+), 49 deletions(-) diff --git a/.azure-pipelines/bazel.yml b/.azure-pipelines/bazel.yml index 62cb412e588f..3c440cdef934 100644 --- a/.azure-pipelines/bazel.yml +++ b/.azure-pipelines/bazel.yml @@ -66,6 +66,8 @@ steps: - bash: | echo "disk space at end of build:" df -h + # Cleanup offending files with unicode names + rm -rf $(Build.StagingDirectory)/tmp/*/*/external/go_sdk/test/fixedbugs displayName: "Check disk space at end" condition: always() diff --git a/api/bazel/api_build_system.bzl b/api/bazel/api_build_system.bzl index c0269d161f80..8a0e0bf71021 100644 --- a/api/bazel/api_build_system.bzl +++ b/api/bazel/api_build_system.bzl @@ -186,14 +186,14 @@ def api_proto_package( proto = name, visibility = ["//visibility:public"], deps = depset([_go_proto_mapping(dep) for dep in deps] + [ - "@com_github_golang_protobuf//ptypes:go_default_library", - "@com_github_golang_protobuf//ptypes/any:go_default_library", - "@com_github_golang_protobuf//ptypes/duration:go_default_library", - "@com_github_golang_protobuf//ptypes/struct:go_default_library", - "@com_github_golang_protobuf//ptypes/timestamp:go_default_library", - "@com_github_golang_protobuf//ptypes/wrappers:go_default_library", "@com_envoyproxy_protoc_gen_validate//validate:go_default_library", - "@com_google_googleapis//google/api:annotations_go_proto", - "@com_google_googleapis//google/rpc:status_go_proto", + "@com_github_golang_protobuf//ptypes:go_default_library_gen", + "@go_googleapis//google/api:annotations_go_proto", + "@go_googleapis//google/rpc:status_go_proto", + "@io_bazel_rules_go//proto/wkt:any_go_proto", + "@io_bazel_rules_go//proto/wkt:duration_go_proto", + "@io_bazel_rules_go//proto/wkt:struct_go_proto", + "@io_bazel_rules_go//proto/wkt:timestamp_go_proto", + "@io_bazel_rules_go//proto/wkt:wrappers_go_proto", ]).to_list(), ) diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index f0173e58946e..68ea3e2ffb84 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -14,23 +14,30 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "protoc-gen-validate (PGV)", project_desc = "protoc plugin to generate polyglot message validators", project_url = "https://github.com/envoyproxy/protoc-gen-validate", - version = "278964a8052f96a2f514add0298098f63fb7f47f", - sha256 = "e368733c9fb7f8489591ffaf269170d7658cc0cd1ee322b601512b769446d3c8", + version = "1bcea29601b5624234a19b3d7f0ebd9e9984f583", + sha256 = "2062bbe50eddf3c98490339721fb02b5b5cd78f610f163b98bbf95ba7105553f", strip_prefix = "protoc-gen-validate-{version}", urls = ["https://github.com/envoyproxy/protoc-gen-validate/archive/{version}.tar.gz"], - release_date = "2020-06-08", + release_date = "2020-11-30", use_category = ["api"], + implied_untracked_deps = [ + "com_github_iancoleman_strcase", + "com_github_lyft_protoc_gen_star", + "com_github_spf13_afero", + "org_golang_google_genproto", + "org_golang_x_text", + ], ), com_github_cncf_udpa = dict( project_name = "xDS API", project_desc = "xDS API Working Group (xDS-WG)", project_url = "https://github.com/cncf/udpa", # During the UDPA -> xDS migration, we aren't working with releases. - version = "5459f2c994033b0afed7e4a70ac7e90c90c1ffee", - sha256 = "c1f5c2438cf725b5f66aa4210dbc4bb691020c5ed4f64d2bc6638b06a11482f1", + version = "cc1b757b3eddccaaaf0743cbb107742bb7e3ee4f", + sha256 = "822a007cf155855d0c08a2e753a39e222e5816b904436196244066a818a8a230", strip_prefix = "udpa-{version}", urls = ["https://github.com/cncf/udpa/archive/{version}.tar.gz"], - release_date = "2020-11-20", + release_date = "2020-12-11", use_category = ["api"], ), com_github_openzipkin_zipkinapi = dict( diff --git a/api/test/build/BUILD b/api/test/build/BUILD index 2dae9fa0de03..affcc0403496 100644 --- a/api/test/build/BUILD +++ b/api/test/build/BUILD @@ -23,11 +23,9 @@ api_go_test( deps = [ "//envoy/api/v2:pkg_go_proto", "//envoy/api/v2/auth:pkg_go_proto", - "//envoy/config/bootstrap/v2:pkg_go_proto", "//envoy/service/accesslog/v2:pkg_go_proto", "//envoy/service/discovery/v2:pkg_go_proto", "//envoy/service/metrics/v2:pkg_go_proto", "//envoy/service/ratelimit/v2:pkg_go_proto", - "//envoy/service/trace/v2:pkg_go_proto", ], ) diff --git a/api/test/build/go_build_test.go b/api/test/build/go_build_test.go index 638ef478b8c7..e350404b3ab6 100644 --- a/api/test/build/go_build_test.go +++ b/api/test/build/go_build_test.go @@ -5,12 +5,10 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/api/v2" _ "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" - _ "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v2" _ "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v2" _ "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2" _ "github.com/envoyproxy/go-control-plane/envoy/service/metrics/v2" _ "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/trace/v2" ) func TestNoop(t *testing.T) { diff --git a/bazel/dependency_imports.bzl b/bazel/dependency_imports.bzl index 62b71950c9ed..f2ef292a2d93 100644 --- a/bazel/dependency_imports.bzl +++ b/bazel/dependency_imports.bzl @@ -16,7 +16,7 @@ load("@rules_antlr//antlr:deps.bzl", "antlr_dependencies") load("@proxy_wasm_rust_sdk//bazel:dependencies.bzl", "proxy_wasm_rust_sdk_dependencies") # go version for rules_go -GO_VERSION = "1.14.7" +GO_VERSION = "1.15.5" def envoy_dependency_imports(go_version = GO_VERSION): rules_foreign_cc_dependencies() @@ -57,6 +57,24 @@ def envoy_dependency_imports(go_version = GO_VERSION): sum = "h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=", version = "v0.3.0", ) + go_repository( + name = "com_github_spf13_afero", + importpath = "github.com/spf13/afero", + sum = "h1:8q6vk3hthlpb2SouZcnBVKboxWQWMDNF38bwholZrJc=", + version = "v1.3.4", + ) + go_repository( + name = "com_github_lyft_protoc_gen_star", + importpath = "github.com/lyft/protoc-gen-star", + sum = "h1:sImehRT+p7lW9n6R7MQc5hVgzWGEkDVZU4AsBQ4Isu8=", + version = "v0.5.1", + ) + go_repository( + name = "com_github_iancoleman_strcase", + importpath = "github.com/iancoleman/strcase", + sum = "h1:ux/56T2xqZO/3cP1I2F86qpeoYPCOzk+KF/UH/Ar+lk=", + version = "v0.0.0-20180726023541-3605ed457bf7", + ) config_validation_pip_install() configs_pip_install() diff --git a/bazel/protobuf.patch b/bazel/protobuf.patch index 1e19ebbb7ffe..fd6325838ba4 100644 --- a/bazel/protobuf.patch +++ b/bazel/protobuf.patch @@ -21,3 +21,17 @@ index efc3d8e7f..746ad4851 100644 ################################################################################ # Protobuf Runtime Library +diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py +index 97ac28028..8b7585d9d 100644 +--- a/python/google/protobuf/__init__.py ++++ b/python/google/protobuf/__init__.py +@@ -31,3 +31,9 @@ + # Copyright 2007 Google Inc. All Rights Reserved. + + __version__ = '3.14.0' ++ ++if __name__ != '__main__': ++ try: ++ __import__('pkg_resources').declare_namespace(__name__) ++ except ImportError: ++ __path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 747d36d610db..458c69f1f82b 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -15,10 +15,10 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Gazelle", project_desc = "Bazel BUILD file generator for Go projects", project_url = "https://github.com/bazelbuild/bazel-gazelle", - version = "0.21.1", - sha256 = "cdb02a887a7187ea4d5a27452311a75ed8637379a1287d8eeb952138ea485f7d", + version = "0.22.2", + sha256 = "b85f48fa105c4403326e9525ad2b2cc437babaa6e15a3fc0b1dbab0ab064bc7c", urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/v{version}/bazel-gazelle-v{version}.tar.gz"], - release_date = "2020-05-28", + release_date = "2020-10-02", use_category = ["build"], ), bazel_toolchains = dict( @@ -495,12 +495,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Protocol Buffers", project_desc = "Language-neutral, platform-neutral extensible mechanism for serializing structured data", project_url = "https://developers.google.com/protocol-buffers", - version = "3.13.0", - sha256 = "465fd9367992a9b9c4fba34a549773735da200903678b81b25f367982e8df376", + version = "3.14.0", + sha256 = "6dd0f6b20094910fbb7f1f7908688df01af2d4f6c5c21331b9f636048674aebf", strip_prefix = "protobuf-{version}", urls = ["https://github.com/protocolbuffers/protobuf/releases/download/v{version}/protobuf-all-{version}.tar.gz"], use_category = ["dataplane_core", "controlplane"], - release_date = "2020-08-14", + release_date = "2020-11-13", cpe = "cpe:2.3:a:google:protobuf:*", ), grpc_httpjson_transcoding = dict( @@ -520,11 +520,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Go rules for Bazel", project_desc = "Bazel rules for the Go language", project_url = "https://github.com/bazelbuild/rules_go", - version = "0.23.7", - sha256 = "0310e837aed522875791750de44408ec91046c630374990edd51827cb169f616", + version = "0.25.0", + sha256 = "6f111c57fd50baf5b8ee9d63024874dd2a014b069426156c55adbf6d3d22cb7b", urls = ["https://github.com/bazelbuild/rules_go/releases/download/v{version}/rules_go-v{version}.tar.gz"], use_category = ["build", "api"], - release_date = "2020-08-06", + release_date = "2020-12-02", implied_untracked_deps = [ "com_github_golang_protobuf", "io_bazel_rules_nogo", diff --git a/generated_api_shadow/bazel/api_build_system.bzl b/generated_api_shadow/bazel/api_build_system.bzl index c0269d161f80..8a0e0bf71021 100644 --- a/generated_api_shadow/bazel/api_build_system.bzl +++ b/generated_api_shadow/bazel/api_build_system.bzl @@ -186,14 +186,14 @@ def api_proto_package( proto = name, visibility = ["//visibility:public"], deps = depset([_go_proto_mapping(dep) for dep in deps] + [ - "@com_github_golang_protobuf//ptypes:go_default_library", - "@com_github_golang_protobuf//ptypes/any:go_default_library", - "@com_github_golang_protobuf//ptypes/duration:go_default_library", - "@com_github_golang_protobuf//ptypes/struct:go_default_library", - "@com_github_golang_protobuf//ptypes/timestamp:go_default_library", - "@com_github_golang_protobuf//ptypes/wrappers:go_default_library", "@com_envoyproxy_protoc_gen_validate//validate:go_default_library", - "@com_google_googleapis//google/api:annotations_go_proto", - "@com_google_googleapis//google/rpc:status_go_proto", + "@com_github_golang_protobuf//ptypes:go_default_library_gen", + "@go_googleapis//google/api:annotations_go_proto", + "@go_googleapis//google/rpc:status_go_proto", + "@io_bazel_rules_go//proto/wkt:any_go_proto", + "@io_bazel_rules_go//proto/wkt:duration_go_proto", + "@io_bazel_rules_go//proto/wkt:struct_go_proto", + "@io_bazel_rules_go//proto/wkt:timestamp_go_proto", + "@io_bazel_rules_go//proto/wkt:wrappers_go_proto", ]).to_list(), ) diff --git a/generated_api_shadow/bazel/repository_locations.bzl b/generated_api_shadow/bazel/repository_locations.bzl index f0173e58946e..68ea3e2ffb84 100644 --- a/generated_api_shadow/bazel/repository_locations.bzl +++ b/generated_api_shadow/bazel/repository_locations.bzl @@ -14,23 +14,30 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "protoc-gen-validate (PGV)", project_desc = "protoc plugin to generate polyglot message validators", project_url = "https://github.com/envoyproxy/protoc-gen-validate", - version = "278964a8052f96a2f514add0298098f63fb7f47f", - sha256 = "e368733c9fb7f8489591ffaf269170d7658cc0cd1ee322b601512b769446d3c8", + version = "1bcea29601b5624234a19b3d7f0ebd9e9984f583", + sha256 = "2062bbe50eddf3c98490339721fb02b5b5cd78f610f163b98bbf95ba7105553f", strip_prefix = "protoc-gen-validate-{version}", urls = ["https://github.com/envoyproxy/protoc-gen-validate/archive/{version}.tar.gz"], - release_date = "2020-06-08", + release_date = "2020-11-30", use_category = ["api"], + implied_untracked_deps = [ + "com_github_iancoleman_strcase", + "com_github_lyft_protoc_gen_star", + "com_github_spf13_afero", + "org_golang_google_genproto", + "org_golang_x_text", + ], ), com_github_cncf_udpa = dict( project_name = "xDS API", project_desc = "xDS API Working Group (xDS-WG)", project_url = "https://github.com/cncf/udpa", # During the UDPA -> xDS migration, we aren't working with releases. - version = "5459f2c994033b0afed7e4a70ac7e90c90c1ffee", - sha256 = "c1f5c2438cf725b5f66aa4210dbc4bb691020c5ed4f64d2bc6638b06a11482f1", + version = "cc1b757b3eddccaaaf0743cbb107742bb7e3ee4f", + sha256 = "822a007cf155855d0c08a2e753a39e222e5816b904436196244066a818a8a230", strip_prefix = "udpa-{version}", urls = ["https://github.com/cncf/udpa/archive/{version}.tar.gz"], - release_date = "2020-11-20", + release_date = "2020-12-11", use_category = ["api"], ), com_github_openzipkin_zipkinapi = dict( diff --git a/source/common/config/protobuf_link_hacks.h b/source/common/config/protobuf_link_hacks.h index b613d60ff84c..de6e816e37b9 100644 --- a/source/common/config/protobuf_link_hacks.h +++ b/source/common/config/protobuf_link_hacks.h @@ -47,6 +47,7 @@ const envoy::service::route::v3::RdsDummy _rds_dummy_v3; const envoy::service::cluster::v3::CdsDummy _cds_dummy_v3; const envoy::service::endpoint::v3::EdsDummy _eds_dummy_v3; const envoy::service::route::v3::SrdsDummy _srds_dummy_v3; +const envoy::service::extension::v3::EcdsDummy _ecds_dummy_v3; // With the v2 -> v3 migration there is another, related linking issue. // Symbols for v2 protos which headers are not included in any file in the codebase are being diff --git a/test/common/access_log/access_log_impl_test.cc b/test/common/access_log/access_log_impl_test.cc index 663f35cd1105..1e6eb05c0cea 100644 --- a/test/common/access_log/access_log_impl_test.cc +++ b/test/common/access_log/access_log_impl_test.cc @@ -1137,7 +1137,7 @@ name: accesslog )EOF"; EXPECT_THROW_WITH_REGEX(AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_), - EnvoyException, ".*\"NOT_A_VALID_CODE\" for type TYPE_ENUM.*"); + EnvoyException, "NOT_A_VALID_CODE"); } TEST_F(AccessLogImplTest, GrpcStatusFilterBlock) { diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index d957311d5e34..bd96f50e0c1e 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -302,7 +302,7 @@ TEST_F(ClusterManagerImplTest, UnknownClusterType) { )EOF"; EXPECT_THROW_WITH_REGEX(create(parseBootstrapFromV3Json(json)), EnvoyException, - "invalid value \"foo\" for type TYPE_ENUM"); + "invalid value \"foo\""); } TEST_F(ClusterManagerImplTest, LocalClusterNotDefined) { diff --git a/test/common/upstream/eds_speed_test.cc b/test/common/upstream/eds_speed_test.cc index d939e3513dd8..33bd77353ad4 100644 --- a/test/common/upstream/eds_speed_test.cc +++ b/test/common/upstream/eds_speed_test.cc @@ -10,6 +10,7 @@ #include "common/config/grpc_mux_impl.h" #include "common/config/grpc_subscription_impl.h" +#include "common/config/protobuf_link_hacks.h" #include "common/config/utility.h" #include "common/singleton/manager_impl.h" #include "common/upstream/eds.h" diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index 27d050861af2..7d031bd37506 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -1917,7 +1917,7 @@ TEST_F(StaticClusterImplTest, UnsupportedLBType) { socket_address: { address: 192.168.1.2, port_value: 44 } )EOF"; - EXPECT_THROW_WITH_MESSAGE( + EXPECT_THROW_WITH_REGEX( { envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); Envoy::Stats::ScopePtr scope = @@ -1930,10 +1930,7 @@ TEST_F(StaticClusterImplTest, UnsupportedLBType) { StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), false); }, - EnvoyException, - "Protobuf message (type envoy.config.cluster.v3.Cluster reason " - "INVALID_ARGUMENT:(lb_policy): invalid " - "value \"fakelbtype\" for type TYPE_ENUM) has unknown fields"); + EnvoyException, "invalid value \"fakelbtype\""); } TEST_F(StaticClusterImplTest, MalformedHostIP) { diff --git a/test/extensions/filters/http/compressor/config_test.cc b/test/extensions/filters/http/compressor/config_test.cc index cea48bc00cff..aec2806eb9b3 100644 --- a/test/extensions/filters/http/compressor/config_test.cc +++ b/test/extensions/filters/http/compressor/config_test.cc @@ -1,5 +1,6 @@ #include "extensions/filters/http/compressor/config.h" +#include "test/extensions/filters/http/compressor/mock_compressor_library.pb.h" #include "test/mocks/server/factory_context.h" #include "gtest/gtest.h" @@ -12,6 +13,8 @@ namespace { using testing::NiceMock; +const ::test::mock_compressor_library::Unregistered _mock_compressor_library_dummy; + TEST(CompressorFilterFactoryTests, MissingCompressorLibraryConfig) { const envoy::extensions::filters::http::compressor::v3::Compressor proto_config; CompressorFilterFactory factory; diff --git a/test/integration/BUILD b/test/integration/BUILD index 036286910f1d..0778f624670d 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -892,6 +892,7 @@ envoy_cc_test( "//test/mocks/http:http_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/grpc_http1_bridge/v2:pkg_cc_proto", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", ], diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index 15f7587a2d34..deefe009901e 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -3,6 +3,7 @@ #include #include "envoy/config/bootstrap/v3/bootstrap.pb.h" +#include "envoy/config/filter/http/grpc_http1_bridge/v2/config.pb.h" #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" @@ -418,6 +419,8 @@ TEST_P(IntegrationTest, UpstreamDisconnectWithTwoRequests) { test_server_->waitForCounterGe("cluster.cluster_0.upstream_rq_200", 2); } +const ::envoy::config::filter::http::grpc_http1_bridge::v2::Config _grpc_http1_bridge_dummy; + // Test hitting the bridge filter with too many response bytes to buffer. Given // the headers are not proxied, the connection manager will send a local error reply. TEST_P(IntegrationTest, HittingGrpcFilterLimitBufferingHeaders) { From 6474355e7a977e255fe88e9a2b97d3d764712852 Mon Sep 17 00:00:00 2001 From: John Esmet Date: Tue, 15 Dec 2020 19:44:40 -0500 Subject: [PATCH 43/49] ratelimit: support returning custom response bodies for non-OK responses from the external ratelimit service (#14189) Signed-off-by: John Esmet --- api/envoy/service/ratelimit/v3/rls.proto | 4 + docs/root/version_history/current.rst | 1 + .../envoy/service/ratelimit/v3/rls.proto | 4 + .../filters/common/ratelimit/ratelimit.h | 14 +- .../common/ratelimit/ratelimit_impl.cc | 4 +- .../filters/http/ratelimit/ratelimit.cc | 27 ++- .../filters/http/ratelimit/ratelimit.h | 5 +- .../filters/network/ratelimit/ratelimit.cc | 3 +- .../filters/network/ratelimit/ratelimit.h | 3 +- .../filters/ratelimit/ratelimit.cc | 2 +- .../filters/ratelimit/ratelimit.h | 3 +- .../network/filter_manager_impl_test.cc | 2 +- .../common/ratelimit/ratelimit_impl_test.cc | 16 +- .../filters/http/ratelimit/ratelimit_test.cc | 172 ++++++++++++++++-- .../network/ratelimit/ratelimit_test.cc | 14 +- .../filters/ratelimit/ratelimit_test.cc | 16 +- test/test_common/utility.h | 5 + 17 files changed, 234 insertions(+), 61 deletions(-) diff --git a/api/envoy/service/ratelimit/v3/rls.proto b/api/envoy/service/ratelimit/v3/rls.proto index 42f24cfb0805..7379368fd4c1 100644 --- a/api/envoy/service/ratelimit/v3/rls.proto +++ b/api/envoy/service/ratelimit/v3/rls.proto @@ -51,6 +51,7 @@ message RateLimitRequest { } // A response from a ShouldRateLimit call. +// [#next-free-field: 6] message RateLimitResponse { option (udpa.annotations.versioning).previous_message_type = "envoy.service.ratelimit.v2.RateLimitResponse"; @@ -131,4 +132,7 @@ message RateLimitResponse { // A list of headers to add to the request when forwarded repeated config.core.v3.HeaderValue request_headers_to_add = 4; + + // A response body to send to the downstream client when the response code is not OK. + bytes raw_body = 5; } diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index a48430ad6f1c..95034b7b1c13 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -81,6 +81,7 @@ New Features * overload: add :ref:`envoy.overload_actions.reduce_timeouts ` overload action to enable scaling timeouts down with load. Scaling support :ref:`is limited ` to the HTTP connection and stream idle timeouts. * ratelimit: added support for use of various :ref:`metadata ` as a ratelimit action. * ratelimit: added :ref:`disable_x_envoy_ratelimited_header ` option to disable `X-Envoy-RateLimited` header. +* ratelimit: added :ref:`body ` field to support custom response bodies for non-OK responses from the external ratelimit service. * router: added support for regex rewrites during HTTP redirects using :ref:`regex_rewrite `. * sds: improved support for atomic :ref:`key rotations ` and added configurable rotation triggers for :ref:`TlsCertificate ` and diff --git a/generated_api_shadow/envoy/service/ratelimit/v3/rls.proto b/generated_api_shadow/envoy/service/ratelimit/v3/rls.proto index 42f24cfb0805..7379368fd4c1 100644 --- a/generated_api_shadow/envoy/service/ratelimit/v3/rls.proto +++ b/generated_api_shadow/envoy/service/ratelimit/v3/rls.proto @@ -51,6 +51,7 @@ message RateLimitRequest { } // A response from a ShouldRateLimit call. +// [#next-free-field: 6] message RateLimitResponse { option (udpa.annotations.versioning).previous_message_type = "envoy.service.ratelimit.v2.RateLimitResponse"; @@ -131,4 +132,7 @@ message RateLimitResponse { // A list of headers to add to the request when forwarded repeated config.core.v3.HeaderValue request_headers_to_add = 4; + + // A response body to send to the downstream client when the response code is not OK. + bytes raw_body = 5; } diff --git a/source/extensions/filters/common/ratelimit/ratelimit.h b/source/extensions/filters/common/ratelimit/ratelimit.h index 068cd369b643..964cc25a7bd6 100644 --- a/source/extensions/filters/common/ratelimit/ratelimit.h +++ b/source/extensions/filters/common/ratelimit/ratelimit.h @@ -44,12 +44,20 @@ class RequestCallbacks { virtual ~RequestCallbacks() = default; /** - * Called when a limit request is complete. The resulting status, - * response headers and request headers to be forwarded to the upstream are supplied. + * Called when a limit request is complete. The resulting status, response headers + * and request headers to be forwarded to the upstream are supplied. + * + * @status The ratelimit status + * @descriptor_statuses The descriptor statuses + * @response_headers_to_add The headers to add to the downstream response, for non-OK statuses + * @request_headers_to_add The headers to add to the upstream request, if not ratelimited + * @response_body The response body to use for the downstream response, for non-OK statuses. May + * contain non UTF-8 values (e.g. binary data). */ virtual void complete(LimitStatus status, DescriptorStatusListPtr&& descriptor_statuses, Http::ResponseHeaderMapPtr&& response_headers_to_add, - Http::RequestHeaderMapPtr&& request_headers_to_add) PURE; + Http::RequestHeaderMapPtr&& request_headers_to_add, + const std::string& response_body) PURE; }; /** diff --git a/source/extensions/filters/common/ratelimit/ratelimit_impl.cc b/source/extensions/filters/common/ratelimit/ratelimit_impl.cc index d4c3f5afdaa3..4aef806f60d1 100644 --- a/source/extensions/filters/common/ratelimit/ratelimit_impl.cc +++ b/source/extensions/filters/common/ratelimit/ratelimit_impl.cc @@ -106,14 +106,14 @@ void GrpcClientImpl::onSuccess( DescriptorStatusListPtr descriptor_statuses = std::make_unique( response->statuses().begin(), response->statuses().end()); callbacks_->complete(status, std::move(descriptor_statuses), std::move(response_headers_to_add), - std::move(request_headers_to_add)); + std::move(request_headers_to_add), response->raw_body()); callbacks_ = nullptr; } void GrpcClientImpl::onFailure(Grpc::Status::GrpcStatus status, const std::string&, Tracing::Span&) { ASSERT(status != Grpc::Status::WellKnownGrpcStatus::Ok); - callbacks_->complete(LimitStatus::Error, nullptr, nullptr, nullptr); + callbacks_->complete(LimitStatus::Error, nullptr, nullptr, nullptr, EMPTY_STRING); callbacks_ = nullptr; } diff --git a/source/extensions/filters/http/ratelimit/ratelimit.cc b/source/extensions/filters/http/ratelimit/ratelimit.cc index 8430f47243a8..b72bb82c4b75 100644 --- a/source/extensions/filters/http/ratelimit/ratelimit.cc +++ b/source/extensions/filters/http/ratelimit/ratelimit.cc @@ -115,7 +115,7 @@ Http::FilterHeadersStatus Filter::encode100ContinueHeaders(Http::ResponseHeaderM } Http::FilterHeadersStatus Filter::encodeHeaders(Http::ResponseHeaderMap& headers, bool) { - populateResponseHeaders(headers); + populateResponseHeaders(headers, /*from_local_reply=*/false); return Http::FilterHeadersStatus::Continue; } @@ -143,7 +143,8 @@ void Filter::onDestroy() { void Filter::complete(Filters::Common::RateLimit::LimitStatus status, Filters::Common::RateLimit::DescriptorStatusListPtr&& descriptor_statuses, Http::ResponseHeaderMapPtr&& response_headers_to_add, - Http::RequestHeaderMapPtr&& request_headers_to_add) { + Http::RequestHeaderMapPtr&& request_headers_to_add, + const std::string& response_body) { state_ = State::Complete; response_headers_to_add_ = std::move(response_headers_to_add); Http::HeaderMapPtr req_headers_to_add = std::move(request_headers_to_add); @@ -195,8 +196,10 @@ void Filter::complete(Filters::Common::RateLimit::LimitStatus status, config_->runtime().snapshot().featureEnabled("ratelimit.http_filter_enforcing", 100)) { state_ = State::Responded; callbacks_->sendLocalReply( - Http::Code::TooManyRequests, "", - [this](Http::HeaderMap& headers) { populateResponseHeaders(headers); }, + Http::Code::TooManyRequests, response_body, + [this](Http::HeaderMap& headers) { + populateResponseHeaders(headers, /*from_local_reply=*/true); + }, config_->rateLimitedGrpcStatus(), RcDetails::get().RateLimited); callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::RateLimited); } else if (status == Filters::Common::RateLimit::LimitStatus::Error) { @@ -208,8 +211,8 @@ void Filter::complete(Filters::Common::RateLimit::LimitStatus status, } } else { state_ = State::Responded; - callbacks_->sendLocalReply(Http::Code::InternalServerError, "", nullptr, absl::nullopt, - RcDetails::get().RateLimitError); + callbacks_->sendLocalReply(Http::Code::InternalServerError, response_body, nullptr, + absl::nullopt, RcDetails::get().RateLimitError); callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::RateLimitServiceError); } } else if (!initiating_call_) { @@ -236,8 +239,18 @@ void Filter::populateRateLimitDescriptors(const Router::RateLimitPolicy& rate_li } } -void Filter::populateResponseHeaders(Http::HeaderMap& response_headers) { +void Filter::populateResponseHeaders(Http::HeaderMap& response_headers, bool from_local_reply) { if (response_headers_to_add_) { + // If the ratelimit service is sending back the content-type header and we're + // populating response headers for a local reply, overwrite the existing + // content-type header. + // + // We do this because sendLocalReply initially sets content-type to text/plain + // whenever the response body is non-empty, but we want the content-type coming + // from the ratelimit service to be authoritative in this case. + if (from_local_reply && !response_headers_to_add_->getContentTypeValue().empty()) { + response_headers.remove(Http::Headers::get().ContentType); + } Http::HeaderUtility::addHeaders(response_headers, *response_headers_to_add_); response_headers_to_add_ = nullptr; } diff --git a/source/extensions/filters/http/ratelimit/ratelimit.h b/source/extensions/filters/http/ratelimit/ratelimit.h index 058eb793569a..5623cd2a9840 100644 --- a/source/extensions/filters/http/ratelimit/ratelimit.h +++ b/source/extensions/filters/http/ratelimit/ratelimit.h @@ -148,7 +148,8 @@ class Filter : public Http::StreamFilter, public Filters::Common::RateLimit::Req void complete(Filters::Common::RateLimit::LimitStatus status, Filters::Common::RateLimit::DescriptorStatusListPtr&& descriptor_statuses, Http::ResponseHeaderMapPtr&& response_headers_to_add, - Http::RequestHeaderMapPtr&& request_headers_to_add) override; + Http::RequestHeaderMapPtr&& request_headers_to_add, + const std::string& response_body) override; private: void initiateCall(const Http::RequestHeaderMap& headers); @@ -156,7 +157,7 @@ class Filter : public Http::StreamFilter, public Filters::Common::RateLimit::Req std::vector& descriptors, const Router::RouteEntry* route_entry, const Http::HeaderMap& headers) const; - void populateResponseHeaders(Http::HeaderMap& response_headers); + void populateResponseHeaders(Http::HeaderMap& response_headers, bool from_local_reply); void appendRequestHeaders(Http::HeaderMapPtr& request_headers_to_add); VhRateLimitOptions getVirtualHostRateLimitOption(const Router::RouteConstSharedPtr& route); diff --git a/source/extensions/filters/network/ratelimit/ratelimit.cc b/source/extensions/filters/network/ratelimit/ratelimit.cc index 00ed50a9f60c..01d1ed73324e 100644 --- a/source/extensions/filters/network/ratelimit/ratelimit.cc +++ b/source/extensions/filters/network/ratelimit/ratelimit.cc @@ -72,7 +72,8 @@ void Filter::onEvent(Network::ConnectionEvent event) { void Filter::complete(Filters::Common::RateLimit::LimitStatus status, Filters::Common::RateLimit::DescriptorStatusListPtr&&, - Http::ResponseHeaderMapPtr&&, Http::RequestHeaderMapPtr&&) { + Http::ResponseHeaderMapPtr&&, Http::RequestHeaderMapPtr&&, + const std::string&) { status_ = Status::Complete; config_->stats().active_.dec(); diff --git a/source/extensions/filters/network/ratelimit/ratelimit.h b/source/extensions/filters/network/ratelimit/ratelimit.h index eba34f434867..d1029bfbf295 100644 --- a/source/extensions/filters/network/ratelimit/ratelimit.h +++ b/source/extensions/filters/network/ratelimit/ratelimit.h @@ -94,7 +94,8 @@ class Filter : public Network::ReadFilter, void complete(Filters::Common::RateLimit::LimitStatus status, Filters::Common::RateLimit::DescriptorStatusListPtr&& descriptor_statuses, Http::ResponseHeaderMapPtr&& response_headers_to_add, - Http::RequestHeaderMapPtr&& request_headers_to_add) override; + Http::RequestHeaderMapPtr&& request_headers_to_add, + const std::string& response_body) override; private: enum class Status { NotStarted, Calling, Complete }; diff --git a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.cc b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.cc index c775dff93534..a9cf61aca3e3 100644 --- a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.cc +++ b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.cc @@ -62,7 +62,7 @@ void Filter::onDestroy() { void Filter::complete(Filters::Common::RateLimit::LimitStatus status, Filters::Common::RateLimit::DescriptorStatusListPtr&& descriptor_statuses, Http::ResponseHeaderMapPtr&& response_headers_to_add, - Http::RequestHeaderMapPtr&& request_headers_to_add) { + Http::RequestHeaderMapPtr&& request_headers_to_add, const std::string&) { // TODO(zuercher): Store headers to append to a response. Adding them to a local reply (over // limit or error) is a matter of modifying the callbacks to allow it. Adding them to an upstream // response requires either response (aka encoder) filters or some other mechanism. diff --git a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h index caa5333cda65..abecfb38ac56 100644 --- a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h +++ b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h @@ -79,7 +79,8 @@ class Filter : public ThriftProxy::ThriftFilters::PassThroughDecoderFilter, void complete(Filters::Common::RateLimit::LimitStatus status, Filters::Common::RateLimit::DescriptorStatusListPtr&& descriptor_statuses, Http::ResponseHeaderMapPtr&& response_headers_to_add, - Http::RequestHeaderMapPtr&& request_headers_to_add) override; + Http::RequestHeaderMapPtr&& request_headers_to_add, + const std::string& response_body) override; private: void initiateCall(const ThriftProxy::MessageMetadata& metadata); diff --git a/test/common/network/filter_manager_impl_test.cc b/test/common/network/filter_manager_impl_test.cc index b2e8d4f809a2..6655c7b6388d 100644 --- a/test/common/network/filter_manager_impl_test.cc +++ b/test/common/network/filter_manager_impl_test.cc @@ -416,7 +416,7 @@ stat_prefix: name .WillOnce(Return(&conn_pool)); request_callbacks->complete(Extensions::Filters::Common::RateLimit::LimitStatus::OK, nullptr, - nullptr, nullptr); + nullptr, nullptr, ""); conn_pool.poolReady(upstream_connection); diff --git a/test/extensions/filters/common/ratelimit/ratelimit_impl_test.cc b/test/extensions/filters/common/ratelimit/ratelimit_impl_test.cc index 319596f436a9..1d514e0673c1 100644 --- a/test/extensions/filters/common/ratelimit/ratelimit_impl_test.cc +++ b/test/extensions/filters/common/ratelimit/ratelimit_impl_test.cc @@ -38,15 +38,17 @@ class MockRequestCallbacks : public RequestCallbacks { public: void complete(LimitStatus status, DescriptorStatusListPtr&& descriptor_statuses, Http::ResponseHeaderMapPtr&& response_headers_to_add, - Http::RequestHeaderMapPtr&& request_headers_to_add) override { + Http::RequestHeaderMapPtr&& request_headers_to_add, + const std::string& response_body) override { complete_(status, descriptor_statuses.get(), response_headers_to_add.get(), - request_headers_to_add.get()); + request_headers_to_add.get(), response_body); } MOCK_METHOD(void, complete_, (LimitStatus status, const DescriptorStatusList* descriptor_statuses, const Http::ResponseHeaderMap* response_headers_to_add, - const Http::RequestHeaderMap* request_headers_to_add)); + const Http::RequestHeaderMap* request_headers_to_add, + const std::string& response_body)); }; class RateLimitGrpcClientTest : public testing::Test { @@ -91,7 +93,7 @@ TEST_F(RateLimitGrpcClientTest, Basic) { response = std::make_unique(); response->set_overall_code(envoy::service::ratelimit::v3::RateLimitResponse::OVER_LIMIT); EXPECT_CALL(span_, setTag(Eq("ratelimit_status"), Eq("over_limit"))); - EXPECT_CALL(request_callbacks_, complete_(LimitStatus::OverLimit, _, _, _)); + EXPECT_CALL(request_callbacks_, complete_(LimitStatus::OverLimit, _, _, _, _)); client_.onSuccess(std::move(response), span_); } @@ -110,7 +112,7 @@ TEST_F(RateLimitGrpcClientTest, Basic) { response = std::make_unique(); response->set_overall_code(envoy::service::ratelimit::v3::RateLimitResponse::OK); EXPECT_CALL(span_, setTag(Eq("ratelimit_status"), Eq("ok"))); - EXPECT_CALL(request_callbacks_, complete_(LimitStatus::OK, _, _, _)); + EXPECT_CALL(request_callbacks_, complete_(LimitStatus::OK, _, _, _, _)); client_.onSuccess(std::move(response), span_); } @@ -127,7 +129,7 @@ TEST_F(RateLimitGrpcClientTest, Basic) { Tracing::NullSpan::instance(), stream_info_); response = std::make_unique(); - EXPECT_CALL(request_callbacks_, complete_(LimitStatus::Error, _, _, _)); + EXPECT_CALL(request_callbacks_, complete_(LimitStatus::Error, _, _, _, _)); client_.onFailure(Grpc::Status::Unknown, "", span_); } @@ -150,7 +152,7 @@ TEST_F(RateLimitGrpcClientTest, Basic) { response = std::make_unique(); response->set_overall_code(envoy::service::ratelimit::v3::RateLimitResponse::OK); EXPECT_CALL(span_, setTag(Eq("ratelimit_status"), Eq("ok"))); - EXPECT_CALL(request_callbacks_, complete_(LimitStatus::OK, _, _, _)); + EXPECT_CALL(request_callbacks_, complete_(LimitStatus::OK, _, _, _, _)); client_.onSuccess(std::move(response), span_); } } diff --git a/test/extensions/filters/http/ratelimit/ratelimit_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_test.cc index a14a2c4463d6..25462a3ce725 100644 --- a/test/extensions/filters/http/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/http/ratelimit/ratelimit_test.cc @@ -234,7 +234,7 @@ TEST_F(HttpRateLimitFilterTest, OkResponse) { setResponseFlag(StreamInfo::ResponseFlag::RateLimited)) .Times(0); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ( 1U, filter_callbacks_.clusterInfo()->statsScope().counterFromStatName(ratelimit_ok_).value()); @@ -285,7 +285,7 @@ TEST_F(HttpRateLimitFilterTest, OkResponseWithHeaders) { request_callbacks_->complete( Filters::Common::RateLimit::LimitStatus::OK, nullptr, Http::ResponseHeaderMapPtr{new Http::TestResponseHeaderMapImpl(*rl_headers)}, - Http::RequestHeaderMapPtr{new Http::TestRequestHeaderMapImpl(*request_headers_to_add)}); + Http::RequestHeaderMapPtr{new Http::TestRequestHeaderMapImpl(*request_headers_to_add)}, ""); Http::TestResponseHeaderMapImpl expected_headers(*rl_headers); Http::TestResponseHeaderMapImpl response_headers; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, false)); @@ -341,7 +341,7 @@ TEST_F(HttpRateLimitFilterTest, OkResponseWithFilterHeaders) { auto descriptor_statuses_ptr = std::make_unique(descriptor_statuses); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OK, - std::move(descriptor_statuses_ptr), nullptr, nullptr); + std::move(descriptor_statuses_ptr), nullptr, nullptr, ""); Http::TestResponseHeaderMapImpl expected_headers{ {"x-ratelimit-limit", "1, 1;w=60;name=\"first\", 4;w=3600;name=\"second\""}, @@ -368,7 +368,7 @@ TEST_F(HttpRateLimitFilterTest, ImmediateOkResponse) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -399,7 +399,7 @@ TEST_F(HttpRateLimitFilterTest, ImmediateErrorResponse) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -438,7 +438,7 @@ TEST_F(HttpRateLimitFilterTest, ErrorResponse) { EXPECT_CALL(filter_callbacks_, continueDecoding()); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); @@ -471,7 +471,7 @@ TEST_F(HttpRateLimitFilterTest, ErrorResponseWithFailureModeAllowOff) { filter_->decodeHeaders(request_headers_, false)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_CALL(filter_callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::RateLimitServiceError)) @@ -512,7 +512,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponse) { setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, - std::move(h), nullptr); + std::move(h), nullptr, ""); EXPECT_EQ(1U, filter_callbacks_.clusterInfo() ->statsScope() @@ -564,7 +564,139 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithHeaders) { Http::ResponseHeaderMapPtr h{new Http::TestResponseHeaderMapImpl(*rl_headers)}; Http::RequestHeaderMapPtr uh{new Http::TestRequestHeaderMapImpl(*request_headers_to_add)}; request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, - std::move(h), std::move(uh)); + std::move(h), std::move(uh), ""); + + EXPECT_THAT(*request_headers_to_add, Not(IsSubsetOfHeaders(request_headers_))); + EXPECT_EQ(1U, filter_callbacks_.clusterInfo() + ->statsScope() + .counterFromStatName(ratelimit_over_limit_) + .value()); + EXPECT_EQ( + 1U, + filter_callbacks_.clusterInfo()->statsScope().counterFromStatName(upstream_rq_4xx_).value()); + EXPECT_EQ( + 1U, + filter_callbacks_.clusterInfo()->statsScope().counterFromStatName(upstream_rq_429_).value()); +} + +TEST_F(HttpRateLimitFilterTest, LimitResponseWithBody) { + SetUpTest(filter_config_); + InSequence s; + + EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)) + .WillOnce(SetArgReferee<1>(descriptor_)); + EXPECT_CALL(*client_, limit(_, _, _, _, _)) + .WillOnce( + WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { + request_callbacks_ = &callbacks; + }))); + + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(request_headers_, false)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, + filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); + EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); + + const std::string response_body = "this is a custom over limit response body."; + const std::string content_length = std::to_string(response_body.length()); + Http::HeaderMapPtr rl_headers{new Http::TestResponseHeaderMapImpl{ + {"x-ratelimit-limit", "1000"}, {"x-ratelimit-remaining", "0"}, {"retry-after", "33"}}}; + Http::TestResponseHeaderMapImpl expected_headers{}; + // We construct the expected_headers map in careful order, because HeaderMapEqualRef below + // compares two header maps in order. In practice, content-length and content-type headers + // are added before additional ratelimit headers and the final x-envoy-ratelimited header. + expected_headers.addCopy(":status", "429"); + expected_headers.addCopy("content-length", std::string(content_length)); + expected_headers.addCopy("content-type", "text/plain"); + expected_headers.copyFrom(*rl_headers); + expected_headers.addCopy("x-envoy-ratelimited", Http::Headers::get().EnvoyRateLimitedValues.True); + + EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), false)); + EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); + EXPECT_CALL(filter_callbacks_, encodeData(_, true)) + .WillOnce( + Invoke([&](Buffer::Instance& data, bool) { EXPECT_EQ(data.toString(), response_body); })); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + + Http::HeaderMapPtr request_headers_to_add{ + new Http::TestRequestHeaderMapImpl{{"x-rls-rate-limited", "true"}}}; + + Http::ResponseHeaderMapPtr h{new Http::TestResponseHeaderMapImpl(*rl_headers)}; + Http::RequestHeaderMapPtr uh{new Http::TestRequestHeaderMapImpl(*request_headers_to_add)}; + request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, + std::move(h), std::move(uh), response_body); + + EXPECT_THAT(*request_headers_to_add, Not(IsSubsetOfHeaders(request_headers_))); + EXPECT_EQ(1U, filter_callbacks_.clusterInfo() + ->statsScope() + .counterFromStatName(ratelimit_over_limit_) + .value()); + EXPECT_EQ( + 1U, + filter_callbacks_.clusterInfo()->statsScope().counterFromStatName(upstream_rq_4xx_).value()); + EXPECT_EQ( + 1U, + filter_callbacks_.clusterInfo()->statsScope().counterFromStatName(upstream_rq_429_).value()); +} + +TEST_F(HttpRateLimitFilterTest, LimitResponseWithBodyAndContentType) { + SetUpTest(filter_config_); + InSequence s; + + EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _, _, _)) + .WillOnce(SetArgReferee<1>(descriptor_)); + EXPECT_CALL(*client_, limit(_, _, _, _, _)) + .WillOnce( + WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { + request_callbacks_ = &callbacks; + }))); + + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(request_headers_, false)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, + filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); + EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); + + const std::string response_body = R"EOF( + { "message": "this is a custom over limit response body as json.", "retry-after": "33" } + )EOF"; + const std::string content_length = std::to_string(response_body.length()); + Http::HeaderMapPtr rl_headers{ + new Http::TestResponseHeaderMapImpl{{"content-type", "application/json"}, + {"x-ratelimit-limit", "1000"}, + {"x-ratelimit-remaining", "0"}, + {"retry-after", "33"}}}; + Http::TestResponseHeaderMapImpl expected_headers{}; + // We construct the expected_headers map in careful order, because HeaderMapEqualRef below + // compares two header maps in order. In practice, content-length and content-type headers + // are added before additional ratelimit headers and the final x-envoy-ratelimited header. + // Additionally, we skip explicitly adding content-type here because it's already part of + // `rl_headers` above. + expected_headers.addCopy(":status", "429"); + expected_headers.addCopy("content-length", std::string(content_length)); + expected_headers.copyFrom(*rl_headers); + expected_headers.addCopy("x-envoy-ratelimited", Http::Headers::get().EnvoyRateLimitedValues.True); + + EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), false)); + EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); + EXPECT_CALL(filter_callbacks_, encodeData(_, true)) + .WillOnce( + Invoke([&](Buffer::Instance& data, bool) { EXPECT_EQ(data.toString(), response_body); })); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + + Http::HeaderMapPtr request_headers_to_add{ + new Http::TestRequestHeaderMapImpl{{"x-rls-rate-limited", "true"}}}; + + Http::ResponseHeaderMapPtr h{new Http::TestResponseHeaderMapImpl(*rl_headers)}; + Http::RequestHeaderMapPtr uh{new Http::TestRequestHeaderMapImpl(*request_headers_to_add)}; + request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, + std::move(h), std::move(uh), response_body); EXPECT_THAT(*request_headers_to_add, Not(IsSubsetOfHeaders(request_headers_))); EXPECT_EQ(1U, filter_callbacks_.clusterInfo() @@ -618,7 +750,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithFilterHeaders) { auto descriptor_statuses_ptr = std::make_unique(descriptor_statuses); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, - std::move(descriptor_statuses_ptr), nullptr, nullptr); + std::move(descriptor_statuses_ptr), nullptr, nullptr, ""); EXPECT_EQ(1U, filter_callbacks_.clusterInfo() ->statsScope() .counterFromStatName(ratelimit_over_limit_) @@ -654,7 +786,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithoutEnvoyRateLimitedHeader) { setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, - std::move(h), nullptr); + std::move(h), nullptr, ""); EXPECT_EQ(1U, filter_callbacks_.clusterInfo() ->statsScope() @@ -689,7 +821,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseRuntimeDisabled) { EXPECT_CALL(filter_callbacks_, continueDecoding()); Http::ResponseHeaderMapPtr h{new Http::TestResponseHeaderMapImpl()}; request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, - std::move(h), nullptr); + std::move(h), nullptr, ""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); @@ -839,7 +971,7 @@ TEST_F(HttpRateLimitFilterTest, InternalRequestType) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -883,7 +1015,7 @@ TEST_F(HttpRateLimitFilterTest, ExternalRequestType) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -937,7 +1069,7 @@ TEST_F(HttpRateLimitFilterTest, DEPRECATED_FEATURE_TEST(ExcludeVirtualHost)) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -988,7 +1120,7 @@ TEST_F(HttpRateLimitFilterTest, OverrideVHRateLimitOptionWithRouteRateLimitSet) .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -1039,7 +1171,7 @@ TEST_F(HttpRateLimitFilterTest, OverrideVHRateLimitOptionWithoutRouteRateLimit) .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -1087,7 +1219,7 @@ TEST_F(HttpRateLimitFilterTest, IncludeVHRateLimitOptionWithOnlyVHRateLimitSet) .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -1137,7 +1269,7 @@ TEST_F(HttpRateLimitFilterTest, IncludeVHRateLimitOptionWithRouteAndVHRateLimitS .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -1185,7 +1317,7 @@ TEST_F(HttpRateLimitFilterTest, IgnoreVHRateLimitOptionWithRouteRateLimitSet) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); diff --git a/test/extensions/filters/network/ratelimit/ratelimit_test.cc b/test/extensions/filters/network/ratelimit/ratelimit_test.cc index f2255e356cd2..019ad7000909 100644 --- a/test/extensions/filters/network/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/network/ratelimit/ratelimit_test.cc @@ -115,7 +115,7 @@ TEST_F(RateLimitFilterTest, OK) { EXPECT_CALL(filter_callbacks_, continueReading()); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); @@ -143,7 +143,7 @@ TEST_F(RateLimitFilterTest, OverLimit) { EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); EXPECT_CALL(*client_, cancel()).Times(0); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); @@ -172,7 +172,7 @@ TEST_F(RateLimitFilterTest, OverLimitNotEnforcing) { EXPECT_CALL(*client_, cancel()).Times(0); EXPECT_CALL(filter_callbacks_, continueReading()); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); @@ -197,7 +197,7 @@ TEST_F(RateLimitFilterTest, Error) { EXPECT_CALL(filter_callbacks_, continueReading()); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); @@ -238,7 +238,7 @@ TEST_F(RateLimitFilterTest, ImmediateOK) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_EQ(Network::FilterStatus::Continue, filter_->onNewConnection()); @@ -262,7 +262,7 @@ TEST_F(RateLimitFilterTest, ImmediateError) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_EQ(Network::FilterStatus::Continue, filter_->onNewConnection()); @@ -305,7 +305,7 @@ TEST_F(RateLimitFilterTest, ErrorResponseWithFailureModeAllowOff) { Buffer::OwnedImpl data("hello"); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(Network::FilterStatus::Continue, filter_->onData(data, false)); diff --git a/test/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit_test.cc b/test/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit_test.cc index 131bb6513f58..c56c9383b330 100644 --- a/test/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit_test.cc @@ -227,7 +227,7 @@ TEST_F(ThriftRateLimitFilterTest, OkResponse) { setResponseFlag(StreamInfo::ResponseFlag::RateLimited)) .Times(0); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("ratelimit.ok").value()); @@ -247,7 +247,7 @@ TEST_F(ThriftRateLimitFilterTest, ImmediateOkResponse) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -271,7 +271,7 @@ TEST_F(ThriftRateLimitFilterTest, ImmediateErrorResponse) { .WillOnce( WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { callbacks.complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); }))); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); @@ -301,7 +301,7 @@ TEST_F(ThriftRateLimitFilterTest, ErrorResponse) { EXPECT_CALL(filter_callbacks_, continueDecoding()); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->messageEnd()); EXPECT_CALL(filter_callbacks_.stream_info_, @@ -339,7 +339,7 @@ TEST_F(ThriftRateLimitFilterTest, ErrorResponseWithFailureModeAllowOff) { EXPECT_CALL(filter_callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::RateLimitServiceError)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::Error, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ( 1U, @@ -373,7 +373,7 @@ TEST_F(ThriftRateLimitFilterTest, LimitResponse) { EXPECT_CALL(filter_callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("ratelimit.over_limit") @@ -406,7 +406,7 @@ TEST_F(ThriftRateLimitFilterTest, LimitResponseWithHeaders) { Http::ResponseHeaderMapPtr h{new Http::TestResponseHeaderMapImpl(*rl_headers)}; request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, - std::move(h), nullptr); + std::move(h), nullptr, ""); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("ratelimit.over_limit") @@ -431,7 +431,7 @@ TEST_F(ThriftRateLimitFilterTest, LimitResponseRuntimeDisabled) { .WillOnce(Return(false)); EXPECT_CALL(filter_callbacks_, continueDecoding()); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, nullptr, - nullptr); + nullptr, ""); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("ratelimit.over_limit") diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 6b4abcd7c63c..7580a576e56d 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -857,6 +857,11 @@ template class TestHeaderMapImplBase : public Inte HeaderMapImpl::copyFrom(*header_map_, rhs); header_map_->verifyByteSizeInternalForTest(); } + void copyFrom(const TestHeaderMapImplBase& rhs) { copyFrom(*rhs.header_map_); } + void copyFrom(const HeaderMap& rhs) { + HeaderMapImpl::copyFrom(*header_map_, rhs); + header_map_->verifyByteSizeInternalForTest(); + } TestHeaderMapImplBase& operator=(const TestHeaderMapImplBase& rhs) { if (this == &rhs) { return *this; From ba656eb98fe127a271a0196a4a89c0d4bed17708 Mon Sep 17 00:00:00 2001 From: itamarkam Date: Wed, 16 Dec 2020 04:47:15 +0200 Subject: [PATCH 44/49] formatter: add a formatter that returns a google::protobuf::Struct rather than a string (#14258) add a formatter that returns google::protobuf::Struct rather than a string Signed-off-by: Itamar Kaminski --- .../formatter/substitution_formatter.cc | 36 ++-- .../common/formatter/substitution_formatter.h | 61 ++++--- .../substitution_format_string_test.cc | 2 +- .../substitution_formatter_speed_test.cc | 65 ++++++- .../formatter/substitution_formatter_test.cc | 160 ++++++++++-------- 5 files changed, 208 insertions(+), 116 deletions(-) diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index 9b121db96803..fc6a92fa9995 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -49,8 +49,8 @@ const std::regex& getStartTimeNewlinePattern() { } const std::regex& getNewlinePattern() { CONSTRUCT_ON_FIRST_USE(std::regex, "\n"); } -template struct JsonFormatMapVisitor : Ts... { using Ts::operator()...; }; -template JsonFormatMapVisitor(Ts...) -> JsonFormatMapVisitor; +template struct StructFormatMapVisitor : Ts... { using Ts::operator()...; }; +template StructFormatMapVisitor(Ts...) -> StructFormatMapVisitor; } // namespace @@ -135,17 +135,17 @@ std::string JsonFormatterImpl::format(const Http::RequestHeaderMap& request_head const Http::ResponseTrailerMap& response_trailers, const StreamInfo::StreamInfo& stream_info, absl::string_view local_reply_body) const { - const auto output_struct = - toStruct(request_headers, response_headers, response_trailers, stream_info, local_reply_body); + const auto output_struct = struct_formatter_.format( + request_headers, response_headers, response_trailers, stream_info, local_reply_body); const std::string log_line = MessageUtil::getJsonStringFromMessage(output_struct, false, true); return absl::StrCat(log_line, "\n"); } -JsonFormatterImpl::JsonFormatMapWrapper -JsonFormatterImpl::toFormatMap(const ProtobufWkt::Struct& json_format) const { - auto output = std::make_unique(); - for (const auto& pair : json_format.fields()) { +StructFormatter::StructFormatMapWrapper +StructFormatter::toFormatMap(const ProtobufWkt::Struct& struct_format) const { + auto output = std::make_unique(); + for (const auto& pair : struct_format.fields()) { switch (pair.second.kind_case()) { case ProtobufWkt::Value::kStringValue: output->emplace(pair.first, SubstitutionFormatParser::parse(pair.second.string_value())); @@ -155,17 +155,17 @@ JsonFormatterImpl::toFormatMap(const ProtobufWkt::Struct& json_format) const { break; default: throw EnvoyException( - "Only string values or nested structs are supported in the JSON access log format."); + "Only string values or nested structs are supported in structured access log format."); } } return {std::move(output)}; }; -ProtobufWkt::Struct JsonFormatterImpl::toStruct(const Http::RequestHeaderMap& request_headers, - const Http::ResponseHeaderMap& response_headers, - const Http::ResponseTrailerMap& response_trailers, - const StreamInfo::StreamInfo& stream_info, - absl::string_view local_reply_body) const { +ProtobufWkt::Struct StructFormatter::format(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, + const StreamInfo::StreamInfo& stream_info, + absl::string_view local_reply_body) const { const std::string& empty_value = omit_empty_values_ ? EMPTY_STRING : DefaultUnspecifiedValueString; const std::function&)> @@ -197,11 +197,11 @@ ProtobufWkt::Struct JsonFormatterImpl::toStruct(const Http::RequestHeaderMap& re } return ValueUtil::stringValue(str); }; - const std::function - json_format_map_callback = [&](const JsonFormatterImpl::JsonFormatMapWrapper& format) { + const std::function + struct_format_map_callback = [&](const StructFormatter::StructFormatMapWrapper& format) { ProtobufWkt::Struct output; auto* fields = output.mutable_fields(); - JsonFormatMapVisitor visitor{json_format_map_callback, providers_callback}; + StructFormatMapVisitor visitor{struct_format_map_callback, providers_callback}; for (const auto& pair : *format.value_) { ProtobufWkt::Value value = absl::visit(visitor, pair.second); if (omit_empty_values_ && value.kind_case() == ProtobufWkt::Value::kNullValue) { @@ -211,7 +211,7 @@ ProtobufWkt::Struct JsonFormatterImpl::toStruct(const Http::RequestHeaderMap& re } return ValueUtil::structValue(output); }; - return json_format_map_callback(json_output_format_).struct_value(); + return struct_format_map_callback(struct_output_format_).struct_value(); } void SubstitutionFormatParser::parseCommandHeader(const std::string& token, const size_t start, diff --git a/source/common/formatter/substitution_formatter.h b/source/common/formatter/substitution_formatter.h index 6a5fc45fb04e..66ac8f83e283 100644 --- a/source/common/formatter/substitution_formatter.h +++ b/source/common/formatter/substitution_formatter.h @@ -107,12 +107,47 @@ class FormatterImpl : public Formatter { std::vector providers_; }; +/** + * An formatter for structured log formats, which returns a Struct proto that + * can be converted easily into multiple formats. + */ +class StructFormatter { +public: + StructFormatter(const ProtobufWkt::Struct& format_mapping, bool preserve_types, + bool omit_empty_values) + : omit_empty_values_(omit_empty_values), preserve_types_(preserve_types), + struct_output_format_(toFormatMap(format_mapping)) {} + + ProtobufWkt::Struct format(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, + const StreamInfo::StreamInfo& stream_info, + absl::string_view local_reply_body) const; + +private: + struct StructFormatMapWrapper; + using StructFormatMapValue = + absl::variant, const StructFormatMapWrapper>; + // Although not required for Struct/JSON, it is nice to have the order of + // properties preserved between the format and the log entry, thus std::map. + using StructFormatMap = std::map; + using StructFormatMapPtr = std::unique_ptr; + struct StructFormatMapWrapper { + StructFormatMapPtr value_; + }; + + StructFormatMapWrapper toFormatMap(const ProtobufWkt::Struct& struct_format) const; + + const bool omit_empty_values_; + const bool preserve_types_; + const StructFormatMapWrapper struct_output_format_; +}; + class JsonFormatterImpl : public Formatter { public: JsonFormatterImpl(const ProtobufWkt::Struct& format_mapping, bool preserve_types, bool omit_empty_values) - : omit_empty_values_(omit_empty_values), preserve_types_(preserve_types), - json_output_format_(toFormatMap(format_mapping)) {} + : struct_formatter_(format_mapping, preserve_types, omit_empty_values) {} // Formatter::format std::string format(const Http::RequestHeaderMap& request_headers, @@ -122,27 +157,7 @@ class JsonFormatterImpl : public Formatter { absl::string_view local_reply_body) const override; private: - struct JsonFormatMapWrapper; - using JsonFormatMapValue = - absl::variant, const JsonFormatMapWrapper>; - // Although not required for JSON, it is nice to have the order of properties - // preserved between the format and the log entry, thus std::map. - using JsonFormatMap = std::map; - using JsonFormatMapPtr = std::unique_ptr; - struct JsonFormatMapWrapper { - JsonFormatMapPtr value_; - }; - - bool omit_empty_values_; - bool preserve_types_; - const JsonFormatMapWrapper json_output_format_; - - ProtobufWkt::Struct toStruct(const Http::RequestHeaderMap& request_headers, - const Http::ResponseHeaderMap& response_headers, - const Http::ResponseTrailerMap& response_trailers, - const StreamInfo::StreamInfo& stream_info, - absl::string_view local_reply_body) const; - JsonFormatMapWrapper toFormatMap(const ProtobufWkt::Struct& json_format) const; + const StructFormatter struct_formatter_; }; /** diff --git a/test/common/formatter/substitution_format_string_test.cc b/test/common/formatter/substitution_format_string_test.cc index 53c9a2086b29..105c18f0038b 100644 --- a/test/common/formatter/substitution_format_string_test.cc +++ b/test/common/formatter/substitution_format_string_test.cc @@ -93,7 +93,7 @@ TEST_F(SubstitutionFormatStringUtilsTest, TestInvalidConfigs) { TestUtility::loadFromYaml(yaml, config_); EXPECT_THROW_WITH_MESSAGE( SubstitutionFormatStringUtils::fromProtoConfig(config_, context_.api()), EnvoyException, - "Only string values or nested structs are supported in the JSON access log format."); + "Only string values or nested structs are supported in structured access log format."); } } diff --git a/test/common/formatter/substitution_formatter_speed_test.cc b/test/common/formatter/substitution_formatter_speed_test.cc index bf06c39b4e49..5cedca3fe04d 100644 --- a/test/common/formatter/substitution_formatter_speed_test.cc +++ b/test/common/formatter/substitution_formatter_speed_test.cc @@ -28,6 +28,24 @@ std::unique_ptr makeJsonFormatter(bool type return std::make_unique(JsonLogFormat, typed, false); } +std::unique_ptr makeStructFormatter(bool typed) { + ProtobufWkt::Struct StructLogFormat; + const std::string format_yaml = R"EOF( + remote_address: '%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%' + start_time: '%START_TIME(%Y/%m/%dT%H:%M:%S%z %s)%' + method: '%REQ(:METHOD)%' + url: '%REQ(X-FORWARDED-PROTO)%://%REQ(:AUTHORITY)%%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%' + protocol: '%PROTOCOL%' + respoinse_code: '%RESPONSE_CODE%' + bytes_sent: '%BYTES_SENT%' + duration: '%DURATION%' + referer: '%REQ(REFERER)%' + user-agent: '%REQ(USER-AGENT)%' + )EOF"; + TestUtility::loadFromYaml(format_yaml, StructLogFormat); + return std::make_unique(StructLogFormat, typed, false); +} + std::unique_ptr makeStreamInfo() { auto stream_info = std::make_unique(); stream_info->setDownstreamRemoteAddress( @@ -54,7 +72,7 @@ static void BM_AccessLogFormatter(benchmark::State& state) { Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; std::string body; - for (auto _ : state) { + for (auto _ : state) { // NOLINT: Silences warning about dead store output_bytes += formatter->format(request_headers, response_headers, response_trailers, *stream_info, body) .length(); @@ -63,6 +81,47 @@ static void BM_AccessLogFormatter(benchmark::State& state) { } BENCHMARK(BM_AccessLogFormatter); +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_StructAccessLogFormatter(benchmark::State& state) { + std::unique_ptr stream_info = makeStreamInfo(); + std::unique_ptr struct_formatter = makeStructFormatter(false); + + size_t output_bytes = 0; + Http::TestRequestHeaderMapImpl request_headers; + Http::TestResponseHeaderMapImpl response_headers; + Http::TestResponseTrailerMapImpl response_trailers; + std::string body; + for (auto _ : state) { // NOLINT: Silences warning about dead store + output_bytes += + struct_formatter + ->format(request_headers, response_headers, response_trailers, *stream_info, body) + .ByteSize(); + } + benchmark::DoNotOptimize(output_bytes); +} +BENCHMARK(BM_StructAccessLogFormatter); + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_TypedStructAccessLogFormatter(benchmark::State& state) { + std::unique_ptr stream_info = makeStreamInfo(); + std::unique_ptr typed_struct_formatter = + makeStructFormatter(true); + + size_t output_bytes = 0; + Http::TestRequestHeaderMapImpl request_headers; + Http::TestResponseHeaderMapImpl response_headers; + Http::TestResponseTrailerMapImpl response_trailers; + std::string body; + for (auto _ : state) { // NOLINT: Silences warning about dead store + output_bytes += + typed_struct_formatter + ->format(request_headers, response_headers, response_trailers, *stream_info, body) + .ByteSize(); + } + benchmark::DoNotOptimize(output_bytes); +} +BENCHMARK(BM_TypedStructAccessLogFormatter); + // NOLINTNEXTLINE(readability-identifier-naming) static void BM_JsonAccessLogFormatter(benchmark::State& state) { std::unique_ptr stream_info = makeStreamInfo(); @@ -73,7 +132,7 @@ static void BM_JsonAccessLogFormatter(benchmark::State& state) { Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; std::string body; - for (auto _ : state) { + for (auto _ : state) { // NOLINT: Silences warning about dead store output_bytes += json_formatter ->format(request_headers, response_headers, response_trailers, *stream_info, body) @@ -94,7 +153,7 @@ static void BM_TypedJsonAccessLogFormatter(benchmark::State& state) { Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; std::string body; - for (auto _ : state) { + for (auto _ : state) { // NOLINT: Silences warning about dead store output_bytes += typed_json_formatter ->format(request_headers, response_headers, response_trailers, *stream_info, body) diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index 6e16c79e6992..81a0ba4f7f36 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -1541,22 +1541,14 @@ TEST(SubstitutionFormatterTest, GrpcStatusFormatterTest) { } } -void verifyJsonOutput(std::string json_string, - absl::node_hash_map expected_map) { - const auto parsed = Json::Factory::loadFromString(json_string); - - // Every json log line should have only one newline character, and it should be the last character - // in the string - const auto newline_pos = json_string.find('\n'); - EXPECT_NE(newline_pos, std::string::npos); - EXPECT_EQ(newline_pos, json_string.length() - 1); - +void verifyStructOutput(ProtobufWkt::Struct output, + absl::node_hash_map expected_map) { for (const auto& pair : expected_map) { - EXPECT_EQ(parsed->getString(pair.first), pair.second); + EXPECT_EQ(output.fields().at(pair.first).string_value(), pair.second); } } -TEST(SubstitutionFormatterTest, JsonFormatterPlainStringTest) { +TEST(SubstitutionFormatterTest, StructFormatterPlainStringTest) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header; Http::TestResponseHeaderMapImpl response_header; @@ -1576,14 +1568,14 @@ TEST(SubstitutionFormatterTest, JsonFormatterPlainStringTest) { plain_string: plain_string_value )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_header, response_header, response_trailer, stream_info, body), expected_json_map); } -TEST(SubstitutionFormatterTest, JsonFormatterNestedObject) { +TEST(SubstitutionFormatterTest, StructFormatterNestedObject) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header; Http::TestResponseHeaderMapImpl response_header; @@ -1604,9 +1596,9 @@ TEST(SubstitutionFormatterTest, JsonFormatterNestedObject) { protocol: '%PROTOCOL%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); - const std::string expected = R"EOF({ + const ProtobufWkt::Struct expected = TestUtility::jsonToStruct(R"EOF({ "level_one": { "level_two": { "level_three": { @@ -1615,13 +1607,13 @@ TEST(SubstitutionFormatterTest, JsonFormatterNestedObject) { } } } - })EOF"; - std::string out_json = + })EOF"); + const ProtobufWkt::Struct out_struct = formatter.format(request_header, response_header, response_trailer, stream_info, body); - EXPECT_TRUE(TestUtility::jsonStringEqual(out_json, expected)); + EXPECT_TRUE(TestUtility::protoEqual(out_struct, expected)); } -TEST(SubstitutionFormatterTest, JsonFormatterSingleOperatorTest) { +TEST(SubstitutionFormatterTest, StructFormatterSingleOperatorTest) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header; Http::TestResponseHeaderMapImpl response_header; @@ -1640,14 +1632,14 @@ TEST(SubstitutionFormatterTest, JsonFormatterSingleOperatorTest) { protocol: '%PROTOCOL%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_header, response_header, response_trailer, stream_info, body), expected_json_map); } -TEST(SubstitutionFormatterTest, JsonFormatterNonExistentHeaderTest) { +TEST(SubstitutionFormatterTest, StructFormatterNonExistentHeaderTest) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header{{"some_request_header", "SOME_REQUEST_HEADER"}}; Http::TestResponseHeaderMapImpl response_header{{"some_response_header", "SOME_RESPONSE_HEADER"}}; @@ -1668,17 +1660,17 @@ TEST(SubstitutionFormatterTest, JsonFormatterNonExistentHeaderTest) { some_response_header: '%RESP(some_response_header)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); absl::optional protocol = Http::Protocol::Http11; EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_header, response_header, response_trailer, stream_info, body), expected_json_map); } -TEST(SubstitutionFormatterTest, JsonFormatterAlternateHeaderTest) { +TEST(SubstitutionFormatterTest, StructFormatterAlternateHeaderTest) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header{ {"request_present_header", "REQUEST_PRESENT_HEADER"}}; @@ -1701,17 +1693,17 @@ TEST(SubstitutionFormatterTest, JsonFormatterAlternateHeaderTest) { response_present_header_or_response_absent_header: '%RESP(response_present_header?response_absent_header)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); absl::optional protocol = Http::Protocol::Http11; EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_header, response_header, response_trailer, stream_info, body), expected_json_map); } -TEST(SubstitutionFormatterTest, JsonFormatterDynamicMetadataTest) { +TEST(SubstitutionFormatterTest, StructFormatterDynamicMetadataTest) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header{{"first", "GET"}, {":path", "/"}}; Http::TestResponseHeaderMapImpl response_header{{"second", "PUT"}, {"test", "test"}}; @@ -1735,14 +1727,14 @@ TEST(SubstitutionFormatterTest, JsonFormatterDynamicMetadataTest) { test_obj.inner_key: '%DYNAMIC_METADATA(com.test:test_obj:inner_key)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_header, response_header, response_trailer, stream_info, body), expected_json_map); } -TEST(SubstitutionFormatterTest, JsonFormatterTypedDynamicMetadataTest) { +TEST(SubstitutionFormatterTest, StructFormatterTypedDynamicMetadataTest) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header{{"first", "GET"}, {":path", "/"}}; Http::TestResponseHeaderMapImpl response_header{{"second", "PUT"}, {"test", "test"}}; @@ -1761,12 +1753,10 @@ TEST(SubstitutionFormatterTest, JsonFormatterTypedDynamicMetadataTest) { test_obj.inner_key: '%DYNAMIC_METADATA(com.test:test_obj:inner_key)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, true, false); + StructFormatter formatter(key_mapping, true, false); - const std::string json = + ProtobufWkt::Struct output = formatter.format(request_header, response_header, response_trailer, stream_info, body); - ProtobufWkt::Struct output; - MessageUtil::loadFromJson(json, output); const auto& fields = output.fields(); EXPECT_EQ("test_value", fields.at("test_key").string_value()); @@ -1775,7 +1765,7 @@ TEST(SubstitutionFormatterTest, JsonFormatterTypedDynamicMetadataTest) { fields.at("test_obj").struct_value().fields().at("inner_key").string_value()); } -TEST(SubstitutionFormatterTest, JsonFormatterFilterStateTest) { +TEST(SubstitutionFormatterTest, StructFormatterFilterStateTest) { Http::TestRequestHeaderMapImpl request_headers; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; @@ -1798,14 +1788,14 @@ TEST(SubstitutionFormatterTest, JsonFormatterFilterStateTest) { test_obj: '%FILTER_STATE(test_obj)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_headers, response_headers, response_trailers, stream_info, body), expected_json_map); } -TEST(SubstitutionFormatterTest, JsonFormatterOmitEmptyTest) { +TEST(SubstitutionFormatterTest, StructFormatterOmitEmptyTest) { Http::TestRequestHeaderMapImpl request_headers; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; @@ -1823,14 +1813,14 @@ TEST(SubstitutionFormatterTest, JsonFormatterOmitEmptyTest) { test_key_dynamic_metadata: '%DYNAMIC_METADATA(nonexistent_key)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, true); + StructFormatter formatter(key_mapping, false, true); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_headers, response_headers, response_trailers, stream_info, body), {}); } -TEST(SubstitutionFormatterTest, JsonFormatterTypedFilterStateTest) { +TEST(SubstitutionFormatterTest, StructFormatterTypedFilterStateTest) { Http::TestRequestHeaderMapImpl request_headers; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; @@ -1850,12 +1840,10 @@ TEST(SubstitutionFormatterTest, JsonFormatterTypedFilterStateTest) { test_obj: '%FILTER_STATE(test_obj)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, true, false); + StructFormatter formatter(key_mapping, true, false); - std::string json = + ProtobufWkt::Struct output = formatter.format(request_headers, response_headers, response_trailers, stream_info, body); - ProtobufWkt::Struct output; - MessageUtil::loadFromJson(json, output); const auto& fields = output.fields(); EXPECT_EQ("test_value", fields.at("test_key").string_value()); @@ -1887,9 +1875,9 @@ TEST(SubstitutionFormatterTest, FilterStateSpeciferTest) { test_key_typed: '%FILTER_STATE(test_key:TYPED)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_headers, response_headers, response_trailers, stream_info, body), expected_json_map); } @@ -1913,14 +1901,11 @@ TEST(SubstitutionFormatterTest, TypedFilterStateSpeciferTest) { test_key_typed: '%FILTER_STATE(test_key:TYPED)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, true, false); + StructFormatter formatter(key_mapping, true, false); - std::string json = + ProtobufWkt::Struct output = formatter.format(request_headers, response_headers, response_trailers, stream_info, body); - ProtobufWkt::Struct output; - MessageUtil::loadFromJson(json, output); - const auto& fields = output.fields(); EXPECT_EQ("test_value By PLAIN", fields.at("test_key_plain").string_value()); EXPECT_EQ("test_value By TYPED", fields.at("test_key_typed").string_value()); @@ -1944,11 +1929,11 @@ TEST(SubstitutionFormatterTest, FilterStateErrorSpeciferTest) { test_key_typed: '%FILTER_STATE(test_key:TYPED)%' )EOF", key_mapping); - EXPECT_THROW_WITH_MESSAGE(JsonFormatterImpl formatter(key_mapping, false, false), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(StructFormatter formatter(key_mapping, false, false), EnvoyException, "Invalid filter state serialize type, only support PLAIN/TYPED."); } -TEST(SubstitutionFormatterTest, JsonFormatterStartTimeTest) { +TEST(SubstitutionFormatterTest, StructFormatterStartTimeTest) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header; Http::TestResponseHeaderMapImpl response_header; @@ -1975,14 +1960,14 @@ TEST(SubstitutionFormatterTest, JsonFormatterStartTimeTest) { all_zeroes: '%START_TIME(%f.%1f.%2f.%3f)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, false, false); + StructFormatter formatter(key_mapping, false, false); - verifyJsonOutput( + verifyStructOutput( formatter.format(request_header, response_header, response_trailer, stream_info, body), expected_json_map); } -TEST(SubstitutionFormatterTest, JsonFormatterMultiTokenTest) { +TEST(SubstitutionFormatterTest, StructFormatterMultiTokenTest) { { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header{{"some_request_header", "SOME_REQUEST_HEADER"}}; @@ -2000,21 +1985,19 @@ TEST(SubstitutionFormatterTest, JsonFormatterMultiTokenTest) { )EOF", key_mapping); for (const bool preserve_types : {false, true}) { - JsonFormatterImpl formatter(key_mapping, preserve_types, false); + StructFormatter formatter(key_mapping, preserve_types, false); absl::optional protocol = Http::Protocol::Http11; EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); - const auto parsed = Json::Factory::loadFromString( - formatter.format(request_header, response_header, response_trailer, stream_info, body)); - for (const auto& pair : expected_json_map) { - EXPECT_EQ(parsed->getString(pair.first), pair.second); - } + verifyStructOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected_json_map); } } } -TEST(SubstitutionFormatterTest, JsonFormatterTypedTest) { +TEST(SubstitutionFormatterTest, StructFormatterTypedTest) { Http::TestRequestHeaderMapImpl request_headers; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; @@ -2043,12 +2026,10 @@ TEST(SubstitutionFormatterTest, JsonFormatterTypedTest) { filter_state: '%FILTER_STATE(test_obj)%' )EOF", key_mapping); - JsonFormatterImpl formatter(key_mapping, true, false); + StructFormatter formatter(key_mapping, true, false); - const auto json = + ProtobufWkt::Struct output = formatter.format(request_headers, response_headers, response_trailers, stream_info, body); - ProtobufWkt::Struct output; - MessageUtil::loadFromJson(json, output); EXPECT_THAT(output.fields().at("request_duration"), ProtoEq(ValueUtil::numberValue(5.0))); EXPECT_THAT(output.fields().at("request_duration_multi"), ProtoEq(ValueUtil::stringValue("5ms"))); @@ -2058,6 +2039,43 @@ TEST(SubstitutionFormatterTest, JsonFormatterTypedTest) { EXPECT_THAT(output.fields().at("filter_state"), ProtoEq(expected)); } +TEST(SubstitutionFormatterTest, JsonFormatterTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header; + Http::TestResponseHeaderMapImpl response_header; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + envoy::config::core::v3::Metadata metadata; + populateMetadataTestData(metadata); + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + EXPECT_CALL(Const(stream_info), lastDownstreamRxByteReceived()) + .WillRepeatedly(Return(std::chrono::nanoseconds(5000000))); + + ProtobufWkt::Struct key_mapping; + TestUtility::loadFromYaml(R"EOF( + request_duration: '%REQUEST_DURATION%' + nested_level: + plain_string: plain_string_value + protocol: '%PROTOCOL%' + )EOF", + key_mapping); + JsonFormatterImpl formatter(key_mapping, false, false); + + const std::string expected = R"EOF({ + "request_duration": "5", + "nested_level": { + "plain_string": "plain_string_value", + "protocol": "HTTP/1.1" + } + })EOF"; + + const std::string out_json = + formatter.format(request_header, response_header, response_trailer, stream_info, body); + EXPECT_TRUE(TestUtility::jsonStringEqual(out_json, expected)); +} + TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { StreamInfo::MockStreamInfo stream_info; Http::TestRequestHeaderMapImpl request_header{{"first", "GET"}, {":path", "/"}}; From 95b7ef27eeda0bde0858257925a8c262baa6b3a2 Mon Sep 17 00:00:00 2001 From: Taylor Barrella Date: Wed, 16 Dec 2020 05:41:06 -0800 Subject: [PATCH 45/49] integration tests: re-enable set_node_on_first_message_only (#14270) A series of changes and reverts caused this to seemingly unintentionally stay disabled Signed-off-by: Taylor Barrella --- test/config/utility.cc | 5 +-- .../aggregate/cluster_integration_test.cc | 2 +- test/integration/ads_integration_test.cc | 45 +++++++++++++------ test/integration/base_integration_test.h | 8 +--- test/integration/rtds_integration_test.cc | 2 +- 5 files changed, 38 insertions(+), 24 deletions(-) diff --git a/test/config/utility.cc b/test/config/utility.cc index 07ef93922d87..a5fa2ddab6d0 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -267,8 +267,6 @@ name: squash )EOF"; } -// TODO(fredlas) set_node_on_first_message_only was true; the delta+SotW unification -// work restores it here. // TODO(#6327) cleaner approach to testing with static config. std::string ConfigHelper::discoveredClustersBootstrap(const std::string& api_type) { return fmt::format( @@ -288,7 +286,7 @@ std::string ConfigHelper::discoveredClustersBootstrap(const std::string& api_typ grpc_services: envoy_grpc: cluster_name: my_cds_cluster - set_node_on_first_message_only: false + set_node_on_first_message_only: true static_resources: clusters: - name: my_cds_cluster @@ -360,6 +358,7 @@ std::string ConfigHelper::adsBootstrap(const std::string& api_type, ads_config: transport_api_version: {2} api_type: {0} + set_node_on_first_message_only: true static_resources: clusters: name: dummy_cluster diff --git a/test/extensions/clusters/aggregate/cluster_integration_test.cc b/test/extensions/clusters/aggregate/cluster_integration_test.cc index 57bd1b731fb0..419f4014a2d6 100644 --- a/test/extensions/clusters/aggregate/cluster_integration_test.cc +++ b/test/extensions/clusters/aggregate/cluster_integration_test.cc @@ -45,7 +45,7 @@ const std::string& config() { grpc_services: envoy_grpc: cluster_name: my_cds_cluster - set_node_on_first_message_only: false + set_node_on_first_message_only: true static_resources: clusters: - name: my_cds_cluster diff --git a/test/integration/ads_integration_test.cc b/test/integration/ads_integration_test.cc index 1f49cd284222..b178297fe127 100644 --- a/test/integration/ads_integration_test.cc +++ b/test/integration/ads_integration_test.cc @@ -188,7 +188,7 @@ TEST_P(AdsIntegrationTest, Failure) { EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "", {}, {}, {})); EXPECT_TRUE(compareDiscoveryRequest( - Config::TypeUrl::get().Cluster, "", {}, {}, {}, true, + Config::TypeUrl::get().Cluster, "", {}, {}, {}, false, Grpc::Status::WellKnownGrpcStatus::Internal, fmt::format("does not match the message-wide type URL {}", Config::TypeUrl::get().Cluster))); sendDiscoveryResponse(Config::TypeUrl::get().Cluster, @@ -203,7 +203,7 @@ TEST_P(AdsIntegrationTest, Failure) { EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "1", {}, {}, {})); EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "", - {"cluster_0"}, {}, {}, true, + {"cluster_0"}, {}, {}, false, Grpc::Status::WellKnownGrpcStatus::Internal, fmt::format("does not match the message-wide type URL {}", Config::TypeUrl::get().ClusterLoadAssignment))); @@ -218,7 +218,7 @@ TEST_P(AdsIntegrationTest, Failure) { {buildRouteConfig("listener_0", "route_config_0")}, {}, "1"); EXPECT_TRUE(compareDiscoveryRequest( - Config::TypeUrl::get().Listener, "", {}, {}, {}, true, + Config::TypeUrl::get().Listener, "", {}, {}, {}, false, Grpc::Status::WellKnownGrpcStatus::Internal, fmt::format("does not match the message-wide type URL {}", Config::TypeUrl::get().Listener))); sendDiscoveryResponse( @@ -233,7 +233,7 @@ TEST_P(AdsIntegrationTest, Failure) { EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "1", {}, {}, {})); EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().RouteConfiguration, "", - {"route_config_0"}, {}, {}, true, + {"route_config_0"}, {}, {}, false, Grpc::Status::WellKnownGrpcStatus::Internal, fmt::format("does not match the message-wide type URL {}", Config::TypeUrl::get().RouteConfiguration))); @@ -415,7 +415,7 @@ TEST_P(AdsIntegrationTest, CdsEdsReplacementWarming) { Config::TypeUrl::get().ClusterLoadAssignment, {buildTlsClusterLoadAssignment("cluster_0")}, {buildTlsClusterLoadAssignment("cluster_0")}, {}, "2"); - EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "2", {}, {}, {}, true)); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "2", {}, {}, {})); EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "2", {"cluster_0"}, {}, {})); } @@ -1167,6 +1167,29 @@ TEST_P(AdsIntegrationTest, NodeMessage) { xds_stream_->finishGrpcStream(Grpc::Status::Ok); } +TEST_P(AdsIntegrationTest, SetNodeAlways) { + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* ads_config = bootstrap.mutable_dynamic_resources()->mutable_ads_config(); + ads_config->set_set_node_on_first_message_only(false); + }); + initialize(); + + // Check that the node is sent in each request. + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "", {}, {}, {}, true)); + sendDiscoveryResponse(Config::TypeUrl::get().Cluster, + {buildCluster("cluster_0")}, + {buildCluster("cluster_0")}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "", + {"cluster_0"}, {"cluster_0"}, {}, true)); + sendDiscoveryResponse( + Config::TypeUrl::get().ClusterLoadAssignment, {buildClusterLoadAssignment("cluster_0")}, + {buildClusterLoadAssignment("cluster_0")}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "1", {}, {}, {}, true)); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "", {}, {}, {}, true)); +}; + // Check if EDS cluster defined in file is loaded before ADS request and used as xDS server class AdsClusterFromFileIntegrationTest : public Grpc::DeltaSotwIntegrationParamTest, public HttpIntegrationTest { @@ -1247,7 +1270,7 @@ TEST_P(AdsClusterFromFileIntegrationTest, BasicTestWidsAdsEndpointLoadedFromFile xds_stream_->startGrpcStream(); EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "", - {"ads_eds_cluster"}, {"ads_eds_cluster"}, {})); + {"ads_eds_cluster"}, {"ads_eds_cluster"}, {}, true)); sendDiscoveryResponse( Config::TypeUrl::get().ClusterLoadAssignment, {buildClusterLoadAssignment("ads_eds_cluster")}, {buildClusterLoadAssignment("ads_eds_cluster")}, {}, "1"); @@ -1260,8 +1283,6 @@ TEST_P(AdsClusterFromFileIntegrationTest, BasicTestWidsAdsEndpointLoadedFromFile class AdsIntegrationTestWithRtds : public AdsIntegrationTest { public: - AdsIntegrationTestWithRtds() = default; - void initialize() override { config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto* layered_runtime = bootstrap.mutable_layered_runtime(); @@ -1294,9 +1315,9 @@ class AdsIntegrationTestWithRtds : public AdsIntegrationTest { Config::TypeUrl::get().Runtime, {some_rtds_layer}, {some_rtds_layer}, {}, "1"); test_server_->waitForCounterGe("runtime.load_success", 1); - EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "", {}, {}, {}, false)); - EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Runtime, "1", {"ads_rtds_layer"}, {}, - {}, false)); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "", {}, {}, {})); + EXPECT_TRUE( + compareDiscoveryRequest(Config::TypeUrl::get().Runtime, "1", {"ads_rtds_layer"}, {}, {})); } }; @@ -1310,8 +1331,6 @@ TEST_P(AdsIntegrationTestWithRtds, Basic) { class AdsIntegrationTestWithRtdsAndSecondaryClusters : public AdsIntegrationTestWithRtds { public: - AdsIntegrationTestWithRtdsAndSecondaryClusters() = default; - void initialize() override { config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { // Add secondary cluster to the list of static resources. diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index 4a474eab0dc8..3180cf96ef3e 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -140,13 +140,11 @@ class BaseIntegrationTest : protected Logger::Loggable { // sending/receiving to/from the (imaginary) xDS server. You should almost always use // compareDiscoveryRequest() and sendDiscoveryResponse(), but the SotW/delta-specific versions are // available if you're writing a SotW/delta-specific test. - // TODO(fredlas) expect_node was defaulting false here; the delta+SotW unification work restores - // it. AssertionResult compareDiscoveryRequest( const std::string& expected_type_url, const std::string& expected_version, const std::vector& expected_resource_names, const std::vector& expected_resource_names_added, - const std::vector& expected_resource_names_removed, bool expect_node = true, + const std::vector& expected_resource_names_removed, bool expect_node = false, const Protobuf::int32 expected_error_code = Grpc::Status::WellKnownGrpcStatus::Ok, const std::string& expected_error_message = ""); template @@ -179,11 +177,9 @@ class BaseIntegrationTest : protected Logger::Loggable { const Protobuf::int32 expected_error_code = Grpc::Status::WellKnownGrpcStatus::Ok, const std::string& expected_error_message = ""); - // TODO(fredlas) expect_node was defaulting false here; the delta+SotW unification work restores - // it. AssertionResult compareSotwDiscoveryRequest( const std::string& expected_type_url, const std::string& expected_version, - const std::vector& expected_resource_names, bool expect_node = true, + const std::vector& expected_resource_names, bool expect_node = false, const Protobuf::int32 expected_error_code = Grpc::Status::WellKnownGrpcStatus::Ok, const std::string& expected_error_message = ""); diff --git a/test/integration/rtds_integration_test.cc b/test/integration/rtds_integration_test.cc index 7eb9ccd28f31..d3a3b0655856 100644 --- a/test/integration/rtds_integration_test.cc +++ b/test/integration/rtds_integration_test.cc @@ -62,7 +62,7 @@ std::string tdsBootstrapConfig(absl::string_view api_type) { grpc_services: envoy_grpc: cluster_name: rtds_cluster - set_node_on_first_message_only: false + set_node_on_first_message_only: true - name: some_admin_layer admin_layer: {{}} admin: From ae1ed1fa74f096dabe8dd5b19fc70333621b0309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Wed, 16 Dec 2020 14:19:04 +0000 Subject: [PATCH 46/49] test: start dissolving :printers_include rule. (#14429) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additional Description: A rule that exports a header without defining all the symbols declared by that header causes ODR violations. Basically, Bazel rules should never contain "include" rules like this. Rather, printer.h and printer.cc should be served from the same library. This change is a cautious, local first step. A second step can later remove the printer_include rule and replace its uses with printer_lib. Risk Level: low Testing: Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a Signed-off-by: Thomas Köppe --- test/test_common/BUILD | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/test_common/BUILD b/test/test_common/BUILD index 8e175627ab9c..0ba5bf70ebc9 100644 --- a/test/test_common/BUILD +++ b/test/test_common/BUILD @@ -15,6 +15,7 @@ envoy_basic_cc_library( name = "printers_includes", hdrs = ["printers.h"], deps = [ + ":printers_lib", "//include/envoy/network:address_interface", ], ) @@ -86,9 +87,12 @@ envoy_cc_test_library( envoy_cc_library( name = "printers_lib", - srcs = ["printers.cc"], + srcs = [ + "printers.cc", + "printers.h", + ], deps = [ - ":printers_includes", + "//include/envoy/network:address_interface", "//source/common/buffer:buffer_lib", "//source/common/http:header_map_lib", ], From 0e00d2d3fdac3347ce0645498f02dc8dc1305a64 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Wed, 16 Dec 2020 09:59:36 -0500 Subject: [PATCH 47/49] http2: Add integration tests for METADATA and RST_STREAM frame flood mitigation for upstream servers (#14365) Signed-off-by: Yan Avlasov --- .../http2_flood_integration_test.cc | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/test/integration/http2_flood_integration_test.cc b/test/integration/http2_flood_integration_test.cc index 68988c9b4d88..d88ac81b2e3b 100644 --- a/test/integration/http2_flood_integration_test.cc +++ b/test/integration/http2_flood_integration_test.cc @@ -111,6 +111,7 @@ class Http2FloodMitigationTest : public SocketInterfaceSwap, void setNetworkConnectionBufferSize(); void beginSession() override; void prefillOutboundDownstreamQueue(uint32_t data_frame_count, uint32_t data_frame_size = 10); + IntegrationStreamDecoderPtr prefillOutboundUpstreamQueue(uint32_t frame_count); void triggerListenerDrain(); }; @@ -274,6 +275,52 @@ void Http2FloodMitigationTest::prefillOutboundDownstreamQueue(uint32_t data_fram EXPECT_EQ(0, test_server_->counter("http2.outbound_flood")->value()); } +IntegrationStreamDecoderPtr +Http2FloodMitigationTest::prefillOutboundUpstreamQueue(uint32_t frame_count) { + // This complex exchange below is to ensure that the upstream outbound queue is empty before + // forcing upstream socket to return EAGAIN. Envoy's upstream codec will send a few frames (i.e. + // SETTINGS and ACKs) after a new stream was established, and the test needs to make sure these + // are flushed into the socket. To do so the test goes through the following steps: + // 1. send request headers, do not end stream + // 2. wait for headers to be received by the upstream and send response headers without ending + // the stream + // 3. wait for the client to receive response headers + // 4. send 1 byte of data from the client + // 5. wait for 1 byte of data at the upstream. Receiving this DATA frame means that all other + // frames that Envoy sent before it were also received by the upstream and the Envoy's upstream + // outbound queue is empty. + codec_client_ = makeHttpConnection(lookupPort("http")); + auto encoder_decoder = codec_client_->startRequest(default_request_headers_); + request_encoder_ = &encoder_decoder.first; + auto response = std::move(encoder_decoder.second); + + EXPECT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + EXPECT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + EXPECT_TRUE(upstream_request_->waitForHeadersComplete()); + + upstream_request_->encodeHeaders(default_response_headers_, false); + + response->waitForHeaders(); + EXPECT_EQ("200", response->headers().getStatusValue()); + codec_client_->sendData(*request_encoder_, 1, false); + EXPECT_TRUE(upstream_request_->waitForData(*dispatcher_, 1)); + + // Make Envoy's writes into the upstream connection to return EAGAIN + writev_matcher_->setSourcePort( + fake_upstream_connection_->connection().remoteAddress()->ip()->port()); + + auto buf = serializeFrames(Http2Frame::makePingFrame(), frame_count); + + writev_matcher_->setWritevReturnsEgain(); + auto* upstream = fake_upstreams_.front().get(); + EXPECT_TRUE(upstream->rawWriteConnection(0, std::string(buf.begin(), buf.end()))); + // Wait for pre-fill data to arrive to Envoy + test_server_->waitForCounterGe("cluster.cluster_0.upstream_cx_rx_bytes_total", 500); + // Verify that pre-fill did not kill upstream connection. + EXPECT_TRUE(fake_upstream_connection_->connected()); + return response; +} + void Http2FloodMitigationTest::triggerListenerDrain() { absl::Notification drain_sequence_started; test_server_->server().dispatcher().post([this, &drain_sequence_started]() { @@ -1382,4 +1429,105 @@ TEST_P(Http2FloodMitigationTest, UpstreamPriorityOneClosedStream) { 1, test_server_->counter("cluster.cluster_0.http2.inbound_priority_frames_flood")->value()); } +// Verify that the server can detect flooding by the RST_STREAM on stream idle timeout +// after sending response headers. +TEST_P(Http2FloodMitigationTest, UpstreamRstStreamOnStreamIdleTimeout) { + // Set large buffer limits so the test is not affected by the flow control. + config_helper_.setBufferLimits(1024 * 1024 * 1024, 1024 * 1024 * 1024); + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + auto* stream_idle_timeout = hcm.mutable_stream_idle_timeout(); + std::chrono::milliseconds timeout(1000); + auto seconds = std::chrono::duration_cast(timeout); + stream_idle_timeout->set_seconds(seconds.count()); + }); + if (!initializeUpstreamFloodTest()) { + return; + } + // pre-fill upstream connection 1 away from overflow + auto response = prefillOutboundUpstreamQueue(ControlFrameFloodLimit); + + // Stream timeout should send 408 downstream and RST_STREAM upstream. + // Verify that when RST_STREAM overflows upstream queue it is handled correctly + // by causing upstream connection to be disconnected. + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); + response->waitForReset(); + // EXPECT_EQ("408", response->headers().getStatusValue()); + EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.http2.outbound_control_flood")->value()); + EXPECT_EQ(1, test_server_->counter("http.config_test.downstream_rq_idle_timeout")->value()); +} + +// Verify that the server can detect flooding by the RST_STREAM sent to upstream when downstream +// disconnects. +TEST_P(Http2FloodMitigationTest, UpstreamRstStreamOnDownstreamRemoteClose) { + if (!initializeUpstreamFloodTest()) { + return; + } + // pre-fill 1 away from overflow + auto response = prefillOutboundUpstreamQueue(ControlFrameFloodLimit); + + // Disconnect downstream connection. Envoy should send RST_STREAM to upstream which should + // overflow the queue and cause the entire upstream connection to be disconnected. + codec_client_->close(); + + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); + EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.http2.outbound_control_flood")->value()); +} + +// Verify that the server can detect flood of request METADATA frames +TEST_P(Http2FloodMitigationTest, RequestMetadata) { + config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + ASSERT(bootstrap.mutable_static_resources()->clusters_size() >= 1, ""); + ConfigHelper::HttpProtocolOptions protocol_options; + protocol_options.mutable_explicit_http_config() + ->mutable_http2_protocol_options() + ->set_allow_metadata(true); + ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), + protocol_options); + }); + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); }); + + if (!initializeUpstreamFloodTest()) { + return; + } + + codec_client_ = makeHttpConnection(lookupPort("http")); + auto encoder_decoder = codec_client_->startRequest(default_request_headers_); + request_encoder_ = &encoder_decoder.first; + auto response = std::move(encoder_decoder.second); + + // Wait for HEADERS to show up at the upstream server. + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); + + // Make Envoy's writes into the upstream connection to return EAGAIN, preventing proxying of the + // METADATA frames + writev_matcher_->setSourcePort( + fake_upstream_connection_->connection().remoteAddress()->ip()->port()); + + writev_matcher_->setWritevReturnsEgain(); + + // Send AllFrameFloodLimit + 1 number of METADATA frames from the downstream client to trigger the + // outbound upstream flood when they are proxied. + Http::MetadataMap metadata_map = { + {"header_key1", "header_value1"}, + {"header_key2", "header_value2"}, + }; + for (uint32_t frame = 0; frame < AllFrameFloodLimit + 1; ++frame) { + codec_client_->sendMetadata(*request_encoder_, metadata_map); + } + + // Upstream connection should be disconnected + // Downstream client should receive 503 since upstream did not send response headers yet + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); + response->waitForEndStream(); + EXPECT_EQ("503", response->headers().getStatusValue()); + // Verify that the flood check was triggered. + EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.http2.outbound_flood")->value()); +} + } // namespace Envoy From 09134ffd78c89406f513e5bfe9a06397bd334184 Mon Sep 17 00:00:00 2001 From: asraa Date: Wed, 16 Dec 2020 10:03:56 -0500 Subject: [PATCH 48/49] [http] Remove legacy codecs (#14381) Removes legacy codecs Signed-off-by: Asra Ali --- bazel/BUILD | 5 - bazel/envoy_build_system.bzl | 2 - bazel/envoy_select.bzl | 7 - ci/do_ci.sh | 1 - docs/root/version_history/current.rst | 1 + source/common/http/BUILD | 7 - source/common/http/codec_client.cc | 16 +- source/common/http/conn_manager_utility.cc | 26 +- source/common/http/http1/BUILD | 59 +- source/common/http/http1/codec_impl_legacy.cc | 1292 -------------- source/common/http/http1/codec_impl_legacy.h | 617 ------- source/common/http/http2/BUILD | 78 +- source/common/http/http2/codec_impl_legacy.cc | 1534 ----------------- source/common/http/http2/codec_impl_legacy.h | 623 ------- source/common/runtime/runtime_features.cc | 1 - .../network/http_connection_manager/BUILD | 2 - .../network/http_connection_manager/config.cc | 37 +- test/common/http/http1/BUILD | 1 - test/common/http/http1/codec_impl_test.cc | 446 ++--- test/common/http/http2/BUILD | 65 +- test/common/http/http2/codec_impl_test.cc | 301 +--- test/common/http/http2/codec_impl_test_util.h | 131 +- test/common/http/http2/frame_replay.h | 3 + test/common/http/http2/frame_replay_test.cc | 34 +- .../http/http2/request_header_fuzz_test.cc | 10 +- .../http/http2/response_header_fuzz_test.cc | 10 +- test/config/utility.cc | 4 - test/config/utility.h | 3 - .../http_connection_manager/config_test.cc | 64 - test/integration/BUILD | 13 - test/integration/base_integration_test.cc | 5 - test/integration/base_integration_test.h | 1 - test/integration/fake_upstream.cc | 38 +- .../http2_flood_integration_test.cc | 4 - 34 files changed, 424 insertions(+), 5017 deletions(-) delete mode 100644 source/common/http/http1/codec_impl_legacy.cc delete mode 100644 source/common/http/http1/codec_impl_legacy.h delete mode 100644 source/common/http/http2/codec_impl_legacy.cc delete mode 100644 source/common/http/http2/codec_impl_legacy.h diff --git a/bazel/BUILD b/bazel/BUILD index 55dca812b2ed..fef695905916 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -284,11 +284,6 @@ config_setting( values = {"define": "path_normalization_by_default=true"}, ) -config_setting( - name = "enable_legacy_codecs_in_integration_tests", - values = {"define": "use_new_codecs_in_integration_tests=false"}, -) - cc_proto_library( name = "grpc_health_proto", deps = ["@com_github_grpc_grpc//src/proto/grpc/health/v1:_health_proto_only"], diff --git a/bazel/envoy_build_system.bzl b/bazel/envoy_build_system.bzl index e8dd5dd0bc25..e33386ed5b98 100644 --- a/bazel/envoy_build_system.bzl +++ b/bazel/envoy_build_system.bzl @@ -18,7 +18,6 @@ load( _envoy_select_boringssl = "envoy_select_boringssl", _envoy_select_google_grpc = "envoy_select_google_grpc", _envoy_select_hot_restart = "envoy_select_hot_restart", - _envoy_select_new_codecs_in_integration_tests = "envoy_select_new_codecs_in_integration_tests", _envoy_select_wasm = "envoy_select_wasm", _envoy_select_wasm_v8 = "envoy_select_wasm_v8", _envoy_select_wasm_wasmtime = "envoy_select_wasm_wasmtime", @@ -195,7 +194,6 @@ envoy_select_wasm = _envoy_select_wasm envoy_select_wasm_wavm = _envoy_select_wasm_wavm envoy_select_wasm_wasmtime = _envoy_select_wasm_wasmtime envoy_select_wasm_v8 = _envoy_select_wasm_v8 -envoy_select_new_codecs_in_integration_tests = _envoy_select_new_codecs_in_integration_tests # Binary wrappers (from envoy_binary.bzl) envoy_cc_binary = _envoy_cc_binary diff --git a/bazel/envoy_select.bzl b/bazel/envoy_select.bzl index b0789ae51291..a0f2097ad6cc 100644 --- a/bazel/envoy_select.bzl +++ b/bazel/envoy_select.bzl @@ -58,10 +58,3 @@ def envoy_select_wasm_wasmtime(xs): "@envoy//bazel:wasm_wasmtime": xs, "//conditions:default": [], }) - -# Select the given values by default and remove if use new codecs are disabled for current build. -def envoy_select_new_codecs_in_integration_tests(xs, repository = ""): - return select({ - repository + "//bazel:enable_legacy_codecs_in_integration_tests": [], - "//conditions:default": xs, - }) diff --git a/ci/do_ci.sh b/ci/do_ci.sh index f50a538c1958..b1bf68933334 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -282,7 +282,6 @@ elif [[ "$CI_TARGET" == "bazel.compile_time_options" ]]; then "--define" "quiche=enabled" "--define" "path_normalization_by_default=true" "--define" "deprecated_features=disabled" - "--define" "use_new_codecs_in_integration_tests=false" "--define" "tcmalloc=gperftools" "--define" "zlib=ng" "--@envoy//source/extensions/filters/http/kill_request:enabled" diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 95034b7b1c13..bff52edb3330 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -56,6 +56,7 @@ Removed Config or Runtime * ext_authz: removed auto ignore case in HTTP-based `ext_authz` header matching and the runtime guard `envoy.reloadable_features.ext_authz_http_service_enable_case_sensitive_string_matcher`. To ignore case, set the :ref:`ignore_case ` field to true. * http: flip default HTTP/1 and HTTP/2 server codec implementations to new codecs that remove the use of exceptions for control flow. To revert to old codec behavior, set the runtime feature `envoy.reloadable_features.new_codec_behavior` to false. * http: removed `envoy.reloadable_features.http1_flood_protection` and legacy code path for turning flood protection off. +* http: removed `envoy.reloadable_features.new_codec_behavior` and legacy codecs. New Features ------------ diff --git a/source/common/http/BUILD b/source/common/http/BUILD index 97963015c4cf..e19d26c058f4 100644 --- a/source/common/http/BUILD +++ b/source/common/http/BUILD @@ -58,21 +58,16 @@ envoy_cc_library( "//include/envoy/http:codec_interface", "//include/envoy/network:connection_interface", "//include/envoy/network:filter_interface", - "//include/envoy/runtime:runtime_interface", "//source/common/common:assert_lib", "//source/common/common:enum_to_int", "//source/common/common:linked_object", "//source/common/common:minimal_logger_lib", "//source/common/config:utility_lib", - "//source/common/http/http1:codec_legacy_lib", "//source/common/http/http1:codec_lib", - "//source/common/http/http2:codec_legacy_lib", "//source/common/http/http2:codec_lib", "//source/common/http/http3:quic_codec_factory_lib", "//source/common/http/http3:well_known_names", "//source/common/network:filter_lib", - "//source/common/runtime:runtime_features_lib", - "//source/common/runtime:runtime_lib", ], ) @@ -248,9 +243,7 @@ envoy_cc_library( "//source/common/common:scope_tracker", "//source/common/common:utility_lib", "//source/common/config:utility_lib", - "//source/common/http/http1:codec_legacy_lib", "//source/common/http/http1:codec_lib", - "//source/common/http/http2:codec_legacy_lib", "//source/common/http/http2:codec_lib", "//source/common/http/http3:quic_codec_factory_lib", "//source/common/http/http3:well_known_names", diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index 761cf879a5f0..47a7fd33f8f1 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -9,15 +9,11 @@ #include "common/config/utility.h" #include "common/http/exception.h" #include "common/http/http1/codec_impl.h" -#include "common/http/http1/codec_impl_legacy.h" #include "common/http/http2/codec_impl.h" -#include "common/http/http2/codec_impl_legacy.h" #include "common/http/http3/quic_codec_factory.h" #include "common/http/http3/well_known_names.h" #include "common/http/status.h" #include "common/http/utility.h" -#include "common/runtime/runtime_features.h" -#include "common/runtime/runtime_impl.h" namespace Envoy { namespace Http { @@ -162,15 +158,9 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne switch (type) { case Type::HTTP1: { - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - codec_ = std::make_unique( - *connection_, host->cluster().http1CodecStats(), *this, host->cluster().http1Settings(), - host->cluster().maxResponseHeadersCount()); - } else { - codec_ = std::make_unique( - *connection_, host->cluster().http1CodecStats(), *this, host->cluster().http1Settings(), - host->cluster().maxResponseHeadersCount()); - } + codec_ = std::make_unique( + *connection_, host->cluster().http1CodecStats(), *this, host->cluster().http1Settings(), + host->cluster().maxResponseHeadersCount()); break; } case Type::HTTP2: { diff --git a/source/common/http/conn_manager_utility.cc b/source/common/http/conn_manager_utility.cc index 86c7861bb420..6042af59adcc 100644 --- a/source/common/http/conn_manager_utility.cc +++ b/source/common/http/conn_manager_utility.cc @@ -11,9 +11,7 @@ #include "common/http/header_utility.h" #include "common/http/headers.h" #include "common/http/http1/codec_impl.h" -#include "common/http/http1/codec_impl_legacy.h" #include "common/http/http2/codec_impl.h" -#include "common/http/http2/codec_impl_legacy.h" #include "common/http/path_utility.h" #include "common/http/utility.h" #include "common/network/utility.h" @@ -53,26 +51,14 @@ ServerConnectionPtr ConnectionManagerUtility::autoCreateCodec( headers_with_underscores_action) { if (determineNextProtocol(connection, data) == Utility::AlpnNames::get().Http2) { Http2::CodecStats& stats = Http2::CodecStats::atomicGet(http2_codec_stats, scope); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - return std::make_unique( - connection, callbacks, stats, random, http2_options, max_request_headers_kb, - max_request_headers_count, headers_with_underscores_action); - } else { - return std::make_unique( - connection, callbacks, stats, random, http2_options, max_request_headers_kb, - max_request_headers_count, headers_with_underscores_action); - } + return std::make_unique( + connection, callbacks, stats, random, http2_options, max_request_headers_kb, + max_request_headers_count, headers_with_underscores_action); } else { Http1::CodecStats& stats = Http1::CodecStats::atomicGet(http1_codec_stats, scope); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - return std::make_unique( - connection, stats, callbacks, http1_settings, max_request_headers_kb, - max_request_headers_count, headers_with_underscores_action); - } else { - return std::make_unique( - connection, stats, callbacks, http1_settings, max_request_headers_kb, - max_request_headers_count, headers_with_underscores_action); - } + return std::make_unique( + connection, stats, callbacks, http1_settings, max_request_headers_kb, + max_request_headers_count, headers_with_underscores_action); } } diff --git a/source/common/http/http1/BUILD b/source/common/http/http1/BUILD index 325fa94883a8..8c864eaca715 100644 --- a/source/common/http/http1/BUILD +++ b/source/common/http/http1/BUILD @@ -24,45 +24,36 @@ envoy_cc_library( ], ) -CODEC_LIB_DEPS = [ - ":codec_stats_lib", - ":header_formatter_lib", - "//include/envoy/buffer:buffer_interface", - "//include/envoy/http:codec_interface", - "//include/envoy/http:header_map_interface", - "//include/envoy/network:connection_interface", - "//source/common/buffer:buffer_lib", - "//source/common/buffer:watermark_buffer_lib", - "//source/common/common:assert_lib", - "//source/common/common:statusor_lib", - "//source/common/common:utility_lib", - "//source/common/grpc:common_lib", - "//source/common/http:codec_helper_lib", - "//source/common/http:codes_lib", - "//source/common/http:exception_lib", - "//source/common/http:header_map_lib", - "//source/common/http:header_utility_lib", - "//source/common/http:headers_lib", - "//source/common/http:status_lib", - "//source/common/http:utility_lib", - "//source/common/runtime:runtime_features_lib", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", -] - envoy_cc_library( name = "codec_lib", srcs = ["codec_impl.cc"], hdrs = ["codec_impl.h"], external_deps = ["http_parser"], - deps = CODEC_LIB_DEPS + ["//source/common/common:cleanup_lib"], -) - -envoy_cc_library( - name = "codec_legacy_lib", - srcs = ["codec_impl_legacy.cc"], - hdrs = ["codec_impl_legacy.h"], - external_deps = ["http_parser"], - deps = CODEC_LIB_DEPS, + deps = [ + ":codec_stats_lib", + ":header_formatter_lib", + "//include/envoy/buffer:buffer_interface", + "//include/envoy/http:codec_interface", + "//include/envoy/http:header_map_interface", + "//include/envoy/network:connection_interface", + "//source/common/buffer:buffer_lib", + "//source/common/buffer:watermark_buffer_lib", + "//source/common/common:assert_lib", + "//source/common/common:cleanup_lib", + "//source/common/common:statusor_lib", + "//source/common/common:utility_lib", + "//source/common/grpc:common_lib", + "//source/common/http:codec_helper_lib", + "//source/common/http:codes_lib", + "//source/common/http:exception_lib", + "//source/common/http:header_map_lib", + "//source/common/http:header_utility_lib", + "//source/common/http:headers_lib", + "//source/common/http:status_lib", + "//source/common/http:utility_lib", + "//source/common/runtime:runtime_features_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + ], ) envoy_cc_library( diff --git a/source/common/http/http1/codec_impl_legacy.cc b/source/common/http/http1/codec_impl_legacy.cc deleted file mode 100644 index 3616cd7ed14b..000000000000 --- a/source/common/http/http1/codec_impl_legacy.cc +++ /dev/null @@ -1,1292 +0,0 @@ -#include "common/http/http1/codec_impl_legacy.h" - -#include -#include -#include - -#include "envoy/buffer/buffer.h" -#include "envoy/http/codec.h" -#include "envoy/http/header_map.h" -#include "envoy/network/connection.h" - -#include "common/common/enum_to_int.h" -#include "common/common/utility.h" -#include "common/grpc/common.h" -#include "common/http/exception.h" -#include "common/http/header_utility.h" -#include "common/http/headers.h" -#include "common/http/http1/header_formatter.h" -#include "common/http/utility.h" -#include "common/runtime/runtime_features.h" - -#include "absl/container/fixed_array.h" -#include "absl/strings/ascii.h" - -namespace Envoy { -namespace Http { -namespace Legacy { -namespace Http1 { -namespace { - -struct Http1ResponseCodeDetailValues { - const absl::string_view TooManyHeaders = "http1.too_many_headers"; - const absl::string_view HeadersTooLarge = "http1.headers_too_large"; - const absl::string_view HttpCodecError = "http1.codec_error"; - const absl::string_view InvalidCharacters = "http1.invalid_characters"; - const absl::string_view ConnectionHeaderSanitization = "http1.connection_header_rejected"; - const absl::string_view InvalidUrl = "http1.invalid_url"; - const absl::string_view InvalidTransferEncoding = "http1.invalid_transfer_encoding"; - const absl::string_view BodyDisallowed = "http1.body_disallowed"; - const absl::string_view TransferEncodingNotAllowed = "http1.transfer_encoding_not_allowed"; - const absl::string_view ContentLengthNotAllowed = "http1.content_length_not_allowed"; - const absl::string_view InvalidUnderscore = "http1.unexpected_underscore"; - const absl::string_view ChunkedContentLength = "http1.content_length_and_chunked_not_allowed"; -}; - -struct Http1HeaderTypesValues { - const absl::string_view Headers = "headers"; - const absl::string_view Trailers = "trailers"; -}; - -using Http1ResponseCodeDetails = ConstSingleton; -using Http1HeaderTypes = ConstSingleton; -using Http::Http1::CodecStats; -using Http::Http1::HeaderKeyFormatter; -using Http::Http1::HeaderKeyFormatterPtr; -using Http::Http1::ProperCaseHeaderKeyFormatter; - -const StringUtil::CaseUnorderedSet& caseUnorderdSetContainingUpgradeAndHttp2Settings() { - CONSTRUCT_ON_FIRST_USE(StringUtil::CaseUnorderedSet, - Http::Headers::get().ConnectionValues.Upgrade, - Http::Headers::get().ConnectionValues.Http2Settings); -} - -HeaderKeyFormatterPtr formatter(const Http::Http1Settings& settings) { - if (settings.header_key_format_ == Http1Settings::HeaderKeyFormat::ProperCase) { - return std::make_unique(); - } - - return nullptr; -} - -} // namespace - -const std::string StreamEncoderImpl::CRLF = "\r\n"; -// Last chunk as defined here https://tools.ietf.org/html/rfc7230#section-4.1 -const std::string StreamEncoderImpl::LAST_CHUNK = "0\r\n"; - -StreamEncoderImpl::StreamEncoderImpl(ConnectionImpl& connection, - HeaderKeyFormatter* header_key_formatter) - : connection_(connection), disable_chunk_encoding_(false), chunk_encoding_(true), - connect_request_(false), is_response_to_head_request_(false), - is_response_to_connect_request_(false), header_key_formatter_(header_key_formatter) { - if (connection_.connection().aboveHighWatermark()) { - runHighWatermarkCallbacks(); - } -} - -void StreamEncoderImpl::encodeHeader(const char* key, uint32_t key_size, const char* value, - uint32_t value_size) { - - ASSERT(key_size > 0); - - connection_.copyToBuffer(key, key_size); - connection_.addCharToBuffer(':'); - connection_.addCharToBuffer(' '); - connection_.copyToBuffer(value, value_size); - connection_.addToBuffer(CRLF); -} -void StreamEncoderImpl::encodeHeader(absl::string_view key, absl::string_view value) { - this->encodeHeader(key.data(), key.size(), value.data(), value.size()); -} - -void StreamEncoderImpl::encodeFormattedHeader(absl::string_view key, absl::string_view value) { - if (header_key_formatter_ != nullptr) { - encodeHeader(header_key_formatter_->format(key), value); - } else { - encodeHeader(key, value); - } -} - -void ResponseEncoderImpl::encode100ContinueHeaders(const ResponseHeaderMap& headers) { - ASSERT(headers.Status()->value() == "100"); - encodeHeaders(headers, false); -} - -void StreamEncoderImpl::encodeHeadersBase(const RequestOrResponseHeaderMap& headers, - absl::optional status, bool end_stream) { - bool saw_content_length = false; - headers.iterate([this](const HeaderEntry& header) -> HeaderMap::Iterate { - absl::string_view key_to_use = header.key().getStringView(); - uint32_t key_size_to_use = header.key().size(); - // Translate :authority -> host so that upper layers do not need to deal with this. - if (key_size_to_use > 1 && key_to_use[0] == ':' && key_to_use[1] == 'a') { - key_to_use = absl::string_view(Headers::get().HostLegacy.get()); - key_size_to_use = Headers::get().HostLegacy.get().size(); - } - - // Skip all headers starting with ':' that make it here. - if (key_to_use[0] == ':') { - return HeaderMap::Iterate::Continue; - } - - encodeFormattedHeader(key_to_use, header.value().getStringView()); - - return HeaderMap::Iterate::Continue; - }); - - if (headers.ContentLength()) { - saw_content_length = true; - } - - ASSERT(!headers.TransferEncoding()); - - // Assume we are chunk encoding unless we are passed a content length or this is a header only - // response. Upper layers generally should strip transfer-encoding since it only applies to - // HTTP/1.1. The codec will infer it based on the type of response. - // for streaming (e.g. SSE stream sent to hystrix dashboard), we do not want - // chunk transfer encoding but we don't have a content-length so disable_chunk_encoding_ is - // consulted before enabling chunk encoding. - // - // Note that for HEAD requests Envoy does best-effort guessing when there is no - // content-length. If a client makes a HEAD request for an upstream resource - // with no bytes but the upstream response doesn't include "Content-length: 0", - // Envoy will incorrectly assume a subsequent response to GET will be chunk encoded. - if (saw_content_length || disable_chunk_encoding_) { - chunk_encoding_ = false; - } else { - if (status && *status == 100) { - // Make sure we don't serialize chunk information with 100-Continue headers. - chunk_encoding_ = false; - } else if (end_stream && !is_response_to_head_request_) { - // If this is a headers-only stream, append an explicit "Content-Length: 0" unless it's a - // response to a HEAD request. - // For 204s and 1xx where content length is disallowed, don't append the content length but - // also don't chunk encode. - if (!status || (*status >= 200 && *status != 204)) { - encodeFormattedHeader(Headers::get().ContentLength.get(), "0"); - } - chunk_encoding_ = false; - } else if (connection_.protocol() == Protocol::Http10) { - chunk_encoding_ = false; - } else if (status && (*status < 200 || *status == 204) && - connection_.strict1xxAnd204Headers()) { - // TODO(zuercher): when the "envoy.reloadable_features.strict_1xx_and_204_response_headers" - // feature flag is removed, this block can be coalesced with the 100 Continue logic above. - - // For 1xx and 204 responses, do not send the chunked encoding header or enable chunked - // encoding: https://tools.ietf.org/html/rfc7230#section-3.3.1 - chunk_encoding_ = false; - } else { - // For responses to connect requests, do not send the chunked encoding header: - // https://tools.ietf.org/html/rfc7231#section-4.3.6. - if (!is_response_to_connect_request_) { - encodeFormattedHeader(Headers::get().TransferEncoding.get(), - Headers::get().TransferEncodingValues.Chunked); - } - // We do not apply chunk encoding for HTTP upgrades, including CONNECT style upgrades. - // If there is a body in a response on the upgrade path, the chunks will be - // passed through via maybeDirectDispatch so we need to avoid appending - // extra chunk boundaries. - // - // When sending a response to a HEAD request Envoy may send an informational - // "Transfer-Encoding: chunked" header, but should not send a chunk encoded body. - chunk_encoding_ = !Utility::isUpgrade(headers) && !is_response_to_head_request_ && - !is_response_to_connect_request_; - } - } - - connection_.addToBuffer(CRLF); - - if (end_stream) { - endEncode(); - } else { - connection_.flushOutput(); - } -} - -void StreamEncoderImpl::encodeData(Buffer::Instance& data, bool end_stream) { - // end_stream may be indicated with a zero length data buffer. If that is the case, so not - // actually write the zero length buffer out. - if (data.length() > 0) { - if (chunk_encoding_) { - connection_.buffer().add(absl::StrCat(absl::Hex(data.length()), CRLF)); - } - - connection_.buffer().move(data); - - if (chunk_encoding_) { - connection_.buffer().add(CRLF); - } - } - - if (end_stream) { - endEncode(); - } else { - connection_.flushOutput(); - } -} - -void StreamEncoderImpl::encodeTrailersBase(const HeaderMap& trailers) { - if (!connection_.enableTrailers()) { - return endEncode(); - } - // Trailers only matter if it is a chunk transfer encoding - // https://tools.ietf.org/html/rfc7230#section-4.4 - if (chunk_encoding_) { - // Finalize the body - connection_.buffer().add(LAST_CHUNK); - - trailers.iterate([this](const HeaderEntry& header) -> HeaderMap::Iterate { - encodeFormattedHeader(header.key().getStringView(), header.value().getStringView()); - return HeaderMap::Iterate::Continue; - }); - - connection_.flushOutput(); - connection_.buffer().add(CRLF); - } - - connection_.flushOutput(); - connection_.onEncodeComplete(); -} - -void StreamEncoderImpl::encodeMetadata(const MetadataMapVector&) { - connection_.stats().metadata_not_supported_error_.inc(); -} - -void StreamEncoderImpl::endEncode() { - if (chunk_encoding_) { - connection_.buffer().add(LAST_CHUNK); - connection_.buffer().add(CRLF); - } - - connection_.flushOutput(true); - connection_.onEncodeComplete(); - // With CONNECT half-closing the connection is used to signal end stream. - if (connect_request_) { - connection_.connection().close(Network::ConnectionCloseType::FlushWriteAndDelay); - } -} - -void ServerConnectionImpl::maybeAddSentinelBufferFragment(Buffer::Instance& output_buffer) { - // It's messy and complicated to try to tag the final write of an HTTP response for response - // tracking for flood protection. Instead, write an empty buffer fragment after the response, - // to allow for tracking. - // When the response is written out, the fragment will be deleted and the counter will be updated - // by ServerConnectionImpl::releaseOutboundResponse() - auto fragment = - Buffer::OwnedBufferFragmentImpl::create(absl::string_view("", 0), response_buffer_releasor_); - output_buffer.addBufferFragment(*fragment.release()); - ASSERT(outbound_responses_ < max_outbound_responses_); - outbound_responses_++; -} - -void ServerConnectionImpl::doFloodProtectionChecks() const { - // Before processing another request, make sure that we are below the response flood protection - // threshold. - if (outbound_responses_ >= max_outbound_responses_) { - ENVOY_CONN_LOG(trace, "error accepting request: too many pending responses queued", - connection_); - stats_.response_flood_.inc(); - throw FrameFloodException("Too many responses queued."); - } -} - -void ConnectionImpl::flushOutput(bool end_encode) { - if (end_encode) { - // If this is an HTTP response in ServerConnectionImpl, track outbound responses for flood - // protection - maybeAddSentinelBufferFragment(*output_buffer_); - } - connection().write(*output_buffer_, false); - ASSERT(0UL == output_buffer_->length()); -} - -void ConnectionImpl::addToBuffer(absl::string_view data) { output_buffer_->add(data); } - -void ConnectionImpl::addCharToBuffer(char c) { output_buffer_->add(&c, 1); } - -void ConnectionImpl::addIntToBuffer(uint64_t i) { output_buffer_->add(absl::StrCat(i)); } - -void ConnectionImpl::copyToBuffer(const char* data, uint64_t length) { - output_buffer_->add(data, length); -} - -void StreamEncoderImpl::resetStream(StreamResetReason reason) { - connection_.onResetStreamBase(reason); -} - -void StreamEncoderImpl::readDisable(bool disable) { - if (disable) { - ++read_disable_calls_; - } else { - ASSERT(read_disable_calls_ != 0); - if (read_disable_calls_ != 0) { - --read_disable_calls_; - } - } - connection_.readDisable(disable); -} - -uint32_t StreamEncoderImpl::bufferLimit() { return connection_.bufferLimit(); } - -const Network::Address::InstanceConstSharedPtr& StreamEncoderImpl::connectionLocalAddress() { - return connection_.connection().localAddress(); -} - -static const char RESPONSE_PREFIX[] = "HTTP/1.1 "; -static const char HTTP_10_RESPONSE_PREFIX[] = "HTTP/1.0 "; - -void ResponseEncoderImpl::encodeHeaders(const ResponseHeaderMap& headers, bool end_stream) { - started_response_ = true; - - // The contract is that client codecs must ensure that :status is present. - ASSERT(headers.Status() != nullptr); - uint64_t numeric_status = Utility::getResponseStatus(headers); - - if (connection_.protocol() == Protocol::Http10 && connection_.supportsHttp10()) { - connection_.copyToBuffer(HTTP_10_RESPONSE_PREFIX, sizeof(HTTP_10_RESPONSE_PREFIX) - 1); - } else { - connection_.copyToBuffer(RESPONSE_PREFIX, sizeof(RESPONSE_PREFIX) - 1); - } - connection_.addIntToBuffer(numeric_status); - connection_.addCharToBuffer(' '); - - const char* status_string = CodeUtility::toString(static_cast(numeric_status)); - uint32_t status_string_len = strlen(status_string); - connection_.copyToBuffer(status_string, status_string_len); - - connection_.addCharToBuffer('\r'); - connection_.addCharToBuffer('\n'); - - if (numeric_status >= 300) { - // Don't do special CONNECT logic if the CONNECT was rejected. - is_response_to_connect_request_ = false; - } - - encodeHeadersBase(headers, absl::make_optional(numeric_status), end_stream); -} - -static const char REQUEST_POSTFIX[] = " HTTP/1.1\r\n"; - -Status RequestEncoderImpl::encodeHeaders(const RequestHeaderMap& headers, bool end_stream) { - const HeaderEntry* method = headers.Method(); - const HeaderEntry* path = headers.Path(); - const HeaderEntry* host = headers.Host(); - bool is_connect = HeaderUtility::isConnect(headers); - - if (!method || (!path && !is_connect)) { - // TODO(#10878): This exception does not occur during dispatch and would not be triggered under - // normal circumstances since inputs would fail parsing at ingress. Replace with proper error - // handling when exceptions are removed. Include missing host header for CONNECT. - throw CodecClientException(":method and :path must be specified"); - } - if (method->value() == Headers::get().MethodValues.Head) { - head_request_ = true; - } else if (method->value() == Headers::get().MethodValues.Connect) { - disableChunkEncoding(); - connection_.connection().enableHalfClose(true); - connect_request_ = true; - } - if (Utility::isUpgrade(headers)) { - upgrade_request_ = true; - } - - connection_.copyToBuffer(method->value().getStringView().data(), method->value().size()); - connection_.addCharToBuffer(' '); - if (is_connect) { - connection_.copyToBuffer(host->value().getStringView().data(), host->value().size()); - } else { - connection_.copyToBuffer(path->value().getStringView().data(), path->value().size()); - } - connection_.copyToBuffer(REQUEST_POSTFIX, sizeof(REQUEST_POSTFIX) - 1); - - encodeHeadersBase(headers, absl::nullopt, end_stream); - return okStatus(); -} - -http_parser_settings ConnectionImpl::settings_{ - [](http_parser* parser) -> int { - static_cast(parser->data)->onMessageBeginBase(); - return 0; - }, - [](http_parser* parser, const char* at, size_t length) -> int { - static_cast(parser->data)->onUrl(at, length); - return 0; - }, - nullptr, // on_status - [](http_parser* parser, const char* at, size_t length) -> int { - static_cast(parser->data)->onHeaderField(at, length); - return 0; - }, - [](http_parser* parser, const char* at, size_t length) -> int { - static_cast(parser->data)->onHeaderValue(at, length); - return 0; - }, - [](http_parser* parser) -> int { - return static_cast(parser->data)->onHeadersCompleteBase(); - }, - [](http_parser* parser, const char* at, size_t length) -> int { - static_cast(parser->data)->bufferBody(at, length); - return 0; - }, - [](http_parser* parser) -> int { - static_cast(parser->data)->onMessageCompleteBase(); - return 0; - }, - [](http_parser* parser) -> int { - // A 0-byte chunk header is used to signal the end of the chunked body. - // When this function is called, http-parser holds the size of the chunk in - // parser->content_length. See - // https://github.com/nodejs/http-parser/blob/v2.9.3/http_parser.h#L336 - const bool is_final_chunk = (parser->content_length == 0); - static_cast(parser->data)->onChunkHeader(is_final_chunk); - return 0; - }, - nullptr // on_chunk_complete -}; - -ConnectionImpl::ConnectionImpl(Network::Connection& connection, CodecStats& stats, - const Http1Settings& settings, http_parser_type type, - uint32_t max_headers_kb, const uint32_t max_headers_count, - HeaderKeyFormatterPtr&& header_key_formatter) - : connection_(connection), stats_(stats), codec_settings_(settings), - header_key_formatter_(std::move(header_key_formatter)), processing_trailers_(false), - handling_upgrade_(false), reset_stream_called_(false), deferred_end_stream_headers_(false), - strict_1xx_and_204_headers_(Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.strict_1xx_and_204_response_headers")), - output_buffer_(connection.dispatcher().getWatermarkFactory().create( - [&]() -> void { this->onBelowLowWatermark(); }, - [&]() -> void { this->onAboveHighWatermark(); }, - []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })), - max_headers_kb_(max_headers_kb), max_headers_count_(max_headers_count) { - output_buffer_->setWatermarks(connection.bufferLimit()); - http_parser_init(&parser_, type); - parser_.allow_chunked_length = 1; - parser_.data = this; -} - -void ConnectionImpl::completeLastHeader() { - ENVOY_CONN_LOG(trace, "completed header: key={} value={}", connection_, - current_header_field_.getStringView(), current_header_value_.getStringView()); - - checkHeaderNameForUnderscores(); - auto& headers_or_trailers = headersOrTrailers(); - if (!current_header_field_.empty()) { - current_header_field_.inlineTransform([](char c) { return absl::ascii_tolower(c); }); - // Strip trailing whitespace of the current header value if any. Leading whitespace was trimmed - // in ConnectionImpl::onHeaderValue. http_parser does not strip leading or trailing whitespace - // as the spec requires: https://tools.ietf.org/html/rfc7230#section-3.2.4 - current_header_value_.rtrim(); - headers_or_trailers.addViaMove(std::move(current_header_field_), - std::move(current_header_value_)); - } - - // Check if the number of headers exceeds the limit. - if (headers_or_trailers.size() > max_headers_count_) { - error_code_ = Http::Code::RequestHeaderFieldsTooLarge; - sendProtocolError(Http1ResponseCodeDetails::get().TooManyHeaders); - const absl::string_view header_type = - processing_trailers_ ? Http1HeaderTypes::get().Trailers : Http1HeaderTypes::get().Headers; - throw CodecProtocolException(absl::StrCat(header_type, " size exceeds limit")); - } - - header_parsing_state_ = HeaderParsingState::Field; - ASSERT(current_header_field_.empty()); - ASSERT(current_header_value_.empty()); -} - -uint32_t ConnectionImpl::getHeadersSize() { - return current_header_field_.size() + current_header_value_.size() + - headersOrTrailers().byteSize(); -} - -void ConnectionImpl::checkMaxHeadersSize() { - const uint32_t total = getHeadersSize(); - if (total > (max_headers_kb_ * 1024)) { - const absl::string_view header_type = - processing_trailers_ ? Http1HeaderTypes::get().Trailers : Http1HeaderTypes::get().Headers; - error_code_ = Http::Code::RequestHeaderFieldsTooLarge; - sendProtocolError(Http1ResponseCodeDetails::get().HeadersTooLarge); - throw CodecProtocolException(absl::StrCat(header_type, " size exceeds limit")); - } -} - -bool ConnectionImpl::maybeDirectDispatch(Buffer::Instance& data) { - if (!handling_upgrade_) { - // Only direct dispatch for Upgrade requests. - return false; - } - - ENVOY_CONN_LOG(trace, "direct-dispatched {} bytes", connection_, data.length()); - onBody(data); - data.drain(data.length()); - return true; -} - -Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) { - // TODO(#10878): Remove this wrapper when exception removal is complete. innerDispatch may either - // throw an exception or return an error status. The utility wrapper catches exceptions and - // converts them to error statuses. - return Utility::exceptionToStatus( - [&](Buffer::Instance& data) -> Http::Status { return innerDispatch(data); }, data); -} - -Http::Status ClientConnectionImpl::dispatch(Buffer::Instance& data) { - Http::Status status = ConnectionImpl::dispatch(data); - if (status.ok() && data.length() > 0) { - // The HTTP/1.1 codec pauses dispatch after a single response is complete. Extraneous data - // after a response is complete indicates an error. - return codecProtocolError("http/1.1 protocol error: extraneous data after response complete"); - } - return status; -} - -Http::Status ConnectionImpl::innerDispatch(Buffer::Instance& data) { - ENVOY_CONN_LOG(trace, "parsing {} bytes", connection_, data.length()); - ASSERT(buffered_body_.length() == 0); - - if (maybeDirectDispatch(data)) { - return Http::okStatus(); - } - - // Always unpause before dispatch. - http_parser_pause(&parser_, 0); - - ssize_t total_parsed = 0; - if (data.length() > 0) { - current_dispatching_buffer_ = &data; - while (data.length() > 0) { - auto slice = data.frontSlice(); - dispatching_slice_already_drained_ = false; - const size_t parsed = dispatchSlice(static_cast(slice.mem_), slice.len_); - if (!dispatching_slice_already_drained_) { - ASSERT(parsed <= slice.len_); - data.drain(parsed); - } - - total_parsed += parsed; - if (HTTP_PARSER_ERRNO(&parser_) != HPE_OK) { - // Parse errors trigger an exception in dispatchSlice so we are guaranteed to be paused at - // this point. - ASSERT(HTTP_PARSER_ERRNO(&parser_) == HPE_PAUSED); - break; - } - } - current_dispatching_buffer_ = nullptr; - dispatchBufferedBody(); - } else { - dispatchSlice(nullptr, 0); - } - ASSERT(buffered_body_.length() == 0); - - ENVOY_CONN_LOG(trace, "parsed {} bytes", connection_, total_parsed); - - // If an upgrade has been handled and there is body data or early upgrade - // payload to send on, send it on. - maybeDirectDispatch(data); - return Http::okStatus(); -} - -size_t ConnectionImpl::dispatchSlice(const char* slice, size_t len) { - ssize_t rc = http_parser_execute(&parser_, &settings_, slice, len); - if (HTTP_PARSER_ERRNO(&parser_) != HPE_OK && HTTP_PARSER_ERRNO(&parser_) != HPE_PAUSED) { - sendProtocolError(Http1ResponseCodeDetails::get().HttpCodecError); - throw CodecProtocolException("http/1.1 protocol error: " + - std::string(http_errno_name(HTTP_PARSER_ERRNO(&parser_)))); - } - - return rc; -} - -void ConnectionImpl::onHeaderField(const char* data, size_t length) { - // We previously already finished up the headers, these headers are - // now trailers. - if (header_parsing_state_ == HeaderParsingState::Done) { - if (!enableTrailers()) { - // Ignore trailers. - return; - } - processing_trailers_ = true; - header_parsing_state_ = HeaderParsingState::Field; - allocTrailers(); - } - if (header_parsing_state_ == HeaderParsingState::Value) { - completeLastHeader(); - } - - current_header_field_.append(data, length); - - checkMaxHeadersSize(); -} - -void ConnectionImpl::onHeaderValue(const char* data, size_t length) { - if (header_parsing_state_ == HeaderParsingState::Done && !enableTrailers()) { - // Ignore trailers. - return; - } - - absl::string_view header_value{data, length}; - if (!Http::HeaderUtility::headerValueIsValid(header_value)) { - ENVOY_CONN_LOG(debug, "invalid header value: {}", connection_, header_value); - error_code_ = Http::Code::BadRequest; - sendProtocolError(Http1ResponseCodeDetails::get().InvalidCharacters); - throw CodecProtocolException("http/1.1 protocol error: header value contains invalid chars"); - } - - header_parsing_state_ = HeaderParsingState::Value; - if (current_header_value_.empty()) { - // Strip leading whitespace if the current header value input contains the first bytes of the - // encoded header value. Trailing whitespace is stripped once the full header value is known in - // ConnectionImpl::completeLastHeader. http_parser does not strip leading or trailing whitespace - // as the spec requires: https://tools.ietf.org/html/rfc7230#section-3.2.4 . - header_value = StringUtil::ltrim(header_value); - } - current_header_value_.append(header_value.data(), header_value.length()); - - checkMaxHeadersSize(); -} - -int ConnectionImpl::onHeadersCompleteBase() { - ASSERT(!processing_trailers_); - ENVOY_CONN_LOG(trace, "onHeadersCompleteBase", connection_); - completeLastHeader(); - - if (!(parser_.http_major == 1 && parser_.http_minor == 1)) { - // This is not necessarily true, but it's good enough since higher layers only care if this is - // HTTP/1.1 or not. - protocol_ = Protocol::Http10; - } - RequestOrResponseHeaderMap& request_or_response_headers = requestOrResponseHeaders(); - if (Utility::isUpgrade(request_or_response_headers) && upgradeAllowed()) { - // Ignore h2c upgrade requests until we support them. - // See https://github.com/envoyproxy/envoy/issues/7161 for details. - if (absl::EqualsIgnoreCase(request_or_response_headers.getUpgradeValue(), - Http::Headers::get().UpgradeValues.H2c)) { - ENVOY_CONN_LOG(trace, "removing unsupported h2c upgrade headers.", connection_); - request_or_response_headers.removeUpgrade(); - if (request_or_response_headers.Connection()) { - const auto& tokens_to_remove = caseUnorderdSetContainingUpgradeAndHttp2Settings(); - std::string new_value = StringUtil::removeTokens( - request_or_response_headers.getConnectionValue(), ",", tokens_to_remove, ","); - if (new_value.empty()) { - request_or_response_headers.removeConnection(); - } else { - request_or_response_headers.setConnection(new_value); - } - } - request_or_response_headers.remove(Headers::get().Http2Settings); - } else { - ENVOY_CONN_LOG(trace, "codec entering upgrade mode.", connection_); - handling_upgrade_ = true; - } - } - if (parser_.method == HTTP_CONNECT) { - if (request_or_response_headers.ContentLength()) { - if (request_or_response_headers.getContentLengthValue() == "0") { - request_or_response_headers.removeContentLength(); - } else { - // Per https://tools.ietf.org/html/rfc7231#section-4.3.6 a payload with a - // CONNECT request has no defined semantics, and may be rejected. - error_code_ = Http::Code::BadRequest; - sendProtocolError(Http1ResponseCodeDetails::get().BodyDisallowed); - throw CodecProtocolException("http/1.1 protocol error: unsupported content length"); - } - } - ENVOY_CONN_LOG(trace, "codec entering upgrade mode for CONNECT request.", connection_); - handling_upgrade_ = true; - } - - // https://tools.ietf.org/html/rfc7230#section-3.3.3 - // If a message is received with both a Transfer-Encoding and a - // Content-Length header field, the Transfer-Encoding overrides the - // Content-Length. Such a message might indicate an attempt to - // perform request smuggling (Section 9.5) or response splitting - // (Section 9.4) and ought to be handled as an error. A sender MUST - // remove the received Content-Length field prior to forwarding such - // a message. - - // Reject message with Http::Code::BadRequest if both Transfer-Encoding and Content-Length - // headers are present or if allowed by http1 codec settings and 'Transfer-Encoding' - // is chunked - remove Content-Length and serve request. - if (parser_.uses_transfer_encoding != 0 && request_or_response_headers.ContentLength()) { - if ((parser_.flags & F_CHUNKED) && codec_settings_.allow_chunked_length_) { - request_or_response_headers.removeContentLength(); - } else { - error_code_ = Http::Code::BadRequest; - sendProtocolError(Http1ResponseCodeDetails::get().ChunkedContentLength); - throw CodecProtocolException( - "http/1.1 protocol error: both 'Content-Length' and 'Transfer-Encoding' are set."); - } - } - - // Per https://tools.ietf.org/html/rfc7230#section-3.3.1 Envoy should reject - // transfer-codings it does not understand. - // Per https://tools.ietf.org/html/rfc7231#section-4.3.6 a payload with a - // CONNECT request has no defined semantics, and may be rejected. - if (request_or_response_headers.TransferEncoding()) { - const absl::string_view encoding = request_or_response_headers.getTransferEncodingValue(); - if (!absl::EqualsIgnoreCase(encoding, Headers::get().TransferEncodingValues.Chunked) || - parser_.method == HTTP_CONNECT) { - error_code_ = Http::Code::NotImplemented; - sendProtocolError(Http1ResponseCodeDetails::get().InvalidTransferEncoding); - throw CodecProtocolException("http/1.1 protocol error: unsupported transfer encoding"); - } - } - - int rc = onHeadersComplete(); - header_parsing_state_ = HeaderParsingState::Done; - - // Returning 2 informs http_parser to not expect a body or further data on this connection. - return handling_upgrade_ ? 2 : rc; -} - -void ConnectionImpl::bufferBody(const char* data, size_t length) { - auto slice = current_dispatching_buffer_->frontSlice(); - if (data == slice.mem_ && length == slice.len_) { - buffered_body_.move(*current_dispatching_buffer_, length); - dispatching_slice_already_drained_ = true; - } else { - buffered_body_.add(data, length); - } -} - -void ConnectionImpl::dispatchBufferedBody() { - ASSERT(HTTP_PARSER_ERRNO(&parser_) == HPE_OK || HTTP_PARSER_ERRNO(&parser_) == HPE_PAUSED); - if (buffered_body_.length() > 0) { - onBody(buffered_body_); - buffered_body_.drain(buffered_body_.length()); - } -} - -void ConnectionImpl::onChunkHeader(bool is_final_chunk) { - if (is_final_chunk) { - // Dispatch body before parsing trailers, so body ends up dispatched even if an error is found - // while processing trailers. - dispatchBufferedBody(); - } -} - -void ConnectionImpl::onMessageCompleteBase() { - ENVOY_CONN_LOG(trace, "message complete", connection_); - - dispatchBufferedBody(); - - if (handling_upgrade_) { - // If this is an upgrade request, swallow the onMessageComplete. The - // upgrade payload will be treated as stream body. - ASSERT(!deferred_end_stream_headers_); - ENVOY_CONN_LOG(trace, "Pausing parser due to upgrade.", connection_); - http_parser_pause(&parser_, 1); - return; - } - - // If true, this indicates we were processing trailers and must - // move the last header into current_header_map_ - if (header_parsing_state_ == HeaderParsingState::Value) { - completeLastHeader(); - } - - onMessageComplete(); -} - -void ConnectionImpl::onMessageBeginBase() { - ENVOY_CONN_LOG(trace, "message begin", connection_); - // Make sure that if HTTP/1.0 and HTTP/1.1 requests share a connection Envoy correctly sets - // protocol for each request. Envoy defaults to 1.1 but sets the protocol to 1.0 where applicable - // in onHeadersCompleteBase - protocol_ = Protocol::Http11; - processing_trailers_ = false; - header_parsing_state_ = HeaderParsingState::Field; - allocHeaders(); - onMessageBegin(); -} - -void ConnectionImpl::onResetStreamBase(StreamResetReason reason) { - ASSERT(!reset_stream_called_); - reset_stream_called_ = true; - onResetStream(reason); -} - -ServerConnectionImpl::ServerConnectionImpl( - Network::Connection& connection, CodecStats& stats, ServerConnectionCallbacks& callbacks, - const Http1Settings& settings, uint32_t max_request_headers_kb, - const uint32_t max_request_headers_count, - envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction - headers_with_underscores_action) - : ConnectionImpl(connection, stats, settings, HTTP_REQUEST, max_request_headers_kb, - max_request_headers_count, formatter(settings)), - callbacks_(callbacks), - response_buffer_releasor_([this](const Buffer::OwnedBufferFragmentImpl* fragment) { - releaseOutboundResponse(fragment); - }), - // Pipelining is generally not well supported on the internet and has a series of dangerous - // overflow bugs. As such we are disabling it for now, and removing this temporary override if - // no one objects. If you use this integer to restore prior behavior, contact the - // maintainer team as it will otherwise be removed entirely soon. - max_outbound_responses_( - Runtime::getInteger("envoy.do_not_use_going_away_max_http2_outbound_responses", 2)), - headers_with_underscores_action_(headers_with_underscores_action) {} - -uint32_t ServerConnectionImpl::getHeadersSize() { - // Add in the size of the request URL if processing request headers. - const uint32_t url_size = (!processing_trailers_ && active_request_.has_value()) - ? active_request_.value().request_url_.size() - : 0; - return url_size + ConnectionImpl::getHeadersSize(); -} - -void ServerConnectionImpl::onEncodeComplete() { - if (active_request_.value().remote_complete_) { - // Only do this if remote is complete. If we are replying before the request is complete the - // only logical thing to do is for higher level code to reset() / close the connection so we - // leave the request around so that it can fire reset callbacks. - active_request_.reset(); - } -} - -void ServerConnectionImpl::handlePath(RequestHeaderMap& headers, unsigned int method) { - HeaderString path(Headers::get().Path); - - bool is_connect = (method == HTTP_CONNECT); - - // The url is relative or a wildcard when the method is OPTIONS. Nothing to do here. - auto& active_request = active_request_.value(); - if (!is_connect && !active_request.request_url_.getStringView().empty() && - (active_request.request_url_.getStringView()[0] == '/' || - ((method == HTTP_OPTIONS) && active_request.request_url_.getStringView()[0] == '*'))) { - headers.addViaMove(std::move(path), std::move(active_request.request_url_)); - return; - } - - // If absolute_urls and/or connect are not going be handled, copy the url and return. - // This forces the behavior to be backwards compatible with the old codec behavior. - // CONNECT "urls" are actually host:port so look like absolute URLs to the above checks. - // Absolute URLS in CONNECT requests will be rejected below by the URL class validation. - if (!codec_settings_.allow_absolute_url_ && !is_connect) { - headers.addViaMove(std::move(path), std::move(active_request.request_url_)); - return; - } - - Utility::Url absolute_url; - if (!absolute_url.initialize(active_request.request_url_.getStringView(), is_connect)) { - sendProtocolError(Http1ResponseCodeDetails::get().InvalidUrl); - throw CodecProtocolException("http/1.1 protocol error: invalid url in request line"); - } - // RFC7230#5.7 - // When a proxy receives a request with an absolute-form of - // request-target, the proxy MUST ignore the received Host header field - // (if any) and instead replace it with the host information of the - // request-target. A proxy that forwards such a request MUST generate a - // new Host field-value based on the received request-target rather than - // forward the received Host field-value. - headers.setHost(absolute_url.hostAndPort()); - - if (!absolute_url.pathAndQueryParams().empty()) { - headers.setPath(absolute_url.pathAndQueryParams()); - } - active_request.request_url_.clear(); -} - -int ServerConnectionImpl::onHeadersComplete() { - // Handle the case where response happens prior to request complete. It's up to upper layer code - // to disconnect the connection but we shouldn't fire any more events since it doesn't make - // sense. - if (active_request_.has_value()) { - auto& active_request = active_request_.value(); - auto& headers = absl::get(headers_or_trailers_); - ENVOY_CONN_LOG(trace, "Server: onHeadersComplete size={}", connection_, headers->size()); - const char* method_string = http_method_str(static_cast(parser_.method)); - - if (!handling_upgrade_ && headers->Connection()) { - // If we fail to sanitize the request, return a 400 to the client - if (!Utility::sanitizeConnectionHeader(*headers)) { - absl::string_view header_value = headers->getConnectionValue(); - ENVOY_CONN_LOG(debug, "Invalid nominated headers in Connection: {}", connection_, - header_value); - error_code_ = Http::Code::BadRequest; - sendProtocolError(Http1ResponseCodeDetails::get().ConnectionHeaderSanitization); - throw CodecProtocolException("Invalid nominated headers in Connection."); - } - } - - // Inform the response encoder about any HEAD method, so it can set content - // length and transfer encoding headers correctly. - active_request.response_encoder_.setIsResponseToHeadRequest(parser_.method == HTTP_HEAD); - active_request.response_encoder_.setIsResponseToConnectRequest(parser_.method == HTTP_CONNECT); - - handlePath(*headers, parser_.method); - ASSERT(active_request.request_url_.empty()); - - headers->setMethod(method_string); - - // Make sure the host is valid. - auto details = HeaderUtility::requestHeadersValid(*headers); - if (details.has_value()) { - sendProtocolError(details.value().get()); - throw CodecProtocolException( - "http/1.1 protocol error: request headers failed spec compliance checks"); - } - - // Determine here whether we have a body or not. This uses the new RFC semantics where the - // presence of content-length or chunked transfer-encoding indicates a body vs. a particular - // method. If there is no body, we defer raising decodeHeaders() until the parser is flushed - // with message complete. This allows upper layers to behave like HTTP/2 and prevents a proxy - // scenario where the higher layers stream through and implicitly switch to chunked transfer - // encoding because end stream with zero body length has not yet been indicated. - if (parser_.flags & F_CHUNKED || - (parser_.content_length > 0 && parser_.content_length != ULLONG_MAX) || handling_upgrade_) { - active_request.request_decoder_->decodeHeaders(std::move(headers), false); - - // If the connection has been closed (or is closing) after decoding headers, pause the parser - // so we return control to the caller. - if (connection_.state() != Network::Connection::State::Open) { - http_parser_pause(&parser_, 1); - } - } else { - deferred_end_stream_headers_ = true; - } - } - - return 0; -} - -void ServerConnectionImpl::onMessageBegin() { - if (!resetStreamCalled()) { - ASSERT(!active_request_.has_value()); - active_request_.emplace(*this, header_key_formatter_.get()); - auto& active_request = active_request_.value(); - active_request.request_decoder_ = &callbacks_.newStream(active_request.response_encoder_); - - // Check for pipelined request flood as we prepare to accept a new request. - // Parse errors that happen prior to onMessageBegin result in stream termination, it is not - // possible to overflow output buffers with early parse errors. - doFloodProtectionChecks(); - } -} - -void ServerConnectionImpl::onUrl(const char* data, size_t length) { - if (active_request_.has_value()) { - active_request_.value().request_url_.append(data, length); - - checkMaxHeadersSize(); - } -} - -void ServerConnectionImpl::onBody(Buffer::Instance& data) { - ASSERT(!deferred_end_stream_headers_); - if (active_request_.has_value()) { - ENVOY_CONN_LOG(trace, "body size={}", connection_, data.length()); - active_request_.value().request_decoder_->decodeData(data, false); - } -} - -void ServerConnectionImpl::onMessageComplete() { - ASSERT(!handling_upgrade_); - if (active_request_.has_value()) { - auto& active_request = active_request_.value(); - - if (active_request.request_decoder_) { - active_request.response_encoder_.readDisable(true); - } - active_request.remote_complete_ = true; - if (deferred_end_stream_headers_) { - active_request.request_decoder_->decodeHeaders( - std::move(absl::get(headers_or_trailers_)), true); - deferred_end_stream_headers_ = false; - } else if (processing_trailers_) { - active_request.request_decoder_->decodeTrailers( - std::move(absl::get(headers_or_trailers_))); - } else { - Buffer::OwnedImpl buffer; - active_request.request_decoder_->decodeData(buffer, true); - } - - // Reset to ensure no information from one requests persists to the next. - headers_or_trailers_.emplace(nullptr); - } - - // Always pause the parser so that the calling code can process 1 request at a time and apply - // back pressure. However this means that the calling code needs to detect if there is more data - // in the buffer and dispatch it again. - http_parser_pause(&parser_, 1); -} - -void ServerConnectionImpl::onResetStream(StreamResetReason reason) { - active_request_.value().response_encoder_.runResetCallbacks(reason); - active_request_.reset(); -} - -void ServerConnectionImpl::sendProtocolErrorOld(absl::string_view details) { - if (active_request_.has_value()) { - active_request_.value().response_encoder_.setDetails(details); - } - // We do this here because we may get a protocol error before we have a logical stream. Higher - // layers can only operate on streams, so there is no coherent way to allow them to send an error - // "out of band." On one hand this is kind of a hack but on the other hand it normalizes HTTP/1.1 - // to look more like HTTP/2 to higher layers. - if (!active_request_.has_value() || - !active_request_.value().response_encoder_.startedResponse()) { - Buffer::OwnedImpl bad_request_response( - absl::StrCat("HTTP/1.1 ", error_code_, " ", CodeUtility::toString(error_code_), - "\r\ncontent-length: 0\r\nconnection: close\r\n\r\n")); - - connection_.write(bad_request_response, false); - } -} - -void ServerConnectionImpl::sendProtocolError(absl::string_view details) { - if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.early_errors_via_hcm")) { - sendProtocolErrorOld(details); - return; - } - // We do this here because we may get a protocol error before we have a logical stream. - if (!active_request_.has_value()) { - onMessageBeginBase(); - } - ASSERT(active_request_.has_value()); - - active_request_.value().response_encoder_.setDetails(details); - if (!active_request_.value().response_encoder_.startedResponse()) { - // Note that the correctness of is_grpc_request and is_head_request is best-effort. - // If headers have not been fully parsed they may not be inferred correctly. - bool is_grpc_request = false; - if (absl::holds_alternative(headers_or_trailers_) && - absl::get(headers_or_trailers_) != nullptr) { - is_grpc_request = - Grpc::Common::isGrpcRequestHeaders(*absl::get(headers_or_trailers_)); - } - active_request_->request_decoder_->sendLocalReply(is_grpc_request, error_code_, - CodeUtility::toString(error_code_), nullptr, - absl::nullopt, details); - return; - } -} - -void ServerConnectionImpl::onAboveHighWatermark() { - if (active_request_.has_value()) { - active_request_.value().response_encoder_.runHighWatermarkCallbacks(); - } -} -void ServerConnectionImpl::onBelowLowWatermark() { - if (active_request_.has_value()) { - active_request_.value().response_encoder_.runLowWatermarkCallbacks(); - } -} - -void ServerConnectionImpl::releaseOutboundResponse( - const Buffer::OwnedBufferFragmentImpl* fragment) { - ASSERT(outbound_responses_ >= 1); - --outbound_responses_; - delete fragment; -} - -void ServerConnectionImpl::checkHeaderNameForUnderscores() { - if (headers_with_underscores_action_ != envoy::config::core::v3::HttpProtocolOptions::ALLOW && - Http::HeaderUtility::headerNameContainsUnderscore(current_header_field_.getStringView())) { - if (headers_with_underscores_action_ == - envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER) { - ENVOY_CONN_LOG(debug, "Dropping header with invalid characters in its name: {}", connection_, - current_header_field_.getStringView()); - stats_.dropped_headers_with_underscores_.inc(); - current_header_field_.clear(); - current_header_value_.clear(); - } else { - ENVOY_CONN_LOG(debug, "Rejecting request due to header name with underscores: {}", - connection_, current_header_field_.getStringView()); - error_code_ = Http::Code::BadRequest; - sendProtocolError(Http1ResponseCodeDetails::get().InvalidUnderscore); - stats_.requests_rejected_with_underscores_in_headers_.inc(); - throw CodecProtocolException("http/1.1 protocol error: header name contains underscores"); - } - } -} - -ClientConnectionImpl::ClientConnectionImpl(Network::Connection& connection, CodecStats& stats, - ConnectionCallbacks&, const Http1Settings& settings, - const uint32_t max_response_headers_count) - : ConnectionImpl(connection, stats, settings, HTTP_RESPONSE, MAX_RESPONSE_HEADERS_KB, - max_response_headers_count, formatter(settings)) {} - -bool ClientConnectionImpl::cannotHaveBody() { - if (pending_response_.has_value() && pending_response_.value().encoder_.headRequest()) { - ASSERT(!pending_response_done_); - return true; - } else if (parser_.status_code == 204 || parser_.status_code == 304 || - (parser_.status_code >= 200 && parser_.content_length == 0 && - !(parser_.flags & F_CHUNKED))) { - return true; - } else { - return false; - } -} - -RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& response_decoder) { - if (resetStreamCalled()) { - throw CodecClientException("cannot create new streams after calling reset"); - } - - // If reads were disabled due to flow control, we expect reads to always be enabled again before - // reusing this connection. This is done when the response is received. - ASSERT(connection_.readEnabled()); - - ASSERT(!pending_response_.has_value()); - ASSERT(pending_response_done_); - pending_response_.emplace(*this, header_key_formatter_.get(), &response_decoder); - pending_response_done_ = false; - return pending_response_.value().encoder_; -} - -int ClientConnectionImpl::onHeadersComplete() { - ENVOY_CONN_LOG(trace, "status_code {}", connection_, parser_.status_code); - - // Handle the case where the client is closing a kept alive connection (by sending a 408 - // with a 'Connection: close' header). In this case we just let response flush out followed - // by the remote close. - if (!pending_response_.has_value() && !resetStreamCalled()) { - throw PrematureResponseException(static_cast(parser_.status_code)); - } else if (pending_response_.has_value()) { - ASSERT(!pending_response_done_); - auto& headers = absl::get(headers_or_trailers_); - ENVOY_CONN_LOG(trace, "Client: onHeadersComplete size={}", connection_, headers->size()); - headers->setStatus(parser_.status_code); - - if (parser_.status_code >= 200 && parser_.status_code < 300 && - pending_response_.value().encoder_.connectRequest()) { - ENVOY_CONN_LOG(trace, "codec entering upgrade mode for CONNECT response.", connection_); - handling_upgrade_ = true; - - // For responses to connect requests, do not accept the chunked - // encoding header: https://tools.ietf.org/html/rfc7231#section-4.3.6 - if (headers->TransferEncoding() && - absl::EqualsIgnoreCase(headers->TransferEncoding()->value().getStringView(), - Headers::get().TransferEncodingValues.Chunked)) { - sendProtocolError(Http1ResponseCodeDetails::get().InvalidTransferEncoding); - throw CodecProtocolException("http/1.1 protocol error: unsupported transfer encoding"); - } - } - - if (strict_1xx_and_204_headers_ && (parser_.status_code < 200 || parser_.status_code == 204)) { - if (headers->TransferEncoding()) { - sendProtocolError(Http1ResponseCodeDetails::get().TransferEncodingNotAllowed); - throw CodecProtocolException( - "http/1.1 protocol error: transfer encoding not allowed in 1xx or 204"); - } - - if (headers->ContentLength()) { - // Report a protocol error for non-zero Content-Length, but paper over zero Content-Length. - if (headers->ContentLength()->value().getStringView() != "0") { - sendProtocolError(Http1ResponseCodeDetails::get().ContentLengthNotAllowed); - throw CodecProtocolException( - "http/1.1 protocol error: content length not allowed in 1xx or 204"); - } - - headers->removeContentLength(); - } - } - - if (parser_.status_code == enumToInt(Http::Code::Continue)) { - pending_response_.value().decoder_->decode100ContinueHeaders(std::move(headers)); - } else if (cannotHaveBody() && !handling_upgrade_) { - deferred_end_stream_headers_ = true; - } else { - pending_response_.value().decoder_->decodeHeaders(std::move(headers), false); - } - - // http-parser treats 1xx headers as their own complete response. Swallow the spurious - // onMessageComplete and continue processing for purely informational headers. - // 101-SwitchingProtocols is exempt as all data after the header is proxied through after - // upgrading. - if (CodeUtility::is1xx(parser_.status_code) && - parser_.status_code != enumToInt(Http::Code::SwitchingProtocols)) { - ignore_message_complete_for_1xx_ = true; - // Reset to ensure no information from the 1xx headers is used for the response headers. - headers_or_trailers_.emplace(nullptr); - } - } - - // Here we deal with cases where the response cannot have a body, but http_parser does not deal - // with it for us. - return cannotHaveBody() ? 1 : 0; -} - -bool ClientConnectionImpl::upgradeAllowed() const { - if (pending_response_.has_value()) { - return pending_response_->encoder_.upgradeRequest(); - } - return false; -} - -void ClientConnectionImpl::onBody(Buffer::Instance& data) { - ASSERT(!deferred_end_stream_headers_); - if (pending_response_.has_value()) { - ASSERT(!pending_response_done_); - pending_response_.value().decoder_->decodeData(data, false); - } -} - -void ClientConnectionImpl::onMessageComplete() { - ENVOY_CONN_LOG(trace, "message complete", connection_); - if (ignore_message_complete_for_1xx_) { - ignore_message_complete_for_1xx_ = false; - return; - } - if (pending_response_.has_value()) { - ASSERT(!pending_response_done_); - // After calling decodeData() with end stream set to true, we should no longer be able to reset. - PendingResponse& response = pending_response_.value(); - // Encoder is used as part of decode* calls later in this function so pending_response_ can not - // be reset just yet. Preserve the state in pending_response_done_ instead. - pending_response_done_ = true; - - if (deferred_end_stream_headers_) { - response.decoder_->decodeHeaders( - std::move(absl::get(headers_or_trailers_)), true); - deferred_end_stream_headers_ = false; - } else if (processing_trailers_) { - response.decoder_->decodeTrailers( - std::move(absl::get(headers_or_trailers_))); - } else { - Buffer::OwnedImpl buffer; - response.decoder_->decodeData(buffer, true); - } - - // Reset to ensure no information from one requests persists to the next. - pending_response_.reset(); - headers_or_trailers_.emplace(nullptr); - } -} - -void ClientConnectionImpl::onResetStream(StreamResetReason reason) { - // Only raise reset if we did not already dispatch a complete response. - if (pending_response_.has_value() && !pending_response_done_) { - pending_response_.value().encoder_.runResetCallbacks(reason); - pending_response_done_ = true; - pending_response_.reset(); - } -} - -void ClientConnectionImpl::sendProtocolError(absl::string_view details) { - if (pending_response_.has_value()) { - ASSERT(!pending_response_done_); - pending_response_.value().encoder_.setDetails(details); - } -} - -void ClientConnectionImpl::onAboveHighWatermark() { - // This should never happen without an active stream/request. - pending_response_.value().encoder_.runHighWatermarkCallbacks(); -} - -void ClientConnectionImpl::onBelowLowWatermark() { - // This can get called without an active stream/request when the response completion causes us to - // close the connection, but in doing so go below low watermark. - if (pending_response_.has_value() && !pending_response_done_) { - pending_response_.value().encoder_.runLowWatermarkCallbacks(); - } -} - -} // namespace Http1 -} // namespace Legacy -} // namespace Http -} // namespace Envoy diff --git a/source/common/http/http1/codec_impl_legacy.h b/source/common/http/http1/codec_impl_legacy.h deleted file mode 100644 index 703de320637e..000000000000 --- a/source/common/http/http1/codec_impl_legacy.h +++ /dev/null @@ -1,617 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include - -#include "envoy/config/core/v3/protocol.pb.h" -#include "envoy/http/codec.h" -#include "envoy/network/connection.h" - -#include "common/buffer/watermark_buffer.h" -#include "common/common/assert.h" -#include "common/common/statusor.h" -#include "common/http/codec_helper.h" -#include "common/http/codes.h" -#include "common/http/header_map_impl.h" -#include "common/http/http1/codec_stats.h" -#include "common/http/http1/header_formatter.h" -#include "common/http/status.h" - -namespace Envoy { -namespace Http { -namespace Legacy { -namespace Http1 { - -class ConnectionImpl; - -/** - * Base class for HTTP/1.1 request and response encoders. - */ -class StreamEncoderImpl : public virtual StreamEncoder, - public Stream, - Logger::Loggable, - public StreamCallbackHelper, - public Http1StreamEncoderOptions { -public: - ~StreamEncoderImpl() override { - // When the stream goes away, undo any read blocks to resume reading. - while (read_disable_calls_ != 0) { - StreamEncoderImpl::readDisable(false); - } - } - // Http::StreamEncoder - void encodeData(Buffer::Instance& data, bool end_stream) override; - void encodeMetadata(const MetadataMapVector&) override; - Stream& getStream() override { return *this; } - Http1StreamEncoderOptionsOptRef http1StreamEncoderOptions() override { return *this; } - - // Http::Http1StreamEncoderOptions - void disableChunkEncoding() override { disable_chunk_encoding_ = true; } - - // Http::Stream - void addCallbacks(StreamCallbacks& callbacks) override { addCallbacksHelper(callbacks); } - void removeCallbacks(StreamCallbacks& callbacks) override { removeCallbacksHelper(callbacks); } - // After this is called, for the HTTP/1 codec, the connection should be closed, i.e. no further - // progress may be made with the codec. - void resetStream(StreamResetReason reason) override; - void readDisable(bool disable) override; - uint32_t bufferLimit() override; - absl::string_view responseDetails() override { return details_; } - const Network::Address::InstanceConstSharedPtr& connectionLocalAddress() override; - void setFlushTimeout(std::chrono::milliseconds) override { - // HTTP/1 has one stream per connection, thus any data encoded is immediately written to the - // connection, invoking any watermarks as necessary. There is no internal buffering that would - // require a flush timeout not already covered by other timeouts. - } - - void setIsResponseToHeadRequest(bool value) { is_response_to_head_request_ = value; } - void setIsResponseToConnectRequest(bool value) { is_response_to_connect_request_ = value; } - void setDetails(absl::string_view details) { details_ = details; } - - void clearReadDisableCallsForTests() { read_disable_calls_ = 0; } - -protected: - StreamEncoderImpl(ConnectionImpl& connection, - Http::Http1::HeaderKeyFormatter* header_key_formatter); - void encodeHeadersBase(const RequestOrResponseHeaderMap& headers, absl::optional status, - bool end_stream); - void encodeTrailersBase(const HeaderMap& headers); - - static const std::string CRLF; - static const std::string LAST_CHUNK; - - ConnectionImpl& connection_; - uint32_t read_disable_calls_{}; - bool disable_chunk_encoding_ : 1; - bool chunk_encoding_ : 1; - bool connect_request_ : 1; - bool is_response_to_head_request_ : 1; - bool is_response_to_connect_request_ : 1; - -private: - /** - * Called to encode an individual header. - * @param key supplies the header to encode. - * @param key_size supplies the byte size of the key. - * @param value supplies the value to encode. - * @param value_size supplies the byte size of the value. - */ - void encodeHeader(const char* key, uint32_t key_size, const char* value, uint32_t value_size); - - /** - * Called to encode an individual header. - * @param key supplies the header to encode as a string_view. - * @param value supplies the value to encode as a string_view. - */ - void encodeHeader(absl::string_view key, absl::string_view value); - - /** - * Called to finalize a stream encode. - */ - void endEncode(); - - void encodeFormattedHeader(absl::string_view key, absl::string_view value); - - const Http::Http1::HeaderKeyFormatter* const header_key_formatter_; - absl::string_view details_; -}; - -/** - * HTTP/1.1 response encoder. - */ -class ResponseEncoderImpl : public StreamEncoderImpl, public ResponseEncoder { -public: - ResponseEncoderImpl(ConnectionImpl& connection, - Http::Http1::HeaderKeyFormatter* header_key_formatter, - bool stream_error_on_invalid_http_message) - : StreamEncoderImpl(connection, header_key_formatter), - stream_error_on_invalid_http_message_(stream_error_on_invalid_http_message) {} - - bool startedResponse() { return started_response_; } - - // Http::ResponseEncoder - void encode100ContinueHeaders(const ResponseHeaderMap& headers) override; - void encodeHeaders(const ResponseHeaderMap& headers, bool end_stream) override; - void encodeTrailers(const ResponseTrailerMap& trailers) override { encodeTrailersBase(trailers); } - - bool streamErrorOnInvalidHttpMessage() const override { - return stream_error_on_invalid_http_message_; - } - -private: - bool started_response_{}; - const bool stream_error_on_invalid_http_message_; -}; - -/** - * HTTP/1.1 request encoder. - */ -class RequestEncoderImpl : public StreamEncoderImpl, public RequestEncoder { -public: - RequestEncoderImpl(ConnectionImpl& connection, - Http::Http1::HeaderKeyFormatter* header_key_formatter) - : StreamEncoderImpl(connection, header_key_formatter) {} - bool upgradeRequest() const { return upgrade_request_; } - bool headRequest() const { return head_request_; } - bool connectRequest() const { return connect_request_; } - - // Http::RequestEncoder - Status encodeHeaders(const RequestHeaderMap& headers, bool end_stream) override; - void encodeTrailers(const RequestTrailerMap& trailers) override { encodeTrailersBase(trailers); } - -private: - bool upgrade_request_{}; - bool head_request_{}; -}; - -/** - * Base class for HTTP/1.1 client and server connections. - * Handles the callbacks of http_parser with its own base routine and then - * virtual dispatches to its subclasses. - */ -class ConnectionImpl : public virtual Connection, protected Logger::Loggable { -public: - /** - * @return Network::Connection& the backing network connection. - */ - Network::Connection& connection() { return connection_; } - - /** - * Called when the active encoder has completed encoding the outbound half of the stream. - */ - virtual void onEncodeComplete() PURE; - - /** - * Called when resetStream() has been called on an active stream. In HTTP/1.1 the only - * valid operation after this point is for the connection to get blown away, but we will not - * fire any more callbacks in case some stack has to unwind. - */ - void onResetStreamBase(StreamResetReason reason); - - /** - * Flush all pending output from encoding. - */ - void flushOutput(bool end_encode = false); - - void addToBuffer(absl::string_view data); - void addCharToBuffer(char c); - void addIntToBuffer(uint64_t i); - Buffer::Instance& buffer() { return *output_buffer_; } - uint64_t bufferRemainingSize(); - void copyToBuffer(const char* data, uint64_t length); - void reserveBuffer(uint64_t size); - void readDisable(bool disable) { - if (connection_.state() == Network::Connection::State::Open) { - connection_.readDisable(disable); - } - } - uint32_t bufferLimit() { return connection_.bufferLimit(); } - virtual bool supportsHttp10() { return false; } - bool maybeDirectDispatch(Buffer::Instance& data); - virtual void maybeAddSentinelBufferFragment(Buffer::Instance&) {} - Http::Http1::CodecStats& stats() { return stats_; } - bool enableTrailers() const { return codec_settings_.enable_trailers_; } - - // Http::Connection - Http::Status dispatch(Buffer::Instance& data) override; - void goAway() override {} // Called during connection manager drain flow - Protocol protocol() override { return protocol_; } - void shutdownNotice() override {} // Called during connection manager drain flow - bool wantsToWrite() override { return false; } - void onUnderlyingConnectionAboveWriteBufferHighWatermark() override { onAboveHighWatermark(); } - void onUnderlyingConnectionBelowWriteBufferLowWatermark() override { onBelowLowWatermark(); } - - bool strict1xxAnd204Headers() { return strict_1xx_and_204_headers_; } - -protected: - ConnectionImpl(Network::Connection& connection, Http::Http1::CodecStats& stats, - const Http1Settings& settings, http_parser_type type, uint32_t max_headers_kb, - const uint32_t max_headers_count, - Http::Http1::HeaderKeyFormatterPtr&& header_key_formatter); - - bool resetStreamCalled() { return reset_stream_called_; } - void onMessageBeginBase(); - - /** - * Get memory used to represent HTTP headers or trailers currently being parsed. - * Computed by adding the partial header field and value that is currently being parsed and the - * estimated header size for previous header lines provided by HeaderMap::byteSize(). - */ - virtual uint32_t getHeadersSize(); - - /** - * Called from onUrl, onHeaderField and onHeaderValue to verify that the headers do not exceed the - * configured max header size limit. Throws a CodecProtocolException if headers exceed the size - * limit. - */ - void checkMaxHeadersSize(); - - Network::Connection& connection_; - Http::Http1::CodecStats& stats_; - const Http1Settings codec_settings_; - http_parser parser_; - Buffer::Instance* current_dispatching_buffer_{}; - Http::Code error_code_{Http::Code::BadRequest}; - const Http::Http1::HeaderKeyFormatterPtr header_key_formatter_; - HeaderString current_header_field_; - HeaderString current_header_value_; - bool processing_trailers_ : 1; - bool handling_upgrade_ : 1; - bool reset_stream_called_ : 1; - // Deferred end stream headers indicate that we are not going to raise headers until the full - // HTTP/1 message has been flushed from the parser. This allows raising an HTTP/2 style headers - // block with end stream set to true with no further protocol data remaining. - bool deferred_end_stream_headers_ : 1; - const bool strict_1xx_and_204_headers_ : 1; - bool dispatching_slice_already_drained_ : 1; - -private: - enum class HeaderParsingState { Field, Value, Done }; - - virtual HeaderMap& headersOrTrailers() PURE; - virtual RequestOrResponseHeaderMap& requestOrResponseHeaders() PURE; - virtual void allocHeaders() PURE; - virtual void allocTrailers() PURE; - - /** - * Called in order to complete an in progress header decode. - */ - void completeLastHeader(); - - /** - * Check if header name contains underscore character. - * Underscore character is allowed in header names by the RFC-7230 and this check is implemented - * as a security measure due to systems that treat '_' and '-' as interchangeable. - * The ServerConnectionImpl may drop header or reject request based on the - * `common_http_protocol_options.headers_with_underscores_action` configuration option in the - * HttpConnectionManager. - */ - virtual bool shouldDropHeaderWithUnderscoresInNames(absl::string_view /* header_name */) const { - return false; - } - - /** - * An inner dispatch call that executes the dispatching logic. While exception removal is in - * migration (#10878), this function may either throw an exception or return an error status. - * Exceptions are caught and translated to their corresponding statuses in the outer level - * dispatch. - * TODO(#10878): Remove this when exception removal is complete. - */ - Http::Status innerDispatch(Buffer::Instance& data); - - /** - * Dispatch a memory span. - * @param slice supplies the start address. - * @len supplies the length of the span. - */ - size_t dispatchSlice(const char* slice, size_t len); - - /** - * Called by the http_parser when body data is received. - * @param data supplies the start address. - * @param length supplies the length. - */ - void bufferBody(const char* data, size_t length); - - /** - * Push the accumulated body through the filter pipeline. - */ - void dispatchBufferedBody(); - - /** - * Called when a request/response is beginning. A base routine happens first then a virtual - * dispatch is invoked. - */ - virtual void onMessageBegin() PURE; - - /** - * Called when URL data is received. - * @param data supplies the start address. - * @param length supplies the length. - */ - virtual void onUrl(const char* data, size_t length) PURE; - - /** - * Called when header field data is received. - * @param data supplies the start address. - * @param length supplies the length. - */ - void onHeaderField(const char* data, size_t length); - - /** - * Called when header value data is received. - * @param data supplies the start address. - * @param length supplies the length. - */ - void onHeaderValue(const char* data, size_t length); - - /** - * Called when headers are complete. A base routine happens first then a virtual dispatch is - * invoked. Note that this only applies to headers and NOT trailers. End of - * trailers are signaled via onMessageCompleteBase(). - * @return 0 if no error, 1 if there should be no body. - */ - int onHeadersCompleteBase(); - virtual int onHeadersComplete() PURE; - - /** - * Called to see if upgrade transition is allowed. - */ - virtual bool upgradeAllowed() const PURE; - - /** - * Called with body data is available for processing when either: - * - There is an accumulated partial body after the parser is done processing bytes read from the - * socket - * - The parser encounters the last byte of the body - * - The codec does a direct dispatch from the read buffer - * For performance reasons there is at most one call to onBody per call to HTTP/1 - * ConnectionImpl::dispatch call. - * @param data supplies the body data - */ - virtual void onBody(Buffer::Instance& data) PURE; - - /** - * Called when the request/response is complete. - */ - void onMessageCompleteBase(); - virtual void onMessageComplete() PURE; - - /** - * Called when accepting a chunk header. - */ - void onChunkHeader(bool is_final_chunk); - - /** - * @see onResetStreamBase(). - */ - virtual void onResetStream(StreamResetReason reason) PURE; - - /** - * Send a protocol error response to remote. - */ - virtual void sendProtocolError(absl::string_view details) PURE; - - /** - * Called when output_buffer_ or the underlying connection go from below a low watermark to over - * a high watermark. - */ - virtual void onAboveHighWatermark() PURE; - - /** - * Called when output_buffer_ or the underlying connection go from above a high watermark to - * below a low watermark. - */ - virtual void onBelowLowWatermark() PURE; - - /** - * Check if header name contains underscore character. - * The ServerConnectionImpl may drop header or reject request based on configuration. - */ - virtual void checkHeaderNameForUnderscores() {} - - static http_parser_settings settings_; - - HeaderParsingState header_parsing_state_{HeaderParsingState::Field}; - // Used to accumulate the HTTP message body during the current dispatch call. The accumulated body - // is pushed through the filter pipeline either at the end of the current dispatch call, or when - // the last byte of the body is processed (whichever happens first). - Buffer::OwnedImpl buffered_body_; - Buffer::InstancePtr output_buffer_; - Protocol protocol_{Protocol::Http11}; - const uint32_t max_headers_kb_; - const uint32_t max_headers_count_; -}; - -/** - * Implementation of Http::ServerConnection for HTTP/1.1. - */ -class ServerConnectionImpl : public ServerConnection, public ConnectionImpl { -public: - ServerConnectionImpl(Network::Connection& connection, Http::Http1::CodecStats& stats, - ServerConnectionCallbacks& callbacks, const Http1Settings& settings, - uint32_t max_request_headers_kb, const uint32_t max_request_headers_count, - envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction - headers_with_underscores_action); - bool supportsHttp10() override { return codec_settings_.accept_http_10_; } - -protected: - /** - * An active HTTP/1.1 request. - */ - struct ActiveRequest { - ActiveRequest(ServerConnectionImpl& connection, - Http::Http1::HeaderKeyFormatter* header_key_formatter) - : response_encoder_(connection, header_key_formatter, - connection.codec_settings_.stream_error_on_invalid_http_message_) {} - - HeaderString request_url_; - RequestDecoder* request_decoder_{}; - ResponseEncoderImpl response_encoder_; - bool remote_complete_{}; - }; - absl::optional& activeRequest() { return active_request_; } - // ConnectionImpl - void onMessageComplete() override; - // Add the size of the request_url to the reported header size when processing request headers. - uint32_t getHeadersSize() override; - -private: - /** - * Manipulate the request's first line, parsing the url and converting to a relative path if - * necessary. Compute Host / :authority headers based on 7230#5.7 and 7230#6 - * - * @param is_connect true if the request has the CONNECT method - * @param headers the request's headers - * @throws CodecProtocolException on an invalid url in the request line - */ - void handlePath(RequestHeaderMap& headers, unsigned int method); - - // ConnectionImpl - void onEncodeComplete() override; - void onMessageBegin() override; - void onUrl(const char* data, size_t length) override; - int onHeadersComplete() override; - // If upgrade behavior is not allowed, the HCM will have sanitized the headers out. - bool upgradeAllowed() const override { return true; } - void onBody(Buffer::Instance& data) override; - void onResetStream(StreamResetReason reason) override; - void sendProtocolError(absl::string_view details) override; - void onAboveHighWatermark() override; - void onBelowLowWatermark() override; - HeaderMap& headersOrTrailers() override { - if (absl::holds_alternative(headers_or_trailers_)) { - return *absl::get(headers_or_trailers_); - } else { - return *absl::get(headers_or_trailers_); - } - } - RequestOrResponseHeaderMap& requestOrResponseHeaders() override { - return *absl::get(headers_or_trailers_); - } - void allocHeaders() override { - ASSERT(nullptr == absl::get(headers_or_trailers_)); - ASSERT(!processing_trailers_); - headers_or_trailers_.emplace(RequestHeaderMapImpl::create()); - } - void allocTrailers() override { - ASSERT(processing_trailers_); - if (!absl::holds_alternative(headers_or_trailers_)) { - headers_or_trailers_.emplace(RequestTrailerMapImpl::create()); - } - } - - void sendProtocolErrorOld(absl::string_view details); - - void releaseOutboundResponse(const Buffer::OwnedBufferFragmentImpl* fragment); - void maybeAddSentinelBufferFragment(Buffer::Instance& output_buffer) override; - void doFloodProtectionChecks() const; - void checkHeaderNameForUnderscores() override; - - ServerConnectionCallbacks& callbacks_; - absl::optional active_request_; - const Buffer::OwnedBufferFragmentImpl::Releasor response_buffer_releasor_; - uint32_t outbound_responses_{}; - // This defaults to 2, which functionally disables pipelining. If any users - // of Envoy wish to enable pipelining (which is dangerous and ill supported) - // we could make this configurable. - uint32_t max_outbound_responses_{}; - // TODO(mattklein123): This should be a member of ActiveRequest but this change needs dedicated - // thought as some of the reset and no header code paths make this difficult. Headers are - // populated on message begin. Trailers are populated on the first parsed trailer field (if - // trailers are enabled). The variant is reset to null headers on message complete for assertion - // purposes. - absl::variant headers_or_trailers_; - // The action to take when a request header name contains underscore characters. - const envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction - headers_with_underscores_action_; -}; - -/** - * Implementation of Http::ClientConnection for HTTP/1.1. - */ -class ClientConnectionImpl : public ClientConnection, public ConnectionImpl { -public: - ClientConnectionImpl(Network::Connection& connection, Http::Http1::CodecStats& stats, - ConnectionCallbacks& callbacks, const Http1Settings& settings, - const uint32_t max_response_headers_count); - - // Http::ClientConnection - RequestEncoder& newStream(ResponseDecoder& response_decoder) override; - -private: - struct PendingResponse { - PendingResponse(ConnectionImpl& connection, - Http::Http1::HeaderKeyFormatter* header_key_formatter, ResponseDecoder* decoder) - : encoder_(connection, header_key_formatter), decoder_(decoder) {} - - RequestEncoderImpl encoder_; - ResponseDecoder* decoder_; - }; - - bool cannotHaveBody(); - - // ConnectionImpl - Http::Status dispatch(Buffer::Instance& data) override; - void onEncodeComplete() override {} - void onMessageBegin() override {} - void onUrl(const char*, size_t) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } - int onHeadersComplete() override; - bool upgradeAllowed() const override; - void onBody(Buffer::Instance& data) override; - void onMessageComplete() override; - void onResetStream(StreamResetReason reason) override; - void sendProtocolError(absl::string_view details) override; - void onAboveHighWatermark() override; - void onBelowLowWatermark() override; - HeaderMap& headersOrTrailers() override { - if (absl::holds_alternative(headers_or_trailers_)) { - return *absl::get(headers_or_trailers_); - } else { - return *absl::get(headers_or_trailers_); - } - } - RequestOrResponseHeaderMap& requestOrResponseHeaders() override { - return *absl::get(headers_or_trailers_); - } - void allocHeaders() override { - ASSERT(nullptr == absl::get(headers_or_trailers_)); - ASSERT(!processing_trailers_); - headers_or_trailers_.emplace(ResponseHeaderMapImpl::create()); - } - void allocTrailers() override { - ASSERT(processing_trailers_); - if (!absl::holds_alternative(headers_or_trailers_)) { - headers_or_trailers_.emplace(ResponseTrailerMapImpl::create()); - } - } - - absl::optional pending_response_; - // TODO(mattklein123): The following bool tracks whether a pending response is complete before - // dispatching callbacks. This is needed so that pending_response_ stays valid during callbacks - // in order to access the stream, but to avoid invoking callbacks that shouldn't be called once - // the response is complete. The existence of this variable is hard to reason about and it should - // be combined with pending_response_ somehow in a follow up cleanup. - bool pending_response_done_{true}; - // Set true between receiving non-101 1xx headers and receiving the spurious onMessageComplete. - bool ignore_message_complete_for_1xx_{}; - // TODO(mattklein123): This should be a member of PendingResponse but this change needs dedicated - // thought as some of the reset and no header code paths make this difficult. Headers are - // populated on message begin. Trailers are populated when the switch to trailer processing is - // detected while parsing the first trailer field (if trailers are enabled). The variant is reset - // to null headers on message complete for assertion purposes. - absl::variant headers_or_trailers_; - - // The default limit of 80 KiB is the vanilla http_parser behaviour. - static constexpr uint32_t MAX_RESPONSE_HEADERS_KB = 80; -}; - -} // namespace Http1 -} // namespace Legacy -} // namespace Http -} // namespace Envoy diff --git a/source/common/http/http2/BUILD b/source/common/http/http2/BUILD index 832decc73f68..826e3f2beeef 100644 --- a/source/common/http/http2/BUILD +++ b/source/common/http/http2/BUILD @@ -18,38 +18,6 @@ envoy_cc_library( ], ) -CODEC_LIB_DEPS = [ - ":codec_stats_lib", - ":metadata_decoder_lib", - ":metadata_encoder_lib", - ":protocol_constraints_lib", - "//include/envoy/event:deferred_deletable", - "//include/envoy/event:dispatcher_interface", - "//include/envoy/http:codec_interface", - "//include/envoy/http:codes_interface", - "//include/envoy/http:header_map_interface", - "//include/envoy/network:connection_interface", - "//include/envoy/stats:stats_interface", - "//source/common/buffer:buffer_lib", - "//source/common/buffer:watermark_buffer_lib", - "//source/common/common:assert_lib", - "//source/common/common:enum_to_int", - "//source/common/common:linked_object", - "//source/common/common:minimal_logger_lib", - "//source/common/common:statusor_lib", - "//source/common/common:utility_lib", - "//source/common/http:codec_helper_lib", - "//source/common/http:codes_lib", - "//source/common/http:exception_lib", - "//source/common/http:header_map_lib", - "//source/common/http:header_utility_lib", - "//source/common/http:headers_lib", - "//source/common/http:status_lib", - "//source/common/http:utility_lib", - "//source/common/runtime:runtime_features_lib", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", -] - envoy_cc_library( name = "codec_lib", srcs = ["codec_impl.cc"], @@ -60,23 +28,37 @@ envoy_cc_library( "abseil_inlined_vector", "abseil_algorithm", ], - deps = CODEC_LIB_DEPS, -) - -envoy_cc_library( - name = "codec_legacy_lib", - srcs = ["codec_impl_legacy.cc"], - hdrs = [ - "codec_impl.h", - "codec_impl_legacy.h", - ], - external_deps = [ - "nghttp2", - "abseil_optional", - "abseil_inlined_vector", - "abseil_algorithm", + deps = [ + ":codec_stats_lib", + ":metadata_decoder_lib", + ":metadata_encoder_lib", + ":protocol_constraints_lib", + "//include/envoy/event:deferred_deletable", + "//include/envoy/event:dispatcher_interface", + "//include/envoy/http:codec_interface", + "//include/envoy/http:codes_interface", + "//include/envoy/http:header_map_interface", + "//include/envoy/network:connection_interface", + "//include/envoy/stats:stats_interface", + "//source/common/buffer:buffer_lib", + "//source/common/buffer:watermark_buffer_lib", + "//source/common/common:assert_lib", + "//source/common/common:enum_to_int", + "//source/common/common:linked_object", + "//source/common/common:minimal_logger_lib", + "//source/common/common:statusor_lib", + "//source/common/common:utility_lib", + "//source/common/http:codec_helper_lib", + "//source/common/http:codes_lib", + "//source/common/http:exception_lib", + "//source/common/http:header_map_lib", + "//source/common/http:header_utility_lib", + "//source/common/http:headers_lib", + "//source/common/http:status_lib", + "//source/common/http:utility_lib", + "//source/common/runtime:runtime_features_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], - deps = CODEC_LIB_DEPS, ) # Separate library for some nghttp2 setup stuff to avoid having tests take a diff --git a/source/common/http/http2/codec_impl_legacy.cc b/source/common/http/http2/codec_impl_legacy.cc deleted file mode 100644 index 794885e47526..000000000000 --- a/source/common/http/http2/codec_impl_legacy.cc +++ /dev/null @@ -1,1534 +0,0 @@ -#include "common/http/http2/codec_impl_legacy.h" - -#include -#include -#include - -#include "envoy/event/dispatcher.h" -#include "envoy/http/codes.h" -#include "envoy/http/header_map.h" -#include "envoy/network/connection.h" - -#include "common/common/assert.h" -#include "common/common/cleanup.h" -#include "common/common/enum_to_int.h" -#include "common/common/fmt.h" -#include "common/common/utility.h" -#include "common/http/codes.h" -#include "common/http/exception.h" -#include "common/http/header_utility.h" -#include "common/http/headers.h" -#include "common/http/http2/codec_stats.h" -#include "common/http/utility.h" -#include "common/runtime/runtime_features.h" - -#include "absl/container/fixed_array.h" - -namespace Envoy { -namespace Http { -namespace Legacy { -namespace Http2 { - -class Http2ResponseCodeDetailValues { -public: - // Invalid HTTP header field was received and stream is going to be - // closed. - const absl::string_view ng_http2_err_http_header_ = "http2.invalid.header.field"; - // Violation in HTTP messaging rule. - const absl::string_view ng_http2_err_http_messaging_ = "http2.violation.of.messaging.rule"; - // none of the above - const absl::string_view ng_http2_err_unknown_ = "http2.unknown.nghttp2.error"; - // The number of headers (or trailers) exceeded the configured limits - const absl::string_view too_many_headers = "http2.too_many_headers"; - // Envoy detected an HTTP/2 frame flood from the server. - const absl::string_view outbound_frame_flood = "http2.outbound_frames_flood"; - // Envoy detected an inbound HTTP/2 frame flood. - const absl::string_view inbound_empty_frame_flood = "http2.inbound_empty_frames_flood"; - // Envoy was configured to drop requests with header keys beginning with underscores. - const absl::string_view invalid_underscore = "http2.unexpected_underscore"; - // The upstream refused the stream. - const absl::string_view remote_refused = "http2.remote_refuse"; - // The upstream reset the stream. - const absl::string_view remote_reset = "http2.remote_reset"; - - const absl::string_view errorDetails(int error_code) const { - switch (error_code) { - case NGHTTP2_ERR_HTTP_HEADER: - return ng_http2_err_http_header_; - case NGHTTP2_ERR_HTTP_MESSAGING: - return ng_http2_err_http_messaging_; - default: - return ng_http2_err_unknown_; - } - } -}; - -int reasonToReset(StreamResetReason reason) { - switch (reason) { - case StreamResetReason::LocalRefusedStreamReset: - return NGHTTP2_REFUSED_STREAM; - case StreamResetReason::ConnectError: - return NGHTTP2_CONNECT_ERROR; - default: - return NGHTTP2_NO_ERROR; - } -} - -using Http2ResponseCodeDetails = ConstSingleton; -using Http::Http2::CodecStats; -using Http::Http2::MetadataDecoder; -using Http::Http2::MetadataEncoder; - -bool Utility::reconstituteCrumbledCookies(const HeaderString& key, const HeaderString& value, - HeaderString& cookies) { - if (key != Headers::get().Cookie.get().c_str()) { - return false; - } - - if (!cookies.empty()) { - cookies.append("; ", 2); - } - - const absl::string_view value_view = value.getStringView(); - cookies.append(value_view.data(), value_view.size()); - return true; -} - -ConnectionImpl::Http2Callbacks ConnectionImpl::http2_callbacks_; - -nghttp2_session* ProdNghttp2SessionFactory::create(const nghttp2_session_callbacks* callbacks, - ConnectionImpl* connection, - const nghttp2_option* options) { - nghttp2_session* session; - nghttp2_session_client_new2(&session, callbacks, connection, options); - return session; -} - -void ProdNghttp2SessionFactory::init(nghttp2_session*, ConnectionImpl* connection, - const envoy::config::core::v3::Http2ProtocolOptions& options) { - connection->sendSettings(options, true); -} - -/** - * Helper to remove const during a cast. nghttp2 takes non-const pointers for headers even though - * it copies them. - */ -template static T* removeConst(const void* object) { - return const_cast(reinterpret_cast(object)); -} - -ConnectionImpl::StreamImpl::StreamImpl(ConnectionImpl& parent, uint32_t buffer_limit) - : parent_(parent), local_end_stream_sent_(false), remote_end_stream_(false), - data_deferred_(false), received_noninformational_headers_(false), - pending_receive_buffer_high_watermark_called_(false), - pending_send_buffer_high_watermark_called_(false), reset_due_to_messaging_error_(false) { - parent_.stats_.streams_active_.inc(); - if (buffer_limit > 0) { - setWriteBufferWatermarks(buffer_limit / 2, buffer_limit); - } -} - -ConnectionImpl::StreamImpl::~StreamImpl() { ASSERT(stream_idle_timer_ == nullptr); } - -void ConnectionImpl::StreamImpl::destroy() { - disarmStreamIdleTimer(); - parent_.stats_.streams_active_.dec(); - parent_.stats_.pending_send_bytes_.sub(pending_send_data_.length()); -} - -static void insertHeader(std::vector& headers, const HeaderEntry& header) { - uint8_t flags = 0; - if (header.key().isReference()) { - flags |= NGHTTP2_NV_FLAG_NO_COPY_NAME; - } - if (header.value().isReference()) { - flags |= NGHTTP2_NV_FLAG_NO_COPY_VALUE; - } - const absl::string_view header_key = header.key().getStringView(); - const absl::string_view header_value = header.value().getStringView(); - headers.push_back({removeConst(header_key.data()), - removeConst(header_value.data()), header_key.size(), - header_value.size(), flags}); -} - -void ConnectionImpl::StreamImpl::buildHeaders(std::vector& final_headers, - const HeaderMap& headers) { - final_headers.reserve(headers.size()); - headers.iterate([&final_headers](const HeaderEntry& header) -> HeaderMap::Iterate { - insertHeader(final_headers, header); - return HeaderMap::Iterate::Continue; - }); -} - -void ConnectionImpl::ServerStreamImpl::encode100ContinueHeaders(const ResponseHeaderMap& headers) { - ASSERT(headers.Status()->value() == "100"); - encodeHeaders(headers, false); -} - -void ConnectionImpl::StreamImpl::encodeHeadersBase(const std::vector& final_headers, - bool end_stream) { - nghttp2_data_provider provider; - if (!end_stream) { - provider.source.ptr = this; - provider.read_callback = [](nghttp2_session*, int32_t, uint8_t*, size_t length, - uint32_t* data_flags, nghttp2_data_source* source, - void*) -> ssize_t { - return static_cast(source->ptr)->onDataSourceRead(length, data_flags); - }; - } - - local_end_stream_ = end_stream; - submitHeaders(final_headers, end_stream ? nullptr : &provider); - parent_.sendPendingFrames(); - parent_.checkProtocolConstraintViolation(); -} - -Status ConnectionImpl::ClientStreamImpl::encodeHeaders(const RequestHeaderMap& headers, - bool end_stream) { - // This must exist outside of the scope of isUpgrade as the underlying memory is - // needed until encodeHeadersBase has been called. - std::vector final_headers; - Http::RequestHeaderMapPtr modified_headers; - if (Http::Utility::isUpgrade(headers)) { - modified_headers = createHeaderMap(headers); - upgrade_type_ = std::string(headers.getUpgradeValue()); - Http::Utility::transformUpgradeRequestFromH1toH2(*modified_headers); - buildHeaders(final_headers, *modified_headers); - } else if (headers.Method() && headers.Method()->value() == "CONNECT") { - // If this is not an upgrade style connect (above branch) it is a bytestream - // connect and should have :path and :protocol set accordingly - // As HTTP/1.1 does not require a path for CONNECT, we may have to add one - // if shifting codecs. For now, default to "/" - this can be made - // configurable if necessary. - // https://tools.ietf.org/html/draft-kinnear-httpbis-http2-transport-02 - modified_headers = createHeaderMap(headers); - modified_headers->setProtocol(Headers::get().ProtocolValues.Bytestream); - if (!headers.Path()) { - modified_headers->setPath("/"); - } - buildHeaders(final_headers, *modified_headers); - } else { - buildHeaders(final_headers, headers); - } - encodeHeadersBase(final_headers, end_stream); - return okStatus(); -} - -void ConnectionImpl::ServerStreamImpl::encodeHeaders(const ResponseHeaderMap& headers, - bool end_stream) { - // The contract is that client codecs must ensure that :status is present. - ASSERT(headers.Status() != nullptr); - - // This must exist outside of the scope of isUpgrade as the underlying memory is - // needed until encodeHeadersBase has been called. - std::vector final_headers; - Http::ResponseHeaderMapPtr modified_headers; - if (Http::Utility::isUpgrade(headers)) { - modified_headers = createHeaderMap(headers); - Http::Utility::transformUpgradeResponseFromH1toH2(*modified_headers); - buildHeaders(final_headers, *modified_headers); - } else { - buildHeaders(final_headers, headers); - } - encodeHeadersBase(final_headers, end_stream); -} - -void ConnectionImpl::StreamImpl::encodeTrailersBase(const HeaderMap& trailers) { - ASSERT(!local_end_stream_); - local_end_stream_ = true; - if (pending_send_data_.length() > 0) { - // In this case we want trailers to come after we release all pending body data that is - // waiting on window updates. We need to save the trailers so that we can emit them later. - // However, for empty trailers, we don't need to to save the trailers. - ASSERT(!pending_trailers_to_encode_); - const bool skip_encoding_empty_trailers = - trailers.empty() && parent_.skip_encoding_empty_trailers_; - if (!skip_encoding_empty_trailers) { - pending_trailers_to_encode_ = cloneTrailers(trailers); - createPendingFlushTimer(); - } - } else { - submitTrailers(trailers); - parent_.sendPendingFrames(); - parent_.checkProtocolConstraintViolation(); - } -} - -void ConnectionImpl::StreamImpl::encodeMetadata(const MetadataMapVector& metadata_map_vector) { - ASSERT(parent_.allow_metadata_); - MetadataEncoder& metadata_encoder = getMetadataEncoder(); - if (!metadata_encoder.createPayload(metadata_map_vector)) { - return; - } - for (uint8_t flags : metadata_encoder.payloadFrameFlagBytes()) { - submitMetadata(flags); - } - parent_.sendPendingFrames(); - parent_.checkProtocolConstraintViolation(); -} - -void ConnectionImpl::StreamImpl::readDisable(bool disable) { - ENVOY_CONN_LOG(debug, "Stream {} {}, unconsumed_bytes {} read_disable_count {}", - parent_.connection_, stream_id_, (disable ? "disabled" : "enabled"), - unconsumed_bytes_, read_disable_count_); - if (disable) { - ++read_disable_count_; - } else { - ASSERT(read_disable_count_ > 0); - --read_disable_count_; - if (!buffersOverrun()) { - nghttp2_session_consume(parent_.session_, stream_id_, unconsumed_bytes_); - unconsumed_bytes_ = 0; - parent_.sendPendingFrames(); - parent_.checkProtocolConstraintViolation(); - } - } -} - -void ConnectionImpl::StreamImpl::pendingRecvBufferHighWatermark() { - ENVOY_CONN_LOG(debug, "recv buffer over limit ", parent_.connection_); - ASSERT(!pending_receive_buffer_high_watermark_called_); - pending_receive_buffer_high_watermark_called_ = true; - readDisable(true); -} - -void ConnectionImpl::StreamImpl::pendingRecvBufferLowWatermark() { - ENVOY_CONN_LOG(debug, "recv buffer under limit ", parent_.connection_); - ASSERT(pending_receive_buffer_high_watermark_called_); - pending_receive_buffer_high_watermark_called_ = false; - readDisable(false); -} - -void ConnectionImpl::ClientStreamImpl::decodeHeaders() { - auto& headers = absl::get(headers_or_trailers_); - const uint64_t status = Http::Utility::getResponseStatus(*headers); - - if (!upgrade_type_.empty() && headers->Status()) { - Http::Utility::transformUpgradeResponseFromH2toH1(*headers, upgrade_type_); - } - - // Non-informational headers are non-1xx OR 101-SwitchingProtocols, since 101 implies that further - // proxying is on an upgrade path. - received_noninformational_headers_ = - !CodeUtility::is1xx(status) || status == enumToInt(Http::Code::SwitchingProtocols); - - if (status == enumToInt(Http::Code::Continue)) { - ASSERT(!remote_end_stream_); - response_decoder_.decode100ContinueHeaders(std::move(headers)); - } else { - response_decoder_.decodeHeaders(std::move(headers), remote_end_stream_); - } -} - -void ConnectionImpl::ClientStreamImpl::decodeTrailers() { - response_decoder_.decodeTrailers( - std::move(absl::get(headers_or_trailers_))); -} - -void ConnectionImpl::ServerStreamImpl::decodeHeaders() { - auto& headers = absl::get(headers_or_trailers_); - if (Http::Utility::isH2UpgradeRequest(*headers)) { - Http::Utility::transformUpgradeRequestFromH2toH1(*headers); - } - request_decoder_->decodeHeaders(std::move(headers), remote_end_stream_); -} - -void ConnectionImpl::ServerStreamImpl::decodeTrailers() { - request_decoder_->decodeTrailers( - std::move(absl::get(headers_or_trailers_))); -} - -void ConnectionImpl::StreamImpl::pendingSendBufferHighWatermark() { - ENVOY_CONN_LOG(debug, "send buffer over limit ", parent_.connection_); - ASSERT(!pending_send_buffer_high_watermark_called_); - pending_send_buffer_high_watermark_called_ = true; - runHighWatermarkCallbacks(); -} - -void ConnectionImpl::StreamImpl::pendingSendBufferLowWatermark() { - ENVOY_CONN_LOG(debug, "send buffer under limit ", parent_.connection_); - ASSERT(pending_send_buffer_high_watermark_called_); - pending_send_buffer_high_watermark_called_ = false; - runLowWatermarkCallbacks(); -} - -void ConnectionImpl::StreamImpl::saveHeader(HeaderString&& name, HeaderString&& value) { - if (!Utility::reconstituteCrumbledCookies(name, value, cookies_)) { - headers().addViaMove(std::move(name), std::move(value)); - } -} - -void ConnectionImpl::StreamImpl::submitTrailers(const HeaderMap& trailers) { - ASSERT(local_end_stream_); - const bool skip_encoding_empty_trailers = - trailers.empty() && parent_.skip_encoding_empty_trailers_; - if (skip_encoding_empty_trailers) { - ENVOY_CONN_LOG(debug, "skipping submitting trailers", parent_.connection_); - - // Instead of submitting empty trailers, we send empty data instead. - Buffer::OwnedImpl empty_buffer; - encodeDataHelper(empty_buffer, /*end_stream=*/true, skip_encoding_empty_trailers); - return; - } - - std::vector final_headers; - buildHeaders(final_headers, trailers); - int rc = nghttp2_submit_trailer(parent_.session_, stream_id_, final_headers.data(), - final_headers.size()); - ASSERT(rc == 0); -} - -void ConnectionImpl::StreamImpl::submitMetadata(uint8_t flags) { - ASSERT(stream_id_ > 0); - const int result = - nghttp2_submit_extension(parent_.session_, METADATA_FRAME_TYPE, flags, stream_id_, nullptr); - ASSERT(result == 0); -} - -ssize_t ConnectionImpl::StreamImpl::onDataSourceRead(uint64_t length, uint32_t* data_flags) { - if (pending_send_data_.length() == 0 && !local_end_stream_) { - ASSERT(!data_deferred_); - data_deferred_ = true; - return NGHTTP2_ERR_DEFERRED; - } else { - *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; - if (local_end_stream_ && pending_send_data_.length() <= length) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - if (pending_trailers_to_encode_) { - // We need to tell the library to not set end stream so that we can emit the trailers. - *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; - submitTrailers(*pending_trailers_to_encode_); - pending_trailers_to_encode_.reset(); - } - } - - return std::min(length, pending_send_data_.length()); - } -} - -int ConnectionImpl::StreamImpl::onDataSourceSend(const uint8_t* framehd, size_t length) { - // In this callback we are writing out a raw DATA frame without copying. nghttp2 assumes that we - // "just know" that the frame header is 9 bytes. - // https://nghttp2.org/documentation/types.html#c.nghttp2_send_data_callback - static const uint64_t FRAME_HEADER_SIZE = 9; - - parent_.protocol_constraints_.incrementOutboundDataFrameCount(); - - Buffer::OwnedImpl output; - if (!parent_.addOutboundFrameFragment(output, framehd, FRAME_HEADER_SIZE)) { - ENVOY_CONN_LOG(debug, "error sending data frame: Too many frames in the outbound queue", - parent_.connection_); - setDetails(Http2ResponseCodeDetails::get().outbound_frame_flood); - return NGHTTP2_ERR_FLOODED; - } - - parent_.stats_.pending_send_bytes_.sub(length); - output.move(pending_send_data_, length); - parent_.connection_.write(output, false); - return 0; -} - -void ConnectionImpl::ClientStreamImpl::submitHeaders(const std::vector& final_headers, - nghttp2_data_provider* provider) { - ASSERT(stream_id_ == -1); - stream_id_ = nghttp2_submit_request(parent_.session_, nullptr, final_headers.data(), - final_headers.size(), provider, base()); - ASSERT(stream_id_ > 0); -} - -void ConnectionImpl::ServerStreamImpl::submitHeaders(const std::vector& final_headers, - nghttp2_data_provider* provider) { - ASSERT(stream_id_ != -1); - int rc = nghttp2_submit_response(parent_.session_, stream_id_, final_headers.data(), - final_headers.size(), provider); - ASSERT(rc == 0); -} - -void ConnectionImpl::ServerStreamImpl::createPendingFlushTimer() { - ASSERT(stream_idle_timer_ == nullptr); - if (stream_idle_timeout_.count() > 0) { - stream_idle_timer_ = - parent_.connection_.dispatcher().createTimer([this] { onPendingFlushTimer(); }); - stream_idle_timer_->enableTimer(stream_idle_timeout_); - } -} - -void ConnectionImpl::StreamImpl::onPendingFlushTimer() { - ENVOY_CONN_LOG(debug, "pending stream flush timeout", parent_.connection_); - stream_idle_timer_.reset(); - parent_.stats_.tx_flush_timeout_.inc(); - ASSERT(local_end_stream_ && !local_end_stream_sent_); - // This will emit a reset frame for this stream and close the stream locally. No reset callbacks - // will be run because higher layers think the stream is already finished. - resetStreamWorker(StreamResetReason::LocalReset); - parent_.sendPendingFrames(); - parent_.checkProtocolConstraintViolation(); -} - -void ConnectionImpl::StreamImpl::encodeData(Buffer::Instance& data, bool end_stream) { - ASSERT(!local_end_stream_); - encodeDataHelper(data, end_stream, /*skip_encoding_empty_trailers=*/false); -} - -void ConnectionImpl::StreamImpl::encodeDataHelper(Buffer::Instance& data, bool end_stream, - bool skip_encoding_empty_trailers) { - if (skip_encoding_empty_trailers) { - ASSERT(data.length() == 0 && end_stream); - } - - local_end_stream_ = end_stream; - parent_.stats_.pending_send_bytes_.add(data.length()); - pending_send_data_.move(data); - if (data_deferred_) { - int rc = nghttp2_session_resume_data(parent_.session_, stream_id_); - ASSERT(rc == 0); - - data_deferred_ = false; - } - - parent_.sendPendingFrames(); - parent_.checkProtocolConstraintViolation(); - - if (local_end_stream_ && pending_send_data_.length() > 0) { - createPendingFlushTimer(); - } -} - -void ConnectionImpl::StreamImpl::resetStream(StreamResetReason reason) { - // Higher layers expect calling resetStream() to immediately raise reset callbacks. - runResetCallbacks(reason); - - // If we submit a reset, nghttp2 will cancel outbound frames that have not yet been sent. - // We want these frames to go out so we defer the reset until we send all of the frames that - // end the local stream. - if (local_end_stream_ && !local_end_stream_sent_) { - parent_.pending_deferred_reset_ = true; - deferred_reset_ = reason; - ENVOY_CONN_LOG(trace, "deferred reset stream", parent_.connection_); - } else { - resetStreamWorker(reason); - } - - // We must still call sendPendingFrames() in both the deferred and not deferred path. This forces - // the cleanup logic to run which will reset the stream in all cases if all data frames could not - // be sent. - parent_.sendPendingFrames(); - parent_.checkProtocolConstraintViolation(); -} - -void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) { - int rc = nghttp2_submit_rst_stream(parent_.session_, NGHTTP2_FLAG_NONE, stream_id_, - reasonToReset(reason)); - ASSERT(rc == 0); -} - -MetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() { - if (metadata_encoder_ == nullptr) { - metadata_encoder_ = std::make_unique(); - } - return *metadata_encoder_; -} - -MetadataDecoder& ConnectionImpl::StreamImpl::getMetadataDecoder() { - if (metadata_decoder_ == nullptr) { - auto cb = [this](MetadataMapPtr&& metadata_map_ptr) { - this->onMetadataDecoded(std::move(metadata_map_ptr)); - }; - metadata_decoder_ = std::make_unique(cb); - } - return *metadata_decoder_; -} - -void ConnectionImpl::StreamImpl::onMetadataDecoded(MetadataMapPtr&& metadata_map_ptr) { - decoder().decodeMetadata(std::move(metadata_map_ptr)); -} - -ConnectionImpl::ConnectionImpl(Network::Connection& connection, CodecStats& stats, - Random::RandomGenerator& random, - const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - const uint32_t max_headers_kb, const uint32_t max_headers_count) - : stats_(stats), connection_(connection), max_headers_kb_(max_headers_kb), - max_headers_count_(max_headers_count), - per_stream_buffer_limit_(http2_options.initial_stream_window_size().value()), - stream_error_on_invalid_http_messaging_( - http2_options.override_stream_error_on_invalid_http_message().value()), - flood_detected_(false), protocol_constraints_(stats, http2_options), - skip_encoding_empty_trailers_(Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.http2_skip_encoding_empty_trailers")), - dispatching_(false), raised_goaway_(false), pending_deferred_reset_(false), random_(random) { - if (http2_options.has_connection_keepalive()) { - keepalive_interval_ = std::chrono::milliseconds( - PROTOBUF_GET_MS_REQUIRED(http2_options.connection_keepalive(), interval)); - keepalive_timeout_ = std::chrono::milliseconds( - PROTOBUF_GET_MS_REQUIRED(http2_options.connection_keepalive(), timeout)); - keepalive_interval_jitter_percent_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT( - http2_options.connection_keepalive(), interval_jitter, 15.0); - - keepalive_send_timer_ = connection.dispatcher().createTimer([this]() { sendKeepalive(); }); - keepalive_timeout_timer_ = - connection.dispatcher().createTimer([this]() { onKeepaliveResponseTimeout(); }); - - // This call schedules the initial interval, with jitter. - onKeepaliveResponse(); - } -} - -ConnectionImpl::~ConnectionImpl() { - for (const auto& stream : active_streams_) { - stream->destroy(); - } - nghttp2_session_del(session_); -} - -void ConnectionImpl::sendKeepalive() { - // Include the current time as the payload to help with debugging. - SystemTime now = connection_.dispatcher().timeSource().systemTime(); - uint64_t ms_since_epoch = - std::chrono::duration_cast(now.time_since_epoch()).count(); - ENVOY_CONN_LOG(trace, "Sending keepalive PING {}", connection_, ms_since_epoch); - - // The last parameter is an opaque 8-byte buffer, so this cast is safe. - int rc = nghttp2_submit_ping(session_, 0 /*flags*/, reinterpret_cast(&ms_since_epoch)); - ASSERT(rc == 0); - sendPendingFrames(); - checkProtocolConstraintViolation(); - - keepalive_timeout_timer_->enableTimer(keepalive_timeout_); -} - -void ConnectionImpl::onKeepaliveResponse() { - // Check the timers for nullptr in case the peer sent an unsolicited PING ACK. - if (keepalive_timeout_timer_ != nullptr) { - keepalive_timeout_timer_->disableTimer(); - } - if (keepalive_send_timer_ != nullptr) { - uint64_t interval_ms = keepalive_interval_.count(); - const uint64_t jitter_percent_mod = keepalive_interval_jitter_percent_ * interval_ms / 100; - if (jitter_percent_mod > 0) { - interval_ms += random_.random() % jitter_percent_mod; - } - keepalive_send_timer_->enableTimer(std::chrono::milliseconds(interval_ms)); - } -} - -void ConnectionImpl::onKeepaliveResponseTimeout() { - ENVOY_CONN_LOG(debug, "Closing connection due to keepalive timeout", connection_); - stats_.keepalive_timeout_.inc(); - connection_.close(Network::ConnectionCloseType::NoFlush); -} - -Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) { - // TODO(#10878): Remove this wrapper when exception removal is complete. innerDispatch may either - // throw an exception or return an error status. The utility wrapper catches exceptions and - // converts them to error statuses. - return Http::Utility::exceptionToStatus( - [&](Buffer::Instance& data) -> Http::Status { return innerDispatch(data); }, data); -} - -Http::Status ConnectionImpl::innerDispatch(Buffer::Instance& data) { - ENVOY_CONN_LOG(trace, "dispatching {} bytes", connection_, data.length()); - // Make sure that dispatching_ is set to false after dispatching, even when - // ConnectionImpl::dispatch returns early or throws an exception (consider removing if there is a - // single return after exception removal (#10878)). - Cleanup cleanup([this]() { dispatching_ = false; }); - for (const Buffer::RawSlice& slice : data.getRawSlices()) { - dispatching_ = true; - ssize_t rc = - nghttp2_session_mem_recv(session_, static_cast(slice.mem_), slice.len_); - if (rc == NGHTTP2_ERR_FLOODED || flood_detected_) { - throw FrameFloodException( - "Flooding was detected in this HTTP/2 session, and it must be closed"); - } - if (rc != static_cast(slice.len_)) { - throw CodecProtocolException(fmt::format("{}", nghttp2_strerror(rc))); - } - - dispatching_ = false; - } - - ENVOY_CONN_LOG(trace, "dispatched {} bytes", connection_, data.length()); - data.drain(data.length()); - - // Decoding incoming frames can generate outbound frames so flush pending. - sendPendingFrames(); - return Http::okStatus(); -} - -ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) { - return static_cast(nghttp2_session_get_stream_user_data(session_, stream_id)); -} - -int ConnectionImpl::onData(int32_t stream_id, const uint8_t* data, size_t len) { - StreamImpl* stream = getStream(stream_id); - // If this results in buffering too much data, the watermark buffer will call - // pendingRecvBufferHighWatermark, resulting in ++read_disable_count_ - stream->pending_recv_data_.add(data, len); - // Update the window to the peer unless some consumer of this stream's data has hit a flow control - // limit and disabled reads on this stream - if (!stream->buffersOverrun()) { - nghttp2_session_consume(session_, stream_id, len); - } else { - stream->unconsumed_bytes_ += len; - } - return 0; -} - -void ConnectionImpl::goAway() { - int rc = nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, - nghttp2_session_get_last_proc_stream_id(session_), - NGHTTP2_NO_ERROR, nullptr, 0); - ASSERT(rc == 0); - - sendPendingFrames(); - checkProtocolConstraintViolation(); -} - -void ConnectionImpl::shutdownNotice() { - int rc = nghttp2_submit_shutdown_notice(session_); - ASSERT(rc == 0); - - sendPendingFrames(); - checkProtocolConstraintViolation(); -} - -int ConnectionImpl::onBeforeFrameReceived(const nghttp2_frame_hd* hd) { - ENVOY_CONN_LOG(trace, "about to recv frame type={}, flags={}", connection_, - static_cast(hd->type), static_cast(hd->flags)); - - // Track all the frames without padding here, since this is the only callback we receive - // for some of them (e.g. CONTINUATION frame, frames sent on closed streams, etc.). - // HEADERS frame is tracked in onBeginHeaders(), DATA frame is tracked in onFrameReceived(). - if (hd->type != NGHTTP2_HEADERS && hd->type != NGHTTP2_DATA) { - if (!trackInboundFrames(hd, 0)) { - return NGHTTP2_ERR_FLOODED; - } - } - - return 0; -} - -ABSL_MUST_USE_RESULT -enum GoAwayErrorCode ngHttp2ErrorCodeToErrorCode(uint32_t code) noexcept { - switch (code) { - case NGHTTP2_NO_ERROR: - return GoAwayErrorCode::NoError; - default: - return GoAwayErrorCode::Other; - } -} - -int ConnectionImpl::onFrameReceived(const nghttp2_frame* frame) { - ENVOY_CONN_LOG(trace, "recv frame type={}", connection_, static_cast(frame->hd.type)); - - // onFrameReceived() is called with a complete HEADERS frame assembled from all the HEADERS - // and CONTINUATION frames, but we track them separately: HEADERS frames in onBeginHeaders() - // and CONTINUATION frames in onBeforeFrameReceived(). - ASSERT(frame->hd.type != NGHTTP2_CONTINUATION); - - if ((frame->hd.type == NGHTTP2_PING) && (frame->ping.hd.flags & NGHTTP2_FLAG_ACK)) { - // The ``opaque_data`` should be exactly what was sent in the ping, which is - // was the current time when the ping was sent. This can be useful while debugging - // to match the ping and ack. - uint64_t data; - static_assert(sizeof(data) == sizeof(frame->ping.opaque_data), "Sizes are equal"); - memcpy(&data, frame->ping.opaque_data, sizeof(data)); - ENVOY_CONN_LOG(trace, "recv PING ACK {}", connection_, data); - - onKeepaliveResponse(); - return 0; - } - - if (frame->hd.type == NGHTTP2_DATA) { - if (!trackInboundFrames(&frame->hd, frame->data.padlen)) { - return NGHTTP2_ERR_FLOODED; - } - } - - // Only raise GOAWAY once, since we don't currently expose stream information. Shutdown - // notifications are the same as a normal GOAWAY. - // TODO: handle multiple GOAWAY frames. - if (frame->hd.type == NGHTTP2_GOAWAY && !raised_goaway_) { - ASSERT(frame->hd.stream_id == 0); - raised_goaway_ = true; - callbacks().onGoAway(ngHttp2ErrorCodeToErrorCode(frame->goaway.error_code)); - return 0; - } - - if (frame->hd.type == NGHTTP2_SETTINGS && frame->hd.flags == NGHTTP2_FLAG_NONE) { - onSettingsForTest(frame->settings); - } - - StreamImpl* stream = getStream(frame->hd.stream_id); - if (!stream) { - return 0; - } - - switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - stream->remote_end_stream_ = frame->hd.flags & NGHTTP2_FLAG_END_STREAM; - if (!stream->cookies_.empty()) { - HeaderString key(Headers::get().Cookie); - stream->headers().addViaMove(std::move(key), std::move(stream->cookies_)); - } - - switch (frame->headers.cat) { - case NGHTTP2_HCAT_RESPONSE: - case NGHTTP2_HCAT_REQUEST: { - stream->decodeHeaders(); - break; - } - - case NGHTTP2_HCAT_HEADERS: { - // It's possible that we are waiting to send a deferred reset, so only raise headers/trailers - // if local is not complete. - if (!stream->deferred_reset_) { - if (nghttp2_session_check_server_session(session_) || - stream->received_noninformational_headers_) { - ASSERT(stream->remote_end_stream_); - stream->decodeTrailers(); - } else { - // We're a client session and still waiting for non-informational headers. - stream->decodeHeaders(); - } - } - break; - } - - default: - // We do not currently support push. - NOT_IMPLEMENTED_GCOVR_EXCL_LINE; - } - - break; - } - case NGHTTP2_DATA: { - stream->remote_end_stream_ = frame->hd.flags & NGHTTP2_FLAG_END_STREAM; - - // It's possible that we are waiting to send a deferred reset, so only raise data if local - // is not complete. - if (!stream->deferred_reset_) { - stream->decoder().decodeData(stream->pending_recv_data_, stream->remote_end_stream_); - } - - stream->pending_recv_data_.drain(stream->pending_recv_data_.length()); - break; - } - case NGHTTP2_RST_STREAM: { - ENVOY_CONN_LOG(trace, "remote reset: {}", connection_, frame->rst_stream.error_code); - stats_.rx_reset_.inc(); - break; - } - } - - return 0; -} - -int ConnectionImpl::onFrameSend(const nghttp2_frame* frame) { - // The nghttp2 library does not cleanly give us a way to determine whether we received invalid - // data from our peer. Sometimes it raises the invalid frame callback, and sometimes it does not. - // In all cases however it will attempt to send a GOAWAY frame with an error status. If we see - // an outgoing frame of this type, we will return an error code so that we can abort execution. - ENVOY_CONN_LOG(trace, "sent frame type={}", connection_, static_cast(frame->hd.type)); - switch (frame->hd.type) { - case NGHTTP2_GOAWAY: { - ENVOY_CONN_LOG(debug, "sent goaway code={}", connection_, frame->goaway.error_code); - if (frame->goaway.error_code != NGHTTP2_NO_ERROR) { - // TODO(mattklein123): Returning this error code abandons standard nghttp2 frame accounting. - // As such, it is not reliable to call sendPendingFrames() again after this and we assume - // that the connection is going to get torn down immediately. One byproduct of this is that - // we need to cancel all pending flush stream timeouts since they can race with connection - // teardown. As part of the work to remove exceptions we should aim to clean up all of this - // error handling logic and only handle this type of case at the end of dispatch. - for (auto& stream : active_streams_) { - stream->disarmStreamIdleTimer(); - } - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - break; - } - - case NGHTTP2_RST_STREAM: { - ENVOY_CONN_LOG(debug, "sent reset code={}", connection_, frame->rst_stream.error_code); - stats_.tx_reset_.inc(); - break; - } - - case NGHTTP2_HEADERS: - case NGHTTP2_DATA: { - StreamImpl* stream = getStream(frame->hd.stream_id); - stream->local_end_stream_sent_ = frame->hd.flags & NGHTTP2_FLAG_END_STREAM; - break; - } - } - - return 0; -} - -int ConnectionImpl::onError(absl::string_view error) { - ENVOY_CONN_LOG(debug, "invalid http2: {}", connection_, error); - return 0; -} - -int ConnectionImpl::onInvalidFrame(int32_t stream_id, int error_code) { - ENVOY_CONN_LOG(debug, "invalid frame: {} on stream {}", connection_, nghttp2_strerror(error_code), - stream_id); - - // Set details of error_code in the stream whenever we have one. - StreamImpl* stream = getStream(stream_id); - if (stream != nullptr) { - stream->setDetails(Http2ResponseCodeDetails::get().errorDetails(error_code)); - } - - if (error_code == NGHTTP2_ERR_HTTP_HEADER || error_code == NGHTTP2_ERR_HTTP_MESSAGING) { - stats_.rx_messaging_error_.inc(); - - if (stream_error_on_invalid_http_messaging_) { - // The stream is about to be closed due to an invalid header or messaging. Don't kill the - // entire connection if one stream has bad headers or messaging. - if (stream != nullptr) { - // See comment below in onStreamClose() for why we do this. - stream->reset_due_to_messaging_error_ = true; - } - return 0; - } - } - - // Cause dispatch to return with an error code. - return NGHTTP2_ERR_CALLBACK_FAILURE; -} - -int ConnectionImpl::onBeforeFrameSend(const nghttp2_frame* frame) { - ENVOY_CONN_LOG(trace, "about to send frame type={}, flags={}", connection_, - static_cast(frame->hd.type), static_cast(frame->hd.flags)); - ASSERT(!is_outbound_flood_monitored_control_frame_); - // Flag flood monitored outbound control frames. - is_outbound_flood_monitored_control_frame_ = - ((frame->hd.type == NGHTTP2_PING || frame->hd.type == NGHTTP2_SETTINGS) && - frame->hd.flags & NGHTTP2_FLAG_ACK) || - frame->hd.type == NGHTTP2_RST_STREAM; - return 0; -} - -bool ConnectionImpl::addOutboundFrameFragment(Buffer::OwnedImpl& output, const uint8_t* data, - size_t length) { - // Reset the outbound frame type (set in the onBeforeFrameSend callback) since the - // onBeforeFrameSend callback is not called for DATA frames. - bool is_outbound_flood_monitored_control_frame = false; - std::swap(is_outbound_flood_monitored_control_frame, is_outbound_flood_monitored_control_frame_); - try { - auto releasor = trackOutboundFrames(is_outbound_flood_monitored_control_frame); - output.add(data, length); - output.addDrainTracker(releasor); - } catch (const FrameFloodException&) { - return false; - } - return true; -} - -ssize_t ConnectionImpl::onSend(const uint8_t* data, size_t length) { - ENVOY_CONN_LOG(trace, "send data: bytes={}", connection_, length); - Buffer::OwnedImpl buffer; - if (!addOutboundFrameFragment(buffer, data, length)) { - ENVOY_CONN_LOG(debug, "error sending frame: Too many frames in the outbound queue.", - connection_); - return NGHTTP2_ERR_FLOODED; - } - - // While the buffer is transient the fragment it contains will be moved into the - // write_buffer_ of the underlying connection_ by the write method below. - // This creates lifetime dependency between the write_buffer_ of the underlying connection - // and the codec object. Specifically the write_buffer_ MUST be either fully drained or - // deleted before the codec object is deleted. This is presently guaranteed by the - // destruction order of the Network::ConnectionImpl object where write_buffer_ is - // destroyed before the filter_manager_ which owns the codec through Http::ConnectionManagerImpl. - connection_.write(buffer, false); - return length; -} - -int ConnectionImpl::onStreamClose(int32_t stream_id, uint32_t error_code) { - StreamImpl* stream = getStream(stream_id); - if (stream) { - ENVOY_CONN_LOG(debug, "stream closed: {}", connection_, error_code); - if (!stream->remote_end_stream_ || !stream->local_end_stream_) { - StreamResetReason reason; - if (stream->reset_due_to_messaging_error_) { - // Unfortunately, the nghttp2 API makes it incredibly difficult to clearly understand - // the flow of resets. I.e., did the reset originate locally? Was it remote? Here, - // we attempt to track cases in which we sent a reset locally due to an invalid frame - // received from the remote. We only do that in two cases currently (HTTP messaging layer - // errors from https://tools.ietf.org/html/rfc7540#section-8 which nghttp2 is very strict - // about). In other cases we treat invalid frames as a protocol error and just kill - // the connection. - reason = StreamResetReason::LocalReset; - } else { - if (error_code == NGHTTP2_REFUSED_STREAM) { - reason = StreamResetReason::RemoteRefusedStreamReset; - stream->setDetails(Http2ResponseCodeDetails::get().remote_refused); - } else if (error_code == NGHTTP2_CONNECT_ERROR) { - reason = StreamResetReason::ConnectError; - stream->setDetails(Http2ResponseCodeDetails::get().remote_reset); - } else { - reason = StreamResetReason::RemoteReset; - stream->setDetails(Http2ResponseCodeDetails::get().remote_reset); - } - } - - stream->runResetCallbacks(reason); - } - - stream->destroy(); - connection_.dispatcher().deferredDelete(stream->removeFromList(active_streams_)); - // Any unconsumed data must be consumed before the stream is deleted. - // nghttp2 does not appear to track this internally, and any stream deleted - // with outstanding window will contribute to a slow connection-window leak. - nghttp2_session_consume(session_, stream_id, stream->unconsumed_bytes_); - stream->unconsumed_bytes_ = 0; - nghttp2_session_set_stream_user_data(session_, stream->stream_id_, nullptr); - } - - return 0; -} - -int ConnectionImpl::onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len) { - ENVOY_CONN_LOG(trace, "recv {} bytes METADATA", connection_, len); - - StreamImpl* stream = getStream(stream_id); - if (!stream || stream->remote_end_stream_) { - return 0; - } - - bool success = stream->getMetadataDecoder().receiveMetadata(data, len); - return success ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; -} - -int ConnectionImpl::onMetadataFrameComplete(int32_t stream_id, bool end_metadata) { - ENVOY_CONN_LOG(trace, "recv METADATA frame on stream {}, end_metadata: {}", connection_, - stream_id, end_metadata); - - StreamImpl* stream = getStream(stream_id); - if (!stream || stream->remote_end_stream_) { - return 0; - } - - bool result = stream->getMetadataDecoder().onMetadataFrameComplete(end_metadata); - return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; -} - -ssize_t ConnectionImpl::packMetadata(int32_t stream_id, uint8_t* buf, size_t len) { - ENVOY_CONN_LOG(trace, "pack METADATA frame on stream {}", connection_, stream_id); - - StreamImpl* stream = getStream(stream_id); - if (stream == nullptr) { - return 0; - } - - MetadataEncoder& encoder = stream->getMetadataEncoder(); - return encoder.packNextFramePayload(buf, len); -} - -int ConnectionImpl::saveHeader(const nghttp2_frame* frame, HeaderString&& name, - HeaderString&& value) { - StreamImpl* stream = getStream(frame->hd.stream_id); - if (!stream) { - // We have seen 1 or 2 crashes where we get a headers callback but there is no associated - // stream data. I honestly am not sure how this can happen. However, from reading the nghttp2 - // code it looks possible that inflate_header_block() can safely inflate headers for an already - // closed stream, but will still call the headers callback. Since that seems possible, we should - // ignore this case here. - // TODO(mattklein123): Figure out a test case that can hit this. - stats_.headers_cb_no_stream_.inc(); - return 0; - } - - auto should_return = checkHeaderNameForUnderscores(name.getStringView()); - if (should_return) { - stream->setDetails(Http2ResponseCodeDetails::get().invalid_underscore); - name.clear(); - value.clear(); - return should_return.value(); - } - - stream->saveHeader(std::move(name), std::move(value)); - - if (stream->headers().byteSize() > max_headers_kb_ * 1024 || - stream->headers().size() > max_headers_count_) { - stream->setDetails(Http2ResponseCodeDetails::get().too_many_headers); - stats_.header_overflow_.inc(); - // This will cause the library to reset/close the stream. - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } else { - return 0; - } -} - -void ConnectionImpl::sendPendingFrames() { - if (dispatching_ || connection_.state() == Network::Connection::State::Closed) { - return; - } - - const int rc = nghttp2_session_send(session_); - if (rc != 0) { - ASSERT(rc == NGHTTP2_ERR_CALLBACK_FAILURE); - // For errors caused by the pending outbound frame flood the FrameFloodException has - // to be thrown. However the nghttp2 library returns only the generic error code for - // all failure types. Check queue limits and throw FrameFloodException if they were - // exceeded. - if (!protocol_constraints_.status().ok()) { - throw FrameFloodException("Too many frames in the outbound queue."); - } - - throw CodecProtocolException(std::string(nghttp2_strerror(rc))); - } - - // See ConnectionImpl::StreamImpl::resetStream() for why we do this. This is an uncommon event, - // so iterating through every stream to find the ones that have a deferred reset is not a big - // deal. Furthermore, queueing a reset frame does not actually invoke the close stream callback. - // This is only done when the reset frame is sent. Thus, it's safe to work directly with the - // stream map. - // NOTE: The way we handle deferred reset is essentially best effort. If we intend to do a - // deferred reset, we try to finish the stream, including writing any pending data frames. - // If we cannot do this (potentially due to not enough window), we just reset the stream. - // In general this behavior occurs only when we are trying to send immediate error messages - // to short circuit requests. In the best effort case, we complete the stream before - // resetting. In other cases, we just do the reset now which will blow away pending data - // frames and release any memory associated with the stream. - if (pending_deferred_reset_) { - pending_deferred_reset_ = false; - for (auto& stream : active_streams_) { - if (stream->deferred_reset_) { - stream->resetStreamWorker(stream->deferred_reset_.value()); - } - } - sendPendingFrames(); - } -} - -void ConnectionImpl::sendSettings( - const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) { - absl::InlinedVector settings; - auto insertParameter = [&settings](const nghttp2_settings_entry& entry) mutable -> bool { - const auto it = std::find_if(settings.cbegin(), settings.cend(), - [&entry](const nghttp2_settings_entry& existing) { - return entry.settings_id == existing.settings_id; - }); - if (it != settings.end()) { - return false; - } - settings.push_back(entry); - return true; - }; - - // Universally disable receiving push promise frames as we don't currently support - // them. nghttp2 will fail the connection if the other side still sends them. - // TODO(mattklein123): Remove this when we correctly proxy push promise. - // NOTE: This is a special case with respect to custom parameter overrides in that server push is - // not supported and therefore not end user configurable. - if (disable_push) { - settings.push_back( - {static_cast(NGHTTP2_SETTINGS_ENABLE_PUSH), disable_push ? 0U : 1U}); - } - - for (const auto& it : http2_options.custom_settings_parameters()) { - ASSERT(it.identifier().value() <= std::numeric_limits::max()); - const bool result = - insertParameter({static_cast(it.identifier().value()), it.value().value()}); - ASSERT(result); - ENVOY_CONN_LOG(debug, "adding custom settings parameter with id {:#x} to {}", connection_, - it.identifier().value(), it.value().value()); - } - - // Insert named parameters. - settings.insert( - settings.end(), - {{NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, http2_options.hpack_table_size().value()}, - {NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL, http2_options.allow_connect()}, - {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, http2_options.max_concurrent_streams().value()}, - {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, http2_options.initial_stream_window_size().value()}}); - if (!settings.empty()) { - int rc = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, settings.data(), settings.size()); - ASSERT(rc == 0); - } else { - // nghttp2_submit_settings need to be called at least once - int rc = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, nullptr, 0); - ASSERT(rc == 0); - } - - const uint32_t initial_connection_window_size = - http2_options.initial_connection_window_size().value(); - // Increase connection window size up to our default size. - if (initial_connection_window_size != NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE) { - ENVOY_CONN_LOG(debug, "updating connection-level initial window size to {}", connection_, - initial_connection_window_size); - int rc = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, - initial_connection_window_size - - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE); - ASSERT(rc == 0); - } -} - -void ConnectionImpl::scheduleProtocolConstraintViolationCallback() { - if (!protocol_constraint_violation_callback_) { - protocol_constraint_violation_callback_ = connection_.dispatcher().createSchedulableCallback( - [this]() { onProtocolConstraintViolation(); }); - protocol_constraint_violation_callback_->scheduleCallbackCurrentIteration(); - } -} - -void ConnectionImpl::onProtocolConstraintViolation() { - // Flooded outbound queue implies that peer is not reading and it does not - // make sense to try to flush pending bytes. - connection_.close(Envoy::Network::ConnectionCloseType::NoFlush); -} - -ConnectionImpl::Http2Callbacks::Http2Callbacks() { - nghttp2_session_callbacks_new(&callbacks_); - nghttp2_session_callbacks_set_send_callback( - callbacks_, - [](nghttp2_session*, const uint8_t* data, size_t length, int, void* user_data) -> ssize_t { - return static_cast(user_data)->onSend(data, length); - }); - - nghttp2_session_callbacks_set_send_data_callback( - callbacks_, - [](nghttp2_session*, nghttp2_frame* frame, const uint8_t* framehd, size_t length, - nghttp2_data_source* source, void*) -> int { - ASSERT(frame->data.padlen == 0); - return static_cast(source->ptr)->onDataSourceSend(framehd, length); - }); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int { - return static_cast(user_data)->onBeginHeaders(frame); - }); - - nghttp2_session_callbacks_set_on_header_callback( - callbacks_, - [](nghttp2_session*, const nghttp2_frame* frame, const uint8_t* raw_name, size_t name_length, - const uint8_t* raw_value, size_t value_length, uint8_t, void* user_data) -> int { - // TODO PERF: Can reference count here to avoid copies. - HeaderString name; - name.setCopy(reinterpret_cast(raw_name), name_length); - HeaderString value; - value.setCopy(reinterpret_cast(raw_value), value_length); - return static_cast(user_data)->onHeader(frame, std::move(name), - std::move(value)); - }); - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks_, - [](nghttp2_session*, uint8_t, int32_t stream_id, const uint8_t* data, size_t len, - void* user_data) -> int { - return static_cast(user_data)->onData(stream_id, data, len); - }); - - nghttp2_session_callbacks_set_on_begin_frame_callback( - callbacks_, [](nghttp2_session*, const nghttp2_frame_hd* hd, void* user_data) -> int { - return static_cast(user_data)->onBeforeFrameReceived(hd); - }); - - nghttp2_session_callbacks_set_on_frame_recv_callback( - callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int { - return static_cast(user_data)->onFrameReceived(frame); - }); - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks_, - [](nghttp2_session*, int32_t stream_id, uint32_t error_code, void* user_data) -> int { - return static_cast(user_data)->onStreamClose(stream_id, error_code); - }); - - nghttp2_session_callbacks_set_on_frame_send_callback( - callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int { - return static_cast(user_data)->onFrameSend(frame); - }); - - nghttp2_session_callbacks_set_before_frame_send_callback( - callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int { - return static_cast(user_data)->onBeforeFrameSend(frame); - }); - - nghttp2_session_callbacks_set_on_frame_not_send_callback( - callbacks_, [](nghttp2_session*, const nghttp2_frame*, int, void*) -> int { - // We used to always return failure here but it looks now this can get called if the other - // side sends GOAWAY and we are trying to send a SETTINGS ACK. Just ignore this for now. - return 0; - }); - - nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( - callbacks_, - [](nghttp2_session*, const nghttp2_frame* frame, int error_code, void* user_data) -> int { - return static_cast(user_data)->onInvalidFrame(frame->hd.stream_id, - error_code); - }); - - nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( - callbacks_, - [](nghttp2_session*, const nghttp2_frame_hd* hd, const uint8_t* data, size_t len, - void* user_data) -> int { - ASSERT(hd->length >= len); - return static_cast(user_data)->onMetadataReceived(hd->stream_id, data, - len); - }); - - nghttp2_session_callbacks_set_unpack_extension_callback( - callbacks_, [](nghttp2_session*, void**, const nghttp2_frame_hd* hd, void* user_data) -> int { - return static_cast(user_data)->onMetadataFrameComplete( - hd->stream_id, hd->flags == END_METADATA_FLAG); - }); - - nghttp2_session_callbacks_set_pack_extension_callback( - callbacks_, - [](nghttp2_session*, uint8_t* buf, size_t len, const nghttp2_frame* frame, - void* user_data) -> ssize_t { - ASSERT(frame->hd.length <= len); - return static_cast(user_data)->packMetadata(frame->hd.stream_id, buf, len); - }); - - nghttp2_session_callbacks_set_error_callback2( - callbacks_, [](nghttp2_session*, int, const char* msg, size_t len, void* user_data) -> int { - return static_cast(user_data)->onError(absl::string_view(msg, len)); - }); -} - -ConnectionImpl::Http2Callbacks::~Http2Callbacks() { nghttp2_session_callbacks_del(callbacks_); } - -ConnectionImpl::Http2Options::Http2Options( - const envoy::config::core::v3::Http2ProtocolOptions& http2_options) { - nghttp2_option_new(&options_); - // Currently we do not do anything with stream priority. Setting the following option prevents - // nghttp2 from keeping around closed streams for use during stream priority dependency graph - // calculations. This saves a tremendous amount of memory in cases where there are a large - // number of kept alive HTTP/2 connections. - nghttp2_option_set_no_closed_streams(options_, 1); - nghttp2_option_set_no_auto_window_update(options_, 1); - - // The max send header block length is configured to an arbitrarily high number so as to never - // trigger the check within nghttp2, as we check request headers length in - // codec_impl::saveHeader. - nghttp2_option_set_max_send_header_block_length(options_, 0x2000000); - - if (http2_options.hpack_table_size().value() != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { - nghttp2_option_set_max_deflate_dynamic_table_size(options_, - http2_options.hpack_table_size().value()); - } - - if (http2_options.allow_metadata()) { - nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE); - } else { - ENVOY_LOG(trace, "Codec does not have Metadata frame support."); - } - - // nghttp2 v1.39.2 lowered the internal flood protection limit from 10K to 1K of ACK frames. - // This new limit may cause the internal nghttp2 mitigation to trigger more often (as it - // requires just 9K of incoming bytes for smallest 9 byte SETTINGS frame), bypassing the same - // mitigation and its associated behavior in the envoy HTTP/2 codec. Since envoy does not rely - // on this mitigation, set back to the old 10K number to avoid any changes in the HTTP/2 codec - // behavior. - nghttp2_option_set_max_outbound_ack(options_, 10000); -} - -ConnectionImpl::Http2Options::~Http2Options() { nghttp2_option_del(options_); } - -ConnectionImpl::ClientHttp2Options::ClientHttp2Options( - const envoy::config::core::v3::Http2ProtocolOptions& http2_options) - : Http2Options(http2_options) { - // Temporarily disable initial max streams limit/protection, since we might want to create - // more than 100 streams before receiving the HTTP/2 SETTINGS frame from the server. - // - // TODO(PiotrSikora): remove this once multiple upstream connections or queuing are implemented. - nghttp2_option_set_peer_max_concurrent_streams( - options_, ::Envoy::Http2::Utility::OptionsLimits::DEFAULT_MAX_CONCURRENT_STREAMS); -} - -ClientConnectionImpl::ClientConnectionImpl( - Network::Connection& connection, Http::ConnectionCallbacks& callbacks, CodecStats& stats, - Random::RandomGenerator& random, - const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - const uint32_t max_response_headers_kb, const uint32_t max_response_headers_count, - Nghttp2SessionFactory& http2_session_factory) - : ConnectionImpl(connection, stats, random, http2_options, max_response_headers_kb, - max_response_headers_count), - callbacks_(callbacks) { - ClientHttp2Options client_http2_options(http2_options); - session_ = http2_session_factory.create(http2_callbacks_.callbacks(), base(), - client_http2_options.options()); - http2_session_factory.init(session_, base(), http2_options); - allow_metadata_ = http2_options.allow_metadata(); -} - -RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& decoder) { - ClientStreamImplPtr stream(new ClientStreamImpl(*this, per_stream_buffer_limit_, decoder)); - // If the connection is currently above the high watermark, make sure to inform the new stream. - // The connection can not pass this on automatically as it has no awareness that a new stream is - // created. - if (connection_.aboveHighWatermark()) { - stream->runHighWatermarkCallbacks(); - } - ClientStreamImpl& stream_ref = *stream; - LinkedList::moveIntoList(std::move(stream), active_streams_); - return stream_ref; -} - -int ClientConnectionImpl::onBeginHeaders(const nghttp2_frame* frame) { - // The client code explicitly does not currently support push promise. - RELEASE_ASSERT(frame->hd.type == NGHTTP2_HEADERS, ""); - RELEASE_ASSERT(frame->headers.cat == NGHTTP2_HCAT_RESPONSE || - frame->headers.cat == NGHTTP2_HCAT_HEADERS, - ""); - if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { - StreamImpl* stream = getStream(frame->hd.stream_id); - stream->allocTrailers(); - } - - return 0; -} - -int ClientConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& name, - HeaderString&& value) { - // The client code explicitly does not currently support push promise. - ASSERT(frame->hd.type == NGHTTP2_HEADERS); - ASSERT(frame->headers.cat == NGHTTP2_HCAT_RESPONSE || frame->headers.cat == NGHTTP2_HCAT_HEADERS); - return saveHeader(frame, std::move(name), std::move(value)); -} - -ServerConnectionImpl::ServerConnectionImpl( - Network::Connection& connection, Http::ServerConnectionCallbacks& callbacks, CodecStats& stats, - Random::RandomGenerator& random, - const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - const uint32_t max_request_headers_kb, const uint32_t max_request_headers_count, - envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction - headers_with_underscores_action) - : ConnectionImpl(connection, stats, random, http2_options, max_request_headers_kb, - max_request_headers_count), - callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action) { - Http2Options h2_options(http2_options); - - nghttp2_session_server_new2(&session_, http2_callbacks_.callbacks(), base(), - h2_options.options()); - sendSettings(http2_options, false); - allow_metadata_ = http2_options.allow_metadata(); -} - -int ServerConnectionImpl::onBeginHeaders(const nghttp2_frame* frame) { - // For a server connection, we should never get push promise frames. - ASSERT(frame->hd.type == NGHTTP2_HEADERS); - - if (!trackInboundFrames(&frame->hd, frame->headers.padlen)) { - return NGHTTP2_ERR_FLOODED; - } - - if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - stats_.trailers_.inc(); - ASSERT(frame->headers.cat == NGHTTP2_HCAT_HEADERS); - - StreamImpl* stream = getStream(frame->hd.stream_id); - stream->allocTrailers(); - return 0; - } - - ServerStreamImplPtr stream(new ServerStreamImpl(*this, per_stream_buffer_limit_)); - if (connection_.aboveHighWatermark()) { - stream->runHighWatermarkCallbacks(); - } - stream->request_decoder_ = &callbacks_.newStream(*stream); - stream->stream_id_ = frame->hd.stream_id; - LinkedList::moveIntoList(std::move(stream), active_streams_); - nghttp2_session_set_stream_user_data(session_, frame->hd.stream_id, - active_streams_.front().get()); - return 0; -} - -int ServerConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& name, - HeaderString&& value) { - // For a server connection, we should never get push promise frames. - ASSERT(frame->hd.type == NGHTTP2_HEADERS); - ASSERT(frame->headers.cat == NGHTTP2_HCAT_REQUEST || frame->headers.cat == NGHTTP2_HCAT_HEADERS); - return saveHeader(frame, std::move(name), std::move(value)); -} - -bool ServerConnectionImpl::trackInboundFrames(const nghttp2_frame_hd* hd, uint32_t padding_length) { - ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}", - connection_, static_cast(hd->type), static_cast(hd->flags), - static_cast(hd->length), padding_length); - auto result = protocol_constraints_.trackInboundFrames(hd, padding_length); - if (!result.ok()) { - ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_, - result.message()); - if (isInboundFramesWithEmptyPayloadError(result)) { - ConnectionImpl::StreamImpl* stream = getStream(hd->stream_id); - if (stream) { - stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood); - } - } - // NGHTTP2_ERR_FLOODED is overridden within nghttp2 library and it doesn't propagate - // all the way to nghttp2_session_mem_recv() where we need it. - flood_detected_ = true; - return false; - } - - return true; -} - -Envoy::Http::Http2::ProtocolConstraints::ReleasorProc -ServerConnectionImpl::trackOutboundFrames(bool is_outbound_flood_monitored_control_frame) { - auto releasor = - protocol_constraints_.incrementOutboundFrameCount(is_outbound_flood_monitored_control_frame); - if (dispatching_downstream_data_ && !protocol_constraints_.checkOutboundFrameLimits().ok()) { - throw FrameFloodException(std::string(protocol_constraints_.status().message())); - } - return releasor; -} - -void ServerConnectionImpl::checkProtocolConstraintViolation() { - if (!protocol_constraints_.checkOutboundFrameLimits().ok()) { - scheduleProtocolConstraintViolationCallback(); - } -} - -Http::Status ServerConnectionImpl::dispatch(Buffer::Instance& data) { - // TODO(#10878): Remove this wrapper when exception removal is complete. innerDispatch may either - // throw an exception or return an error status. The utility wrapper catches exceptions and - // converts them to error statuses. - return Http::Utility::exceptionToStatus( - [&](Buffer::Instance& data) -> Http::Status { return innerDispatch(data); }, data); -} - -Http::Status ServerConnectionImpl::innerDispatch(Buffer::Instance& data) { - ASSERT(!dispatching_downstream_data_); - dispatching_downstream_data_ = true; - - // Make sure the dispatching_downstream_data_ is set to false even - // when ConnectionImpl::dispatch throws an exception. - Cleanup cleanup([this]() { dispatching_downstream_data_ = false; }); - - // Make sure downstream outbound queue was not flooded by the upstream frames. - if (!protocol_constraints_.checkOutboundFrameLimits().ok()) { - throw FrameFloodException(std::string(protocol_constraints_.status().message())); - } - - return ConnectionImpl::innerDispatch(data); -} - -absl::optional -ServerConnectionImpl::checkHeaderNameForUnderscores(absl::string_view header_name) { - if (headers_with_underscores_action_ != envoy::config::core::v3::HttpProtocolOptions::ALLOW && - Http::HeaderUtility::headerNameContainsUnderscore(header_name)) { - if (headers_with_underscores_action_ == - envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER) { - ENVOY_CONN_LOG(debug, "Dropping header with invalid characters in its name: {}", connection_, - header_name); - stats_.dropped_headers_with_underscores_.inc(); - return 0; - } - ENVOY_CONN_LOG(debug, "Rejecting request due to header name with underscores: {}", connection_, - header_name); - stats_.requests_rejected_with_underscores_in_headers_.inc(); - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - return absl::nullopt; -} - -} // namespace Http2 -} // namespace Legacy -} // namespace Http -} // namespace Envoy diff --git a/source/common/http/http2/codec_impl_legacy.h b/source/common/http/http2/codec_impl_legacy.h deleted file mode 100644 index 766724a36d53..000000000000 --- a/source/common/http/http2/codec_impl_legacy.h +++ /dev/null @@ -1,623 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "envoy/common/random_generator.h" -#include "envoy/config/core/v3/protocol.pb.h" -#include "envoy/event/deferred_deletable.h" -#include "envoy/http/codec.h" -#include "envoy/network/connection.h" - -#include "common/buffer/buffer_impl.h" -#include "common/buffer/watermark_buffer.h" -#include "common/common/linked_object.h" -#include "common/common/logger.h" -#include "common/common/thread.h" -#include "common/http/codec_helper.h" -#include "common/http/header_map_impl.h" -#include "common/http/http2/codec_stats.h" -#include "common/http/http2/metadata_decoder.h" -#include "common/http/http2/metadata_encoder.h" -#include "common/http/http2/protocol_constraints.h" -#include "common/http/status.h" -#include "common/http/utility.h" - -#include "absl/types/optional.h" -#include "nghttp2/nghttp2.h" - -namespace Envoy { -namespace Http { -namespace Legacy { -namespace Http2 { - -// This is not the full client magic, but it's the smallest size that should be able to -// differentiate between HTTP/1 and HTTP/2. -const std::string CLIENT_MAGIC_PREFIX = "PRI * HTTP/2"; - -class Utility { -public: - /** - * Deal with https://tools.ietf.org/html/rfc7540#section-8.1.2.5 - * @param key supplies the incoming header key. - * @param value supplies the incoming header value. - * @param cookies supplies the header string to fill if this is a cookie header that needs to be - * rebuilt. - */ - static bool reconstituteCrumbledCookies(const HeaderString& key, const HeaderString& value, - HeaderString& cookies); -}; - -class ConnectionImpl; - -// Abstract nghttp2_session factory. Used to enable injection of factories for testing. -class Nghttp2SessionFactory { -public: - using ConnectionImplType = ConnectionImpl; - virtual ~Nghttp2SessionFactory() = default; - - // Returns a new nghttp2_session to be used with |connection|. - virtual nghttp2_session* create(const nghttp2_session_callbacks* callbacks, - ConnectionImplType* connection, - const nghttp2_option* options) PURE; - - // Initializes the |session|. - virtual void init(nghttp2_session* session, ConnectionImplType* connection, - const envoy::config::core::v3::Http2ProtocolOptions& options) PURE; -}; - -class ProdNghttp2SessionFactory : public Nghttp2SessionFactory { -public: - nghttp2_session* create(const nghttp2_session_callbacks* callbacks, ConnectionImpl* connection, - const nghttp2_option* options) override; - - void init(nghttp2_session* session, ConnectionImpl* connection, - const envoy::config::core::v3::Http2ProtocolOptions& options) override; - - // Returns a global factory instance. Note that this is possible because no internal state is - // maintained; the thread safety of create() and init()'s side effects is guaranteed by Envoy's - // worker based threading model. - static ProdNghttp2SessionFactory& get() { - static ProdNghttp2SessionFactory* instance = new ProdNghttp2SessionFactory(); - return *instance; - } -}; - -/** - * Base class for HTTP/2 client and server codecs. - */ -class ConnectionImpl : public virtual Connection, protected Logger::Loggable { -public: - ConnectionImpl(Network::Connection& connection, Http::Http2::CodecStats& stats, - Random::RandomGenerator& random, - const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - const uint32_t max_headers_kb, const uint32_t max_headers_count); - - ~ConnectionImpl() override; - - // Http::Connection - // NOTE: the `dispatch` method is also overridden in the ServerConnectionImpl class - Http::Status dispatch(Buffer::Instance& data) override; - void goAway() override; - Protocol protocol() override { return Protocol::Http2; } - void shutdownNotice() override; - bool wantsToWrite() override { return nghttp2_session_want_write(session_); } - // Propagate network connection watermark events to each stream on the connection. - void onUnderlyingConnectionAboveWriteBufferHighWatermark() override { - for (auto& stream : active_streams_) { - stream->runHighWatermarkCallbacks(); - } - } - void onUnderlyingConnectionBelowWriteBufferLowWatermark() override { - for (auto& stream : active_streams_) { - stream->runLowWatermarkCallbacks(); - } - } - - /** - * An inner dispatch call that executes the dispatching logic. While exception removal is in - * migration (#10878), this function may either throw an exception or return an error status. - * Exceptions are caught and translated to their corresponding statuses in the outer level - * dispatch. - * This needs to be virtual so that ServerConnectionImpl can override. - * TODO(#10878): Remove this when exception removal is complete. - */ - virtual Http::Status innerDispatch(Buffer::Instance& data); - -protected: - friend class ProdNghttp2SessionFactory; - - /** - * Wrapper for static nghttp2 callback dispatchers. - */ - class Http2Callbacks { - public: - Http2Callbacks(); - ~Http2Callbacks(); - - const nghttp2_session_callbacks* callbacks() { return callbacks_; } - - private: - nghttp2_session_callbacks* callbacks_; - }; - - /** - * Wrapper for static nghttp2 session options. - */ - class Http2Options { - public: - Http2Options(const envoy::config::core::v3::Http2ProtocolOptions& http2_options); - ~Http2Options(); - - const nghttp2_option* options() { return options_; } - - protected: - nghttp2_option* options_; - }; - - class ClientHttp2Options : public Http2Options { - public: - ClientHttp2Options(const envoy::config::core::v3::Http2ProtocolOptions& http2_options); - }; - - /** - * Base class for client and server side streams. - */ - struct StreamImpl : public virtual StreamEncoder, - public Stream, - public LinkedObject, - public Event::DeferredDeletable, - public StreamCallbackHelper { - - StreamImpl(ConnectionImpl& parent, uint32_t buffer_limit); - ~StreamImpl() override; - // TODO(mattklein123): Optimally this would be done in the destructor but there are currently - // deferred delete lifetime issues that need sorting out if the destructor of the stream is - // going to be able to refer to the parent connection. - void destroy(); - void disarmStreamIdleTimer() { - if (stream_idle_timer_ != nullptr) { - // To ease testing and the destructor assertion. - stream_idle_timer_->disableTimer(); - stream_idle_timer_.reset(); - } - } - - StreamImpl* base() { return this; } - ssize_t onDataSourceRead(uint64_t length, uint32_t* data_flags); - int onDataSourceSend(const uint8_t* framehd, size_t length); - void resetStreamWorker(StreamResetReason reason); - static void buildHeaders(std::vector& final_headers, const HeaderMap& headers); - void saveHeader(HeaderString&& name, HeaderString&& value); - void encodeHeadersBase(const std::vector& final_headers, bool end_stream); - virtual void submitHeaders(const std::vector& final_headers, - nghttp2_data_provider* provider) PURE; - void encodeTrailersBase(const HeaderMap& headers); - void submitTrailers(const HeaderMap& trailers); - void submitMetadata(uint8_t flags); - virtual StreamDecoder& decoder() PURE; - virtual HeaderMap& headers() PURE; - virtual void allocTrailers() PURE; - virtual HeaderMapPtr cloneTrailers(const HeaderMap& trailers) PURE; - virtual void createPendingFlushTimer() PURE; - void onPendingFlushTimer(); - - // Http::StreamEncoder - void encodeData(Buffer::Instance& data, bool end_stream) override; - Stream& getStream() override { return *this; } - void encodeMetadata(const MetadataMapVector& metadata_map_vector) override; - Http1StreamEncoderOptionsOptRef http1StreamEncoderOptions() override { return absl::nullopt; } - - // Http::Stream - void addCallbacks(StreamCallbacks& callbacks) override { addCallbacksHelper(callbacks); } - void removeCallbacks(StreamCallbacks& callbacks) override { removeCallbacksHelper(callbacks); } - void resetStream(StreamResetReason reason) override; - void readDisable(bool disable) override; - uint32_t bufferLimit() override { return pending_recv_data_.highWatermark(); } - const Network::Address::InstanceConstSharedPtr& connectionLocalAddress() override { - return parent_.connection_.localAddress(); - } - absl::string_view responseDetails() override { return details_; } - void setFlushTimeout(std::chrono::milliseconds timeout) override { - stream_idle_timeout_ = timeout; - } - - // This code assumes that details is a static string, so that we - // can avoid copying it. - void setDetails(absl::string_view details) { - // TODO(asraa): In some cases nghttp2's error handling may cause processing of multiple - // invalid frames for a single stream. If a temporal stream error is returned from a callback, - // remaining frames in the buffer will still be partially processed. For example, remaining - // frames will still parse through nghttp2's push promise error handling and in - // onBeforeFrame(Send/Received) callbacks, which may return invalid frame errors and attempt - // to set details again. In these cases, we simply do not overwrite details. When internal - // error latching is implemented in the codec for exception removal, we should prevent calling - // setDetails in an error state. - if (details_.empty()) { - details_ = details; - } - } - - void setWriteBufferWatermarks(uint32_t low_watermark, uint32_t high_watermark) { - pending_recv_data_.setWatermarks(low_watermark, high_watermark); - pending_send_data_.setWatermarks(low_watermark, high_watermark); - } - - // If the receive buffer encounters watermark callbacks, enable/disable reads on this stream. - void pendingRecvBufferHighWatermark(); - void pendingRecvBufferLowWatermark(); - - // If the send buffer encounters watermark callbacks, propagate this information to the streams. - // The router and connection manager will propagate them on as appropriate. - void pendingSendBufferHighWatermark(); - void pendingSendBufferLowWatermark(); - - // Does any necessary WebSocket/Upgrade conversion, then passes the headers - // to the decoder_. - virtual void decodeHeaders() PURE; - virtual void decodeTrailers() PURE; - - // Get MetadataEncoder for this stream. - Http::Http2::MetadataEncoder& getMetadataEncoder(); - // Get MetadataDecoder for this stream. - Http::Http2::MetadataDecoder& getMetadataDecoder(); - // Callback function for MetadataDecoder. - void onMetadataDecoded(MetadataMapPtr&& metadata_map_ptr); - - bool buffersOverrun() const { return read_disable_count_ > 0; } - - void encodeDataHelper(Buffer::Instance& data, bool end_stream, - bool skip_encoding_empty_trailers); - - ConnectionImpl& parent_; - int32_t stream_id_{-1}; - uint32_t unconsumed_bytes_{0}; - uint32_t read_disable_count_{0}; - - // Note that in current implementation the watermark callbacks of the pending_recv_data_ are - // never called. The watermark value is set to the size of the stream window. As a result this - // watermark can never overflow because the peer can never send more bytes than the stream - // window without triggering protocol error and this buffer is drained after each DATA frame was - // dispatched through the filter chain. See source/docs/flow_control.md for more information. - Buffer::WatermarkBuffer pending_recv_data_{ - [this]() -> void { this->pendingRecvBufferLowWatermark(); }, - [this]() -> void { this->pendingRecvBufferHighWatermark(); }, - []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }}; - Buffer::WatermarkBuffer pending_send_data_{ - [this]() -> void { this->pendingSendBufferLowWatermark(); }, - [this]() -> void { this->pendingSendBufferHighWatermark(); }, - []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }}; - HeaderMapPtr pending_trailers_to_encode_; - std::unique_ptr metadata_decoder_; - std::unique_ptr metadata_encoder_; - absl::optional deferred_reset_; - HeaderString cookies_; - bool local_end_stream_sent_ : 1; - bool remote_end_stream_ : 1; - bool data_deferred_ : 1; - bool received_noninformational_headers_ : 1; - bool pending_receive_buffer_high_watermark_called_ : 1; - bool pending_send_buffer_high_watermark_called_ : 1; - bool reset_due_to_messaging_error_ : 1; - absl::string_view details_; - // See HttpConnectionManager.stream_idle_timeout. - std::chrono::milliseconds stream_idle_timeout_{}; - Event::TimerPtr stream_idle_timer_; - }; - - using StreamImplPtr = std::unique_ptr; - - /** - * Client side stream (request). - */ - struct ClientStreamImpl : public StreamImpl, public RequestEncoder { - ClientStreamImpl(ConnectionImpl& parent, uint32_t buffer_limit, - ResponseDecoder& response_decoder) - : StreamImpl(parent, buffer_limit), response_decoder_(response_decoder), - headers_or_trailers_(ResponseHeaderMapImpl::create()) {} - - // StreamImpl - void submitHeaders(const std::vector& final_headers, - nghttp2_data_provider* provider) override; - StreamDecoder& decoder() override { return response_decoder_; } - void decodeHeaders() override; - void decodeTrailers() override; - HeaderMap& headers() override { - if (absl::holds_alternative(headers_or_trailers_)) { - return *absl::get(headers_or_trailers_); - } else { - return *absl::get(headers_or_trailers_); - } - } - void allocTrailers() override { - // If we are waiting for informational headers, make a new response header map, otherwise - // we are about to receive trailers. The codec makes sure this is the only valid sequence. - if (received_noninformational_headers_) { - headers_or_trailers_.emplace(ResponseTrailerMapImpl::create()); - } else { - headers_or_trailers_.emplace(ResponseHeaderMapImpl::create()); - } - } - HeaderMapPtr cloneTrailers(const HeaderMap& trailers) override { - return createHeaderMap(trailers); - } - void createPendingFlushTimer() override { - // Client streams do not create a flush timer because we currently assume that any failure - // to flush would be covered by a request/stream/etc. timeout. - } - - // RequestEncoder - Status encodeHeaders(const RequestHeaderMap& headers, bool end_stream) override; - void encodeTrailers(const RequestTrailerMap& trailers) override { - encodeTrailersBase(trailers); - } - - ResponseDecoder& response_decoder_; - absl::variant headers_or_trailers_; - std::string upgrade_type_; - }; - - using ClientStreamImplPtr = std::unique_ptr; - - /** - * Server side stream (response). - */ - struct ServerStreamImpl : public StreamImpl, public ResponseEncoder { - ServerStreamImpl(ConnectionImpl& parent, uint32_t buffer_limit) - : StreamImpl(parent, buffer_limit), headers_or_trailers_(RequestHeaderMapImpl::create()) { - stream_error_on_invalid_http_message_ = parent.stream_error_on_invalid_http_messaging_; - } - - // StreamImpl - void submitHeaders(const std::vector& final_headers, - nghttp2_data_provider* provider) override; - StreamDecoder& decoder() override { return *request_decoder_; } - void decodeHeaders() override; - void decodeTrailers() override; - HeaderMap& headers() override { - if (absl::holds_alternative(headers_or_trailers_)) { - return *absl::get(headers_or_trailers_); - } else { - return *absl::get(headers_or_trailers_); - } - } - void allocTrailers() override { - headers_or_trailers_.emplace(RequestTrailerMapImpl::create()); - } - HeaderMapPtr cloneTrailers(const HeaderMap& trailers) override { - return createHeaderMap(trailers); - } - void createPendingFlushTimer() override; - - // ResponseEncoder - void encode100ContinueHeaders(const ResponseHeaderMap& headers) override; - void encodeHeaders(const ResponseHeaderMap& headers, bool end_stream) override; - void encodeTrailers(const ResponseTrailerMap& trailers) override { - encodeTrailersBase(trailers); - } - - RequestDecoder* request_decoder_{}; - absl::variant headers_or_trailers_; - bool stream_error_on_invalid_http_message_; - - bool streamErrorOnInvalidHttpMessage() const override { - return stream_error_on_invalid_http_message_; - } - }; - - using ServerStreamImplPtr = std::unique_ptr; - - ConnectionImpl* base() { return this; } - // NOTE: Always use non debug nullptr checks against the return value of this function. There are - // edge cases (such as for METADATA frames) where nghttp2 will issue a callback for a stream_id - // that is not associated with an existing stream. - StreamImpl* getStream(int32_t stream_id); - int saveHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value); - void sendPendingFrames(); - void sendSettings(const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - bool disable_push); - // Callback triggered when the peer's SETTINGS frame is received. - // NOTE: This is only used for tests. - virtual void onSettingsForTest(const nghttp2_settings&) {} - - /** - * Check if header name contains underscore character. - * Underscore character is allowed in header names by the RFC-7230 and this check is implemented - * as a security measure due to systems that treat '_' and '-' as interchangeable. - * The ServerConnectionImpl may drop header or reject request based on the - * `common_http_protocol_options.headers_with_underscores_action` configuration option in the - * HttpConnectionManager. - */ - virtual absl::optional checkHeaderNameForUnderscores(absl::string_view /* header_name */) { - return absl::nullopt; - } - - /** - * This method checks if a protocol constraint had been violated in the sendPendingFrames() call - * outside of the dispatch context. - * This method is a stop-gap solution for implementing checking of protocol constraint violations - * outside of the dispatch context (where at this point the sendPendingFrames() method always - * returns success). It allows each case where sendPendingFrames() is called outside of the - * dispatch context to be fixed in its own PR so it is easier to review and reason about. Once all - * error handling is implemented this method will be removed and the `sendPendingFrames()` will be - * changed to return error in both dispatching and non-dispatching contexts. At the same time the - * RELEASE_ASSERTs will be removed as well. - * The implementation in the ClientConnectionImpl is a no-op as client connections to not check - * protocol constraints. - * The implementation in the ServerConnectionImpl schedules callback to terminate connection if - * the protocol constraint was violated. - */ - virtual void checkProtocolConstraintViolation() PURE; - - /** - * Callback for terminating connection when protocol constrain has been violated - * outside of the dispatch context. - */ - void scheduleProtocolConstraintViolationCallback(); - void onProtocolConstraintViolation(); - - static Http2Callbacks http2_callbacks_; - - std::list active_streams_; - nghttp2_session* session_{}; - Http::Http2::CodecStats& stats_; - Network::Connection& connection_; - const uint32_t max_headers_kb_; - const uint32_t max_headers_count_; - uint32_t per_stream_buffer_limit_; - bool allow_metadata_; - const bool stream_error_on_invalid_http_messaging_; - bool flood_detected_; - - // Set if the type of frame that is about to be sent is PING or SETTINGS with the ACK flag set, or - // RST_STREAM. - bool is_outbound_flood_monitored_control_frame_ = 0; - ::Envoy::Http::Http2::ProtocolConstraints protocol_constraints_; - - // For the flood mitigation to work the onSend callback must be called once for each outbound - // frame. This is what the nghttp2 library is doing, however this is not documented. The - // Http2FloodMitigationTest.* tests in test/integration/http2_integration_test.cc will break if - // this changes in the future. Also it is important that onSend does not do partial writes, as the - // nghttp2 library will keep calling this callback to write the rest of the frame. - ssize_t onSend(const uint8_t* data, size_t length); - - // Some browsers (e.g. WebKit-based browsers: https://bugs.webkit.org/show_bug.cgi?id=210108) have - // a problem with processing empty trailers (END_STREAM | END_HEADERS with zero length HEADERS) of - // an HTTP/2 response as reported here: https://github.com/envoyproxy/envoy/issues/10514. This is - // controlled by "envoy.reloadable_features.http2_skip_encoding_empty_trailers" runtime feature - // flag. - const bool skip_encoding_empty_trailers_; - -private: - virtual ConnectionCallbacks& callbacks() PURE; - virtual int onBeginHeaders(const nghttp2_frame* frame) PURE; - int onData(int32_t stream_id, const uint8_t* data, size_t len); - int onBeforeFrameReceived(const nghttp2_frame_hd* hd); - int onFrameReceived(const nghttp2_frame* frame); - int onBeforeFrameSend(const nghttp2_frame* frame); - int onFrameSend(const nghttp2_frame* frame); - int onError(absl::string_view error); - virtual int onHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) PURE; - int onInvalidFrame(int32_t stream_id, int error_code); - int onStreamClose(int32_t stream_id, uint32_t error_code); - int onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len); - int onMetadataFrameComplete(int32_t stream_id, bool end_metadata); - ssize_t packMetadata(int32_t stream_id, uint8_t* buf, size_t len); - // Adds buffer fragment for a new outbound frame to the supplied Buffer::OwnedImpl. - // Returns true on success or false if outbound queue limits were exceeded. - bool addOutboundFrameFragment(Buffer::OwnedImpl& output, const uint8_t* data, size_t length); - virtual Envoy::Http::Http2::ProtocolConstraints::ReleasorProc - trackOutboundFrames(bool is_outbound_flood_monitored_control_frame) PURE; - virtual bool trackInboundFrames(const nghttp2_frame_hd* hd, uint32_t padding_length) PURE; - void sendKeepalive(); - void onKeepaliveResponse(); - void onKeepaliveResponseTimeout(); - - bool dispatching_ : 1; - bool raised_goaway_ : 1; - bool pending_deferred_reset_ : 1; - Event::SchedulableCallbackPtr protocol_constraint_violation_callback_; - Random::RandomGenerator& random_; - Event::TimerPtr keepalive_send_timer_; - Event::TimerPtr keepalive_timeout_timer_; - std::chrono::milliseconds keepalive_interval_; - std::chrono::milliseconds keepalive_timeout_; - uint32_t keepalive_interval_jitter_percent_; -}; - -/** - * HTTP/2 client connection codec. - */ -class ClientConnectionImpl : public ClientConnection, public ConnectionImpl { -public: - using SessionFactory = Nghttp2SessionFactory; - ClientConnectionImpl(Network::Connection& connection, ConnectionCallbacks& callbacks, - Http::Http2::CodecStats& stats, Random::RandomGenerator& random, - const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - const uint32_t max_response_headers_kb, - const uint32_t max_response_headers_count, - SessionFactory& http2_session_factory); - - // Http::ClientConnection - RequestEncoder& newStream(ResponseDecoder& response_decoder) override; - -private: - // ConnectionImpl - ConnectionCallbacks& callbacks() override { return callbacks_; } - int onBeginHeaders(const nghttp2_frame* frame) override; - int onHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) override; - - // Presently client connections do not track or check queue limits for outbound frames and do not - // terminate connections when queue limits are exceeded. The primary reason is the complexity of - // the clean-up of upstream connections. The clean-up of upstream connection causes RST_STREAM - // messages to be sent on corresponding downstream connections. This may actually trigger flood - // mitigation on the downstream connections, which causes an exception to be thrown in the middle - // of the clean-up loop, leaving resources in a half cleaned up state. - // TODO(yanavlasov): add flood mitigation for upstream connections as well. - Envoy::Http::Http2::ProtocolConstraints::ReleasorProc trackOutboundFrames(bool) override { - return Envoy::Http::Http2::ProtocolConstraints::ReleasorProc([]() {}); - } - bool trackInboundFrames(const nghttp2_frame_hd*, uint32_t) override { return true; } - void checkProtocolConstraintViolation() override {} - - Http::ConnectionCallbacks& callbacks_; -}; - -/** - * HTTP/2 server connection codec. - */ -class ServerConnectionImpl : public ServerConnection, public ConnectionImpl { -public: - ServerConnectionImpl(Network::Connection& connection, ServerConnectionCallbacks& callbacks, - Http::Http2::CodecStats& stats, Random::RandomGenerator& random, - const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - const uint32_t max_request_headers_kb, - const uint32_t max_request_headers_count, - envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction - headers_with_underscores_action); - -private: - // ConnectionImpl - ConnectionCallbacks& callbacks() override { return callbacks_; } - int onBeginHeaders(const nghttp2_frame* frame) override; - int onHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) override; - Envoy::Http::Http2::ProtocolConstraints::ReleasorProc - trackOutboundFrames(bool is_outbound_flood_monitored_control_frame) override; - bool trackInboundFrames(const nghttp2_frame_hd* hd, uint32_t padding_length) override; - absl::optional checkHeaderNameForUnderscores(absl::string_view header_name) override; - - /** - * Check protocol constraint violations outside of the dispatching context. - * This method ASSERTs if it is called in the dispatching context. - */ - void checkProtocolConstraintViolation() override; - - // Http::Connection - // The reason for overriding the dispatch method is to do flood mitigation only when - // processing data from downstream client. Doing flood mitigation when processing upstream - // responses makes clean-up tricky, which needs to be improved (see comments for the - // ClientConnectionImpl::checkProtocolConstraintsStatus method). The dispatch method on the - // ServerConnectionImpl objects is called only when processing data from the downstream client in - // the ConnectionManagerImpl::onData method. - Http::Status dispatch(Buffer::Instance& data) override; - Http::Status innerDispatch(Buffer::Instance& data) override; - - ServerConnectionCallbacks& callbacks_; - - // This flag indicates that downstream data is being dispatched and turns on flood mitigation - // in the checkMaxOutbound*Framed methods. - bool dispatching_downstream_data_{false}; - - // The action to take when a request header name contains underscore characters. - envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction - headers_with_underscores_action_; -}; - -} // namespace Http2 -} // namespace Legacy -} // namespace Http -} // namespace Envoy diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 1ac9652de1b4..06288bbe4ba1 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -77,7 +77,6 @@ constexpr const char* runtime_features[] = { "envoy.reloadable_features.http_transport_failure_reason_in_body", "envoy.reloadable_features.http2_skip_encoding_empty_trailers", "envoy.reloadable_features.listener_in_place_filterchain_update", - "envoy.reloadable_features.new_codec_behavior", "envoy.reloadable_features.overload_manager_disable_keepalive_drain_http2", "envoy.reloadable_features.prefer_quic_kernel_bpf_packet_routing", "envoy.reloadable_features.preserve_query_string_in_path_redirects", diff --git a/source/extensions/filters/network/http_connection_manager/BUILD b/source/extensions/filters/network/http_connection_manager/BUILD index 012cd2b00cce..db02c5750db8 100644 --- a/source/extensions/filters/network/http_connection_manager/BUILD +++ b/source/extensions/filters/network/http_connection_manager/BUILD @@ -38,9 +38,7 @@ envoy_cc_extension( "//source/common/http:default_server_string_lib", "//source/common/http:request_id_extension_lib", "//source/common/http:utility_lib", - "//source/common/http/http1:codec_legacy_lib", "//source/common/http/http1:codec_lib", - "//source/common/http/http2:codec_legacy_lib", "//source/common/http/http2:codec_lib", "//source/common/json:json_loader_lib", "//source/common/local_reply:local_reply_lib", diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index 2222f052e590..01a88e0b9ab6 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -22,9 +22,7 @@ #include "common/http/conn_manager_utility.h" #include "common/http/default_server_string.h" #include "common/http/http1/codec_impl.h" -#include "common/http/http1/codec_impl_legacy.h" #include "common/http/http2/codec_impl.h" -#include "common/http/http2/codec_impl_legacy.h" #include "common/http/http3/quic_codec_factory.h" #include "common/http/http3/well_known_names.h" #include "common/http/request_id_extension_impl.h" @@ -566,34 +564,17 @@ HttpConnectionManagerConfig::createCodec(Network::Connection& connection, Http::ServerConnectionCallbacks& callbacks) { switch (codec_type_) { case CodecType::HTTP1: { - if (context_.runtime().snapshot().runtimeFeatureEnabled( - "envoy.reloadable_features.new_codec_behavior")) { - return std::make_unique( - connection, Http::Http1::CodecStats::atomicGet(http1_codec_stats_, context_.scope()), - callbacks, http1_settings_, maxRequestHeadersKb(), maxRequestHeadersCount(), - headersWithUnderscoresAction()); - } else { - return std::make_unique( - connection, Http::Http1::CodecStats::atomicGet(http1_codec_stats_, context_.scope()), - callbacks, http1_settings_, maxRequestHeadersKb(), maxRequestHeadersCount(), - headersWithUnderscoresAction()); - } + return std::make_unique( + connection, Http::Http1::CodecStats::atomicGet(http1_codec_stats_, context_.scope()), + callbacks, http1_settings_, maxRequestHeadersKb(), maxRequestHeadersCount(), + headersWithUnderscoresAction()); } case CodecType::HTTP2: { - if (context_.runtime().snapshot().runtimeFeatureEnabled( - "envoy.reloadable_features.new_codec_behavior")) { - return std::make_unique( - connection, callbacks, - Http::Http2::CodecStats::atomicGet(http2_codec_stats_, context_.scope()), - context_.api().randomGenerator(), http2_options_, maxRequestHeadersKb(), - maxRequestHeadersCount(), headersWithUnderscoresAction()); - } else { - return std::make_unique( - connection, callbacks, - Http::Http2::CodecStats::atomicGet(http2_codec_stats_, context_.scope()), - context_.api().randomGenerator(), http2_options_, maxRequestHeadersKb(), - maxRequestHeadersCount(), headersWithUnderscoresAction()); - } + return std::make_unique( + connection, callbacks, + Http::Http2::CodecStats::atomicGet(http2_codec_stats_, context_.scope()), + context_.api().randomGenerator(), http2_options_, maxRequestHeadersKb(), + maxRequestHeadersCount(), headersWithUnderscoresAction()); } case CodecType::HTTP3: // Hard code Quiche factory name here to instantiate a QUIC codec implemented. diff --git a/test/common/http/http1/BUILD b/test/common/http/http1/BUILD index b63855d4d0f4..bb66b74b8ffd 100644 --- a/test/common/http/http1/BUILD +++ b/test/common/http/http1/BUILD @@ -26,7 +26,6 @@ envoy_cc_test( "//source/common/event:dispatcher_lib", "//source/common/http:exception_lib", "//source/common/http:header_map_lib", - "//source/common/http/http1:codec_legacy_lib", "//source/common/http/http1:codec_lib", "//test/common/stats:stat_test_utility_lib", "//test/mocks/buffer:buffer_mocks", diff --git a/test/common/http/http1/codec_impl_test.cc b/test/common/http/http1/codec_impl_test.cc index b292dd1aa644..c2eb068cf3f5 100644 --- a/test/common/http/http1/codec_impl_test.cc +++ b/test/common/http/http1/codec_impl_test.cc @@ -9,7 +9,6 @@ #include "common/http/exception.h" #include "common/http/header_map_impl.h" #include "common/http/http1/codec_impl.h" -#include "common/http/http1/codec_impl_legacy.h" #include "common/runtime/runtime_impl.h" #include "test/common/stats/stat_test_utility.h" @@ -55,7 +54,7 @@ Buffer::OwnedImpl createBufferWithNByteSlices(absl::string_view input, size_t ma } } // namespace -class Http1CodecTestBase { +class Http1CodecTestBase : public testing::Test { protected: Http::Http1::CodecStats& http1CodecStats() { return Http::Http1::CodecStats::atomicGet(http1_codec_stats_, store_); @@ -65,21 +64,12 @@ class Http1CodecTestBase { Http::Http1::CodecStats::AtomicPtr http1_codec_stats_; }; -class Http1ServerConnectionImplTest : public Http1CodecTestBase, - public testing::TestWithParam { +class Http1ServerConnectionImplTest : public Http1CodecTestBase { public: - bool testingNewCodec() { return GetParam(); } - void initialize() { - if (testingNewCodec()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, headers_with_underscores_action_); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, headers_with_underscores_action_); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, + max_request_headers_count_, headers_with_underscores_action_); } NiceMock connection_; @@ -139,15 +129,9 @@ void Http1ServerConnectionImplTest::expect400(Protocol p, bool allow_absolute_ur if (allow_absolute_url) { codec_settings_.allow_absolute_url_ = allow_absolute_url; - if (testingNewCodec()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, + max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); } MockRequestDecoder decoder; @@ -175,15 +159,9 @@ void Http1ServerConnectionImplTest::expectHeadersTest(Protocol p, bool allow_abs // Make a new 'codec' with the right settings if (allow_absolute_url) { codec_settings_.allow_absolute_url_ = allow_absolute_url; - if (testingNewCodec()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, + max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); } MockRequestDecoder decoder; @@ -202,15 +180,9 @@ void Http1ServerConnectionImplTest::expectTrailersTest(bool enable_trailers) { // Make a new 'codec' with the right settings if (enable_trailers) { codec_settings_.enable_trailers_ = enable_trailers; - if (testingNewCodec()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, + max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); } InSequence sequence; @@ -244,15 +216,9 @@ void Http1ServerConnectionImplTest::testTrailersExceedLimit(std::string trailer_ initialize(); // Make a new 'codec' with the right settings codec_settings_.enable_trailers_ = enable_trailers; - if (testingNewCodec()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, + max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); std::string exception_reason; NiceMock decoder; EXPECT_CALL(callbacks_, newStream(_, _)) @@ -333,15 +299,9 @@ void Http1ServerConnectionImplTest::testRequestHeadersAccepted(std::string heade void Http1ServerConnectionImplTest::testServerAllowChunkedContentLength(uint32_t content_length, bool allow_chunked_length) { codec_settings_.allow_chunked_length_ = allow_chunked_length; - if (testingNewCodec()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, + max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); MockRequestDecoder decoder; Http::ResponseEncoder* response_encoder = nullptr; @@ -389,12 +349,7 @@ void Http1ServerConnectionImplTest::testServerAllowChunkedContentLength(uint32_t } } -INSTANTIATE_TEST_SUITE_P(Codecs, Http1ServerConnectionImplTest, testing::Bool(), - [](const testing::TestParamInfo& param) { - return param.param ? "New" : "Legacy"; - }); - -TEST_P(Http1ServerConnectionImplTest, EmptyHeader) { +TEST_F(Http1ServerConnectionImplTest, EmptyHeader) { initialize(); InSequence sequence; @@ -418,7 +373,7 @@ TEST_P(Http1ServerConnectionImplTest, EmptyHeader) { // We support the identity encoding, but because it does not end in chunked encoding we reject it // per RFC 7230 Section 3.3.3 -TEST_P(Http1ServerConnectionImplTest, IdentityEncodingNoChunked) { +TEST_F(Http1ServerConnectionImplTest, IdentityEncodingNoChunked) { initialize(); InSequence sequence; @@ -433,7 +388,7 @@ TEST_P(Http1ServerConnectionImplTest, IdentityEncodingNoChunked) { EXPECT_EQ(status.message(), "http/1.1 protocol error: unsupported transfer encoding"); } -TEST_P(Http1ServerConnectionImplTest, UnsupportedEncoding) { +TEST_F(Http1ServerConnectionImplTest, UnsupportedEncoding) { initialize(); InSequence sequence; @@ -452,7 +407,7 @@ TEST_P(Http1ServerConnectionImplTest, UnsupportedEncoding) { // Note that this test is validating a performance optimization, not a functional behavior // requirement. If future changes to the codec make this test not pass, but do not regress // performance of large HTTP body handling, this test can be changed or removed. -TEST_P(Http1ServerConnectionImplTest, LargeBodyOptimization) { +TEST_F(Http1ServerConnectionImplTest, LargeBodyOptimization) { initialize(); InSequence sequence; @@ -482,7 +437,7 @@ TEST_P(Http1ServerConnectionImplTest, LargeBodyOptimization) { } // Verify that data in the two body chunks is merged before the call to decodeData. -TEST_P(Http1ServerConnectionImplTest, ChunkedBody) { +TEST_F(Http1ServerConnectionImplTest, ChunkedBody) { initialize(); InSequence sequence; @@ -513,7 +468,7 @@ TEST_P(Http1ServerConnectionImplTest, ChunkedBody) { // Verify dispatch behavior when dispatching an incomplete chunk, and resumption of the parse via a // second dispatch. -TEST_P(Http1ServerConnectionImplTest, ChunkedBodySplitOverTwoDispatches) { +TEST_F(Http1ServerConnectionImplTest, ChunkedBodySplitOverTwoDispatches) { initialize(); InSequence sequence; @@ -551,7 +506,7 @@ TEST_P(Http1ServerConnectionImplTest, ChunkedBodySplitOverTwoDispatches) { // Verify that headers and chunked body are processed correctly and data is merged before the // decodeData call even if delivered in a buffer that holds 1 byte per slice. -TEST_P(Http1ServerConnectionImplTest, ChunkedBodyFragmentedBuffer) { +TEST_F(Http1ServerConnectionImplTest, ChunkedBodyFragmentedBuffer) { initialize(); InSequence sequence; @@ -580,7 +535,7 @@ TEST_P(Http1ServerConnectionImplTest, ChunkedBodyFragmentedBuffer) { EXPECT_EQ(0U, buffer.length()); } -TEST_P(Http1ServerConnectionImplTest, ChunkedBodyCase) { +TEST_F(Http1ServerConnectionImplTest, ChunkedBodyCase) { initialize(); InSequence sequence; @@ -607,7 +562,7 @@ TEST_P(Http1ServerConnectionImplTest, ChunkedBodyCase) { // Verify that body dispatch does not happen after detecting a parse error processing a chunk // header. -TEST_P(Http1ServerConnectionImplTest, InvalidChunkHeader) { +TEST_F(Http1ServerConnectionImplTest, InvalidChunkHeader) { initialize(); InSequence sequence; @@ -633,7 +588,7 @@ TEST_P(Http1ServerConnectionImplTest, InvalidChunkHeader) { EXPECT_EQ(status.message(), "http/1.1 protocol error: HPE_INVALID_CHUNK_SIZE"); } -TEST_P(Http1ServerConnectionImplTest, IdentityAndChunkedBody) { +TEST_F(Http1ServerConnectionImplTest, IdentityAndChunkedBody) { initialize(); InSequence sequence; @@ -650,7 +605,7 @@ TEST_P(Http1ServerConnectionImplTest, IdentityAndChunkedBody) { EXPECT_EQ(status.message(), "http/1.1 protocol error: unsupported transfer encoding"); } -TEST_P(Http1ServerConnectionImplTest, HostWithLWS) { +TEST_F(Http1ServerConnectionImplTest, HostWithLWS) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -671,7 +626,7 @@ TEST_P(Http1ServerConnectionImplTest, HostWithLWS) { // Regression test for https://github.com/envoyproxy/envoy/issues/10270. Linear whitespace at the // beginning and end of a header value should be stripped. Whitespace in the middle should be // preserved. -TEST_P(Http1ServerConnectionImplTest, InnerLWSIsPreserved) { +TEST_F(Http1ServerConnectionImplTest, InnerLWSIsPreserved) { initialize(); // Header with many spaces surrounded by non-whitespace characters to ensure that dispatching is @@ -704,17 +659,11 @@ TEST_P(Http1ServerConnectionImplTest, InnerLWSIsPreserved) { } } -TEST_P(Http1ServerConnectionImplTest, CodecHasCorrectStreamErrorIfTrue) { +TEST_F(Http1ServerConnectionImplTest, CodecHasCorrectStreamErrorIfTrue) { codec_settings_.stream_error_on_invalid_http_message_ = true; - if (GetParam()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, + max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); Buffer::OwnedImpl buffer("GET / HTTP/1.1\r\n"); NiceMock decoder; @@ -729,17 +678,11 @@ TEST_P(Http1ServerConnectionImplTest, CodecHasCorrectStreamErrorIfTrue) { EXPECT_TRUE(response_encoder->streamErrorOnInvalidHttpMessage()); } -TEST_P(Http1ServerConnectionImplTest, CodecHasCorrectStreamErrorIfFalse) { +TEST_F(Http1ServerConnectionImplTest, CodecHasCorrectStreamErrorIfFalse) { codec_settings_.stream_error_on_invalid_http_message_ = false; - if (GetParam()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, - max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_request_headers_kb_, + max_request_headers_count_, envoy::config::core::v3::HttpProtocolOptions::ALLOW); Buffer::OwnedImpl buffer("GET / HTTP/1.1\r\n"); NiceMock decoder; @@ -754,7 +697,7 @@ TEST_P(Http1ServerConnectionImplTest, CodecHasCorrectStreamErrorIfFalse) { EXPECT_FALSE(response_encoder->streamErrorOnInvalidHttpMessage()); } -TEST_P(Http1ServerConnectionImplTest, CodecHasDefaultStreamErrorIfNotSet) { +TEST_F(Http1ServerConnectionImplTest, CodecHasDefaultStreamErrorIfNotSet) { initialize(); Buffer::OwnedImpl buffer("GET / HTTP/1.1\r\n"); @@ -770,7 +713,7 @@ TEST_P(Http1ServerConnectionImplTest, CodecHasDefaultStreamErrorIfNotSet) { EXPECT_FALSE(response_encoder->streamErrorOnInvalidHttpMessage()); } -TEST_P(Http1ServerConnectionImplTest, Http10) { +TEST_F(Http1ServerConnectionImplTest, Http10) { initialize(); InSequence sequence; @@ -788,7 +731,7 @@ TEST_P(Http1ServerConnectionImplTest, Http10) { EXPECT_EQ(Protocol::Http10, codec_->protocol()); } -TEST_P(Http1ServerConnectionImplTest, Http10AbsoluteNoOp) { +TEST_F(Http1ServerConnectionImplTest, Http10AbsoluteNoOp) { initialize(); TestRequestHeaderMapImpl expected_headers{{":path", "/"}, {":method", "GET"}}; @@ -796,7 +739,7 @@ TEST_P(Http1ServerConnectionImplTest, Http10AbsoluteNoOp) { expectHeadersTest(Protocol::Http10, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, Http10Absolute) { +TEST_F(Http1ServerConnectionImplTest, Http10Absolute) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -805,7 +748,7 @@ TEST_P(Http1ServerConnectionImplTest, Http10Absolute) { expectHeadersTest(Protocol::Http10, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, Http10MultipleResponses) { +TEST_F(Http1ServerConnectionImplTest, Http10MultipleResponses) { initialize(); MockRequestDecoder decoder; @@ -851,7 +794,7 @@ TEST_P(Http1ServerConnectionImplTest, Http10MultipleResponses) { } } -TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePath1) { +TEST_F(Http1ServerConnectionImplTest, Http11AbsolutePath1) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -860,7 +803,7 @@ TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePath1) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePath2) { +TEST_F(Http1ServerConnectionImplTest, Http11AbsolutePath2) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -869,7 +812,7 @@ TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePath2) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePathWithPort) { +TEST_F(Http1ServerConnectionImplTest, Http11AbsolutePathWithPort) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -879,7 +822,7 @@ TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePathWithPort) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, Http11AbsoluteEnabledNoOp) { +TEST_F(Http1ServerConnectionImplTest, Http11AbsoluteEnabledNoOp) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -888,7 +831,7 @@ TEST_P(Http1ServerConnectionImplTest, Http11AbsoluteEnabledNoOp) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, Http11InvalidRequest) { +TEST_F(Http1ServerConnectionImplTest, Http11InvalidRequest) { initialize(); // Invalid because www.somewhere.com is not an absolute path nor an absolute url @@ -896,7 +839,7 @@ TEST_P(Http1ServerConnectionImplTest, Http11InvalidRequest) { expect400(Protocol::Http11, true, buffer, "http1.codec_error"); } -TEST_P(Http1ServerConnectionImplTest, Http11InvalidTrailerPost) { +TEST_F(Http1ServerConnectionImplTest, Http11InvalidTrailerPost) { initialize(); MockRequestDecoder decoder; @@ -921,7 +864,7 @@ TEST_P(Http1ServerConnectionImplTest, Http11InvalidTrailerPost) { EXPECT_TRUE(isCodecProtocolError(status)); } -TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePathNoSlash) { +TEST_F(Http1ServerConnectionImplTest, Http11AbsolutePathNoSlash) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -930,21 +873,21 @@ TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePathNoSlash) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePathBad) { +TEST_F(Http1ServerConnectionImplTest, Http11AbsolutePathBad) { initialize(); Buffer::OwnedImpl buffer("GET * HTTP/1.1\r\nHost: bah\r\n\r\n"); expect400(Protocol::Http11, true, buffer, "http1.invalid_url"); } -TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePortTooLarge) { +TEST_F(Http1ServerConnectionImplTest, Http11AbsolutePortTooLarge) { initialize(); Buffer::OwnedImpl buffer("GET http://foobar.com:1000000 HTTP/1.1\r\nHost: bah\r\n\r\n"); expect400(Protocol::Http11, true, buffer); } -TEST_P(Http1ServerConnectionImplTest, SketchyConnectionHeader) { +TEST_F(Http1ServerConnectionImplTest, SketchyConnectionHeader) { initialize(); Buffer::OwnedImpl buffer( @@ -952,7 +895,7 @@ TEST_P(Http1ServerConnectionImplTest, SketchyConnectionHeader) { expect400(Protocol::Http11, true, buffer, "http1.connection_header_rejected"); } -TEST_P(Http1ServerConnectionImplTest, Http11RelativeOnly) { +TEST_F(Http1ServerConnectionImplTest, Http11RelativeOnly) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -961,7 +904,7 @@ TEST_P(Http1ServerConnectionImplTest, Http11RelativeOnly) { expectHeadersTest(Protocol::Http11, false, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, Http11Options) { +TEST_F(Http1ServerConnectionImplTest, Http11Options) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -970,7 +913,7 @@ TEST_P(Http1ServerConnectionImplTest, Http11Options) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, SimpleGet) { +TEST_F(Http1ServerConnectionImplTest, SimpleGet) { initialize(); InSequence sequence; @@ -987,7 +930,7 @@ TEST_P(Http1ServerConnectionImplTest, SimpleGet) { EXPECT_EQ(0U, buffer.length()); } -TEST_P(Http1ServerConnectionImplTest, BadRequestNoStreamLegacy) { +TEST_F(Http1ServerConnectionImplTest, BadRequestNoStreamLegacy) { TestScopedRuntime scoped_runtime; Runtime::LoaderSingleton::getExisting()->mergeValues( {{"envoy.reloadable_features.early_errors_via_hcm", "false"}}); @@ -1007,7 +950,7 @@ TEST_P(Http1ServerConnectionImplTest, BadRequestNoStreamLegacy) { // Test that if the stream is not created at the time an error is detected, it // is created as part of sending the protocol error. -TEST_P(Http1ServerConnectionImplTest, BadRequestNoStream) { +TEST_F(Http1ServerConnectionImplTest, BadRequestNoStream) { initialize(); MockRequestDecoder decoder; @@ -1027,7 +970,7 @@ TEST_P(Http1ServerConnectionImplTest, BadRequestNoStream) { // This behavior was observed during CVE-2019-18801 and helped to limit the // scope of affected Envoy configurations. -TEST_P(Http1ServerConnectionImplTest, RejectInvalidMethod) { +TEST_F(Http1ServerConnectionImplTest, RejectInvalidMethod) { initialize(); MockRequestDecoder decoder; @@ -1039,7 +982,7 @@ TEST_P(Http1ServerConnectionImplTest, RejectInvalidMethod) { EXPECT_TRUE(isCodecProtocolError(status)); } -TEST_P(Http1ServerConnectionImplTest, BadRequestStartedStream) { +TEST_F(Http1ServerConnectionImplTest, BadRequestStartedStream) { initialize(); MockRequestDecoder decoder; @@ -1055,7 +998,7 @@ TEST_P(Http1ServerConnectionImplTest, BadRequestStartedStream) { EXPECT_TRUE(isCodecProtocolError(status)); } -TEST_P(Http1ServerConnectionImplTest, FloodProtection) { +TEST_F(Http1ServerConnectionImplTest, FloodProtection) { initialize(); NiceMock decoder; @@ -1106,7 +1049,7 @@ TEST_P(Http1ServerConnectionImplTest, FloodProtection) { } } -TEST_P(Http1ServerConnectionImplTest, HostHeaderTranslation) { +TEST_F(Http1ServerConnectionImplTest, HostHeaderTranslation) { initialize(); InSequence sequence; @@ -1126,7 +1069,7 @@ TEST_P(Http1ServerConnectionImplTest, HostHeaderTranslation) { // Ensures that requests with invalid HTTP header values are properly rejected // when the runtime guard is enabled for the feature. -TEST_P(Http1ServerConnectionImplTest, HeaderInvalidCharsRejection) { +TEST_F(Http1ServerConnectionImplTest, HeaderInvalidCharsRejection) { TestScopedRuntime scoped_runtime; // When the runtime-guarded feature is enabled, invalid header values // should result in a rejection. @@ -1151,7 +1094,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderInvalidCharsRejection) { // Ensures that request headers with names containing the underscore character are allowed // when the option is set to allow. -TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreAllowed) { +TEST_F(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreAllowed) { headers_with_underscores_action_ = envoy::config::core::v3::HttpProtocolOptions::ALLOW; initialize(); @@ -1175,7 +1118,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreAllowed) { // Ensures that request headers with names containing the underscore character are dropped // when the option is set to drop headers. -TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreAreDropped) { +TEST_F(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreAreDropped) { headers_with_underscores_action_ = envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER; initialize(); @@ -1198,7 +1141,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreAreDropped) { // Ensures that request with header names containing the underscore character are rejected // when the option is set to reject request. -TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreCauseRequestRejected) { +TEST_F(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreCauseRequestRejected) { headers_with_underscores_action_ = envoy::config::core::v3::HttpProtocolOptions::REJECT_REQUEST; initialize(); @@ -1219,7 +1162,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreCauseRequestReject EXPECT_EQ(1, store_.counter("http1.requests_rejected_with_underscores_in_headers").value()); } -TEST_P(Http1ServerConnectionImplTest, HeaderInvalidAuthority) { +TEST_F(Http1ServerConnectionImplTest, HeaderInvalidAuthority) { TestScopedRuntime scoped_runtime; initialize(); @@ -1242,7 +1185,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderInvalidAuthority) { // Mutate an HTTP GET with embedded NULs, this should always be rejected in some // way (not necessarily with "head value contains NUL" though). -TEST_P(Http1ServerConnectionImplTest, HeaderMutateEmbeddedNul) { +TEST_F(Http1ServerConnectionImplTest, HeaderMutateEmbeddedNul) { const std::string example_input = "GET / HTTP/1.1\r\nHOST: h.com\r\nfoo: barbaz\r\n"; for (size_t n = 1; n < example_input.size(); ++n) { @@ -1266,7 +1209,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderMutateEmbeddedNul) { // Mutate an HTTP GET with CR or LF. These can cause an error status or maybe // result in a valid decodeHeaders(). In any case, the validHeaderString() // ASSERTs should validate we never have any embedded CR or LF. -TEST_P(Http1ServerConnectionImplTest, HeaderMutateEmbeddedCRLF) { +TEST_F(Http1ServerConnectionImplTest, HeaderMutateEmbeddedCRLF) { const std::string example_input = "GET / HTTP/1.1\r\nHOST: h.com\r\nfoo: barbaz\r\n"; for (const char c : {'\r', '\n'}) { @@ -1286,7 +1229,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderMutateEmbeddedCRLF) { } } -TEST_P(Http1ServerConnectionImplTest, CloseDuringHeadersComplete) { +TEST_F(Http1ServerConnectionImplTest, CloseDuringHeadersComplete) { initialize(); InSequence sequence; @@ -1308,7 +1251,7 @@ TEST_P(Http1ServerConnectionImplTest, CloseDuringHeadersComplete) { EXPECT_NE(0U, buffer.length()); } -TEST_P(Http1ServerConnectionImplTest, PostWithContentLength) { +TEST_F(Http1ServerConnectionImplTest, PostWithContentLength) { initialize(); InSequence sequence; @@ -1334,7 +1277,7 @@ TEST_P(Http1ServerConnectionImplTest, PostWithContentLength) { // Verify that headers and body with content length are processed correctly and data is merged // before the decodeData call even if delivered in a buffer that holds 1 byte per slice. -TEST_P(Http1ServerConnectionImplTest, PostWithContentLengthFragmentedBuffer) { +TEST_F(Http1ServerConnectionImplTest, PostWithContentLengthFragmentedBuffer) { initialize(); InSequence sequence; @@ -1359,7 +1302,7 @@ TEST_P(Http1ServerConnectionImplTest, PostWithContentLengthFragmentedBuffer) { EXPECT_EQ(0U, buffer.length()); } -TEST_P(Http1ServerConnectionImplTest, HeaderOnlyResponse) { +TEST_F(Http1ServerConnectionImplTest, HeaderOnlyResponse) { initialize(); NiceMock decoder; @@ -1385,7 +1328,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderOnlyResponse) { // As with Http1ClientConnectionImplTest.LargeHeaderRequestEncode but validate // the response encoder instead of request encoder. -TEST_P(Http1ServerConnectionImplTest, LargeHeaderResponseEncode) { +TEST_F(Http1ServerConnectionImplTest, LargeHeaderResponseEncode) { initialize(); NiceMock decoder; @@ -1411,7 +1354,7 @@ TEST_P(Http1ServerConnectionImplTest, LargeHeaderResponseEncode) { output); } -TEST_P(Http1ServerConnectionImplTest, HeaderOnlyResponseTrainProperHeaders) { +TEST_F(Http1ServerConnectionImplTest, HeaderOnlyResponseTrainProperHeaders) { codec_settings_.header_key_format_ = Http1Settings::HeaderKeyFormat::ProperCase; initialize(); @@ -1438,7 +1381,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderOnlyResponseTrainProperHeaders) { output); } -TEST_P(Http1ServerConnectionImplTest, HeaderOnlyResponseWith204) { +TEST_F(Http1ServerConnectionImplTest, HeaderOnlyResponseWith204) { initialize(); NiceMock decoder; @@ -1462,7 +1405,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderOnlyResponseWith204) { EXPECT_EQ("HTTP/1.1 204 No Content\r\n\r\n", output); } -TEST_P(Http1ServerConnectionImplTest, HeaderOnlyResponseWith100Then200) { +TEST_F(Http1ServerConnectionImplTest, HeaderOnlyResponseWith100Then200) { initialize(); NiceMock decoder; @@ -1493,7 +1436,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderOnlyResponseWith100Then200) { EXPECT_EQ("HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n", output); } -TEST_P(Http1ServerConnectionImplTest, MetadataTest) { +TEST_F(Http1ServerConnectionImplTest, MetadataTest) { initialize(); NiceMock decoder; @@ -1516,7 +1459,7 @@ TEST_P(Http1ServerConnectionImplTest, MetadataTest) { EXPECT_EQ(1, store_.counter("http1.metadata_not_supported_error").value()); } -TEST_P(Http1ServerConnectionImplTest, ChunkedResponse) { +TEST_F(Http1ServerConnectionImplTest, ChunkedResponse) { initialize(); NiceMock decoder; @@ -1552,7 +1495,7 @@ TEST_P(Http1ServerConnectionImplTest, ChunkedResponse) { output); } -TEST_P(Http1ServerConnectionImplTest, ChunkedResponseWithTrailers) { +TEST_F(Http1ServerConnectionImplTest, ChunkedResponseWithTrailers) { codec_settings_.enable_trailers_ = true; initialize(); NiceMock decoder; @@ -1585,7 +1528,7 @@ TEST_P(Http1ServerConnectionImplTest, ChunkedResponseWithTrailers) { output); } -TEST_P(Http1ServerConnectionImplTest, ContentLengthResponse) { +TEST_F(Http1ServerConnectionImplTest, ContentLengthResponse) { initialize(); NiceMock decoder; @@ -1612,7 +1555,7 @@ TEST_P(Http1ServerConnectionImplTest, ContentLengthResponse) { EXPECT_EQ("HTTP/1.1 200 OK\r\ncontent-length: 11\r\n\r\nHello World", output); } -TEST_P(Http1ServerConnectionImplTest, HeadRequestResponse) { +TEST_F(Http1ServerConnectionImplTest, HeadRequestResponse) { initialize(); NiceMock decoder; @@ -1636,7 +1579,7 @@ TEST_P(Http1ServerConnectionImplTest, HeadRequestResponse) { EXPECT_EQ("HTTP/1.1 200 OK\r\ncontent-length: 5\r\n\r\n", output); } -TEST_P(Http1ServerConnectionImplTest, HeadChunkedRequestResponse) { +TEST_F(Http1ServerConnectionImplTest, HeadChunkedRequestResponse) { initialize(); NiceMock decoder; @@ -1660,7 +1603,7 @@ TEST_P(Http1ServerConnectionImplTest, HeadChunkedRequestResponse) { EXPECT_EQ("HTTP/1.1 200 OK\r\ntransfer-encoding: chunked\r\n\r\n", output); } -TEST_P(Http1ServerConnectionImplTest, DoubleRequest) { +TEST_F(Http1ServerConnectionImplTest, DoubleRequest) { initialize(); NiceMock decoder; @@ -1686,11 +1629,11 @@ TEST_P(Http1ServerConnectionImplTest, DoubleRequest) { EXPECT_EQ(0U, buffer.length()); } -TEST_P(Http1ServerConnectionImplTest, RequestWithTrailersDropped) { expectTrailersTest(false); } +TEST_F(Http1ServerConnectionImplTest, RequestWithTrailersDropped) { expectTrailersTest(false); } -TEST_P(Http1ServerConnectionImplTest, RequestWithTrailersKept) { expectTrailersTest(true); } +TEST_F(Http1ServerConnectionImplTest, RequestWithTrailersKept) { expectTrailersTest(true); } -TEST_P(Http1ServerConnectionImplTest, IgnoreUpgradeH2c) { +TEST_F(Http1ServerConnectionImplTest, IgnoreUpgradeH2c) { initialize(); TestRequestHeaderMapImpl expected_headers{ @@ -1701,7 +1644,7 @@ TEST_P(Http1ServerConnectionImplTest, IgnoreUpgradeH2c) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, IgnoreUpgradeH2cClose) { +TEST_F(Http1ServerConnectionImplTest, IgnoreUpgradeH2cClose) { initialize(); TestRequestHeaderMapImpl expected_headers{{":authority", "www.somewhere.com"}, @@ -1714,7 +1657,7 @@ TEST_P(Http1ServerConnectionImplTest, IgnoreUpgradeH2cClose) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, IgnoreUpgradeH2cCloseEtc) { +TEST_F(Http1ServerConnectionImplTest, IgnoreUpgradeH2cCloseEtc) { initialize(); TestRequestHeaderMapImpl expected_headers{{":authority", "www.somewhere.com"}, @@ -1727,7 +1670,7 @@ TEST_P(Http1ServerConnectionImplTest, IgnoreUpgradeH2cCloseEtc) { expectHeadersTest(Protocol::Http11, true, buffer, expected_headers); } -TEST_P(Http1ServerConnectionImplTest, UpgradeRequest) { +TEST_F(Http1ServerConnectionImplTest, UpgradeRequest) { initialize(); InSequence sequence; @@ -1751,7 +1694,7 @@ TEST_P(Http1ServerConnectionImplTest, UpgradeRequest) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ServerConnectionImplTest, UpgradeRequestWithEarlyData) { +TEST_F(Http1ServerConnectionImplTest, UpgradeRequestWithEarlyData) { initialize(); InSequence sequence; @@ -1767,7 +1710,7 @@ TEST_P(Http1ServerConnectionImplTest, UpgradeRequestWithEarlyData) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ServerConnectionImplTest, UpgradeRequestWithTEChunked) { +TEST_F(Http1ServerConnectionImplTest, UpgradeRequestWithTEChunked) { initialize(); InSequence sequence; @@ -1785,7 +1728,7 @@ TEST_P(Http1ServerConnectionImplTest, UpgradeRequestWithTEChunked) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ServerConnectionImplTest, UpgradeRequestWithNoBody) { +TEST_F(Http1ServerConnectionImplTest, UpgradeRequestWithNoBody) { initialize(); InSequence sequence; @@ -1804,7 +1747,7 @@ TEST_P(Http1ServerConnectionImplTest, UpgradeRequestWithNoBody) { } // Test that 101 upgrade responses do not contain content-length or transfer-encoding headers. -TEST_P(Http1ServerConnectionImplTest, UpgradeRequestResponseHeaders) { +TEST_F(Http1ServerConnectionImplTest, UpgradeRequestResponseHeaders) { initialize(); NiceMock decoder; @@ -1828,7 +1771,7 @@ TEST_P(Http1ServerConnectionImplTest, UpgradeRequestResponseHeaders) { EXPECT_EQ("HTTP/1.1 101 Switching Protocols\r\n\r\n", output); } -TEST_P(Http1ServerConnectionImplTest, ConnectRequestNoContentLength) { +TEST_F(Http1ServerConnectionImplTest, ConnectRequestNoContentLength) { initialize(); InSequence sequence; @@ -1852,7 +1795,7 @@ TEST_P(Http1ServerConnectionImplTest, ConnectRequestNoContentLength) { // We use the absolute URL parsing code for CONNECT requests, but it does not // actually allow absolute URLs. -TEST_P(Http1ServerConnectionImplTest, ConnectRequestAbsoluteURLNotallowed) { +TEST_F(Http1ServerConnectionImplTest, ConnectRequestAbsoluteURLNotallowed) { initialize(); InSequence sequence; @@ -1865,7 +1808,7 @@ TEST_P(Http1ServerConnectionImplTest, ConnectRequestAbsoluteURLNotallowed) { EXPECT_TRUE(isCodecProtocolError(status)); } -TEST_P(Http1ServerConnectionImplTest, ConnectRequestWithEarlyData) { +TEST_F(Http1ServerConnectionImplTest, ConnectRequestWithEarlyData) { initialize(); InSequence sequence; @@ -1880,7 +1823,7 @@ TEST_P(Http1ServerConnectionImplTest, ConnectRequestWithEarlyData) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ServerConnectionImplTest, ConnectRequestWithTEChunked) { +TEST_F(Http1ServerConnectionImplTest, ConnectRequestWithTEChunked) { initialize(); InSequence sequence; @@ -1897,7 +1840,7 @@ TEST_P(Http1ServerConnectionImplTest, ConnectRequestWithTEChunked) { EXPECT_EQ(status.message(), "http/1.1 protocol error: unsupported transfer encoding"); } -TEST_P(Http1ServerConnectionImplTest, ConnectRequestWithNonZeroContentLength) { +TEST_F(Http1ServerConnectionImplTest, ConnectRequestWithNonZeroContentLength) { initialize(); InSequence sequence; @@ -1913,7 +1856,7 @@ TEST_P(Http1ServerConnectionImplTest, ConnectRequestWithNonZeroContentLength) { EXPECT_EQ(status.message(), "http/1.1 protocol error: unsupported content length"); } -TEST_P(Http1ServerConnectionImplTest, ConnectRequestWithZeroContentLength) { +TEST_F(Http1ServerConnectionImplTest, ConnectRequestWithZeroContentLength) { initialize(); InSequence sequence; @@ -1930,7 +1873,7 @@ TEST_P(Http1ServerConnectionImplTest, ConnectRequestWithZeroContentLength) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ServerConnectionImplTest, WatermarkTest) { +TEST_F(Http1ServerConnectionImplTest, WatermarkTest) { EXPECT_CALL(connection_, bufferLimit()).WillOnce(Return(10)); initialize(); @@ -1964,51 +1907,39 @@ TEST_P(Http1ServerConnectionImplTest, WatermarkTest) { ->onUnderlyingConnectionBelowWriteBufferLowWatermark(); } -TEST_P(Http1ServerConnectionImplTest, TestSmugglingDisallowChunkedContentLength0) { +TEST_F(Http1ServerConnectionImplTest, TestSmugglingDisallowChunkedContentLength0) { testServerAllowChunkedContentLength(0, false); } -TEST_P(Http1ServerConnectionImplTest, TestSmugglingDisallowChunkedContentLength1) { +TEST_F(Http1ServerConnectionImplTest, TestSmugglingDisallowChunkedContentLength1) { // content-length less than POST body size testServerAllowChunkedContentLength(1, false); } -TEST_P(Http1ServerConnectionImplTest, TestSmugglingDisallowChunkedContentLength100) { +TEST_F(Http1ServerConnectionImplTest, TestSmugglingDisallowChunkedContentLength100) { // content-length greater than POST body size testServerAllowChunkedContentLength(100, false); } -TEST_P(Http1ServerConnectionImplTest, TestSmugglingAllowChunkedContentLength0) { +TEST_F(Http1ServerConnectionImplTest, TestSmugglingAllowChunkedContentLength0) { testServerAllowChunkedContentLength(0, true); } -TEST_P(Http1ServerConnectionImplTest, TestSmugglingAllowChunkedContentLength1) { +TEST_F(Http1ServerConnectionImplTest, TestSmugglingAllowChunkedContentLength1) { // content-length less than POST body size testServerAllowChunkedContentLength(1, true); } -TEST_P(Http1ServerConnectionImplTest, TestSmugglingAllowChunkedContentLength100) { +TEST_F(Http1ServerConnectionImplTest, TestSmugglingAllowChunkedContentLength100) { // content-length greater than POST body size testServerAllowChunkedContentLength(100, true); } -class Http1ClientConnectionImplTest : public Http1CodecTestBase, - public testing::TestWithParam { +class Http1ClientConnectionImplTest : public Http1CodecTestBase { public: - bool testingNewCodec() { return GetParam(); } - void initialize() { - if (testingNewCodec()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_response_headers_count_); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_response_headers_count_); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_response_headers_count_); } void readDisableOnRequestEncoder(RequestEncoder* request_encoder, bool disable) { - if (testingNewCodec()) { - dynamic_cast(request_encoder)->readDisable(disable); - } else { - dynamic_cast(request_encoder)->readDisable(disable); - } + dynamic_cast(request_encoder)->readDisable(disable); } NiceMock connection_; @@ -2023,21 +1954,11 @@ class Http1ClientConnectionImplTest : public Http1CodecTestBase, uint32_t max_response_headers_count_{Http::DEFAULT_MAX_HEADERS_COUNT}; }; -INSTANTIATE_TEST_SUITE_P(Codecs, Http1ClientConnectionImplTest, testing::Bool(), - [](const testing::TestParamInfo& param) { - return param.param ? "New" : "Legacy"; - }); - void Http1ClientConnectionImplTest::testClientAllowChunkedContentLength(uint32_t content_length, bool allow_chunked_length) { codec_settings_.allow_chunked_length_ = allow_chunked_length; - if (testingNewCodec()) { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_response_headers_count_); - } else { - codec_ = std::make_unique( - connection_, http1CodecStats(), callbacks_, codec_settings_, max_response_headers_count_); - } + codec_ = std::make_unique( + connection_, http1CodecStats(), callbacks_, codec_settings_, max_response_headers_count_); NiceMock response_decoder; Http::RequestEncoder& request_encoder = codec_->newStream(response_decoder); @@ -2074,7 +1995,7 @@ void Http1ClientConnectionImplTest::testClientAllowChunkedContentLength(uint32_t }; } -TEST_P(Http1ClientConnectionImplTest, SimpleGet) { +TEST_F(Http1ClientConnectionImplTest, SimpleGet) { initialize(); MockResponseDecoder response_decoder; @@ -2088,7 +2009,7 @@ TEST_P(Http1ClientConnectionImplTest, SimpleGet) { EXPECT_EQ("GET / HTTP/1.1\r\ncontent-length: 0\r\n\r\n", output); } -TEST_P(Http1ClientConnectionImplTest, SimpleGetWithHeaderCasing) { +TEST_F(Http1ClientConnectionImplTest, SimpleGetWithHeaderCasing) { codec_settings_.header_key_format_ = Http1Settings::HeaderKeyFormat::ProperCase; initialize(); @@ -2104,7 +2025,7 @@ TEST_P(Http1ClientConnectionImplTest, SimpleGetWithHeaderCasing) { EXPECT_EQ("GET / HTTP/1.1\r\nMy-Custom-Header: hey\r\nContent-Length: 0\r\n\r\n", output); } -TEST_P(Http1ClientConnectionImplTest, HostHeaderTranslate) { +TEST_F(Http1ClientConnectionImplTest, HostHeaderTranslate) { initialize(); MockResponseDecoder response_decoder; @@ -2118,7 +2039,7 @@ TEST_P(Http1ClientConnectionImplTest, HostHeaderTranslate) { EXPECT_EQ("GET / HTTP/1.1\r\nhost: host\r\ncontent-length: 0\r\n\r\n", output); } -TEST_P(Http1ClientConnectionImplTest, Reset) { +TEST_F(Http1ClientConnectionImplTest, Reset) { initialize(); MockResponseDecoder response_decoder; @@ -2132,7 +2053,7 @@ TEST_P(Http1ClientConnectionImplTest, Reset) { // Verify that we correctly enable reads on the connection when the final response is // received. -TEST_P(Http1ClientConnectionImplTest, FlowControlReadDisabledReenable) { +TEST_F(Http1ClientConnectionImplTest, FlowControlReadDisabledReenable) { initialize(); MockResponseDecoder response_decoder; @@ -2161,7 +2082,7 @@ TEST_P(Http1ClientConnectionImplTest, FlowControlReadDisabledReenable) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, PrematureResponse) { +TEST_F(Http1ClientConnectionImplTest, PrematureResponse) { initialize(); Buffer::OwnedImpl response("HTTP/1.1 408 Request Timeout\r\nConnection: Close\r\n\r\n"); @@ -2169,7 +2090,7 @@ TEST_P(Http1ClientConnectionImplTest, PrematureResponse) { EXPECT_TRUE(isPrematureResponseError(status)); } -TEST_P(Http1ClientConnectionImplTest, EmptyBodyResponse503) { +TEST_F(Http1ClientConnectionImplTest, EmptyBodyResponse503) { initialize(); NiceMock response_decoder; @@ -2183,7 +2104,7 @@ TEST_P(Http1ClientConnectionImplTest, EmptyBodyResponse503) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, EmptyBodyResponse200) { +TEST_F(Http1ClientConnectionImplTest, EmptyBodyResponse200) { initialize(); NiceMock response_decoder; @@ -2197,7 +2118,7 @@ TEST_P(Http1ClientConnectionImplTest, EmptyBodyResponse200) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, HeadRequest) { +TEST_F(Http1ClientConnectionImplTest, HeadRequest) { initialize(); NiceMock response_decoder; @@ -2211,7 +2132,7 @@ TEST_P(Http1ClientConnectionImplTest, HeadRequest) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, 204Response) { +TEST_F(Http1ClientConnectionImplTest, 204Response) { initialize(); NiceMock response_decoder; @@ -2226,7 +2147,7 @@ TEST_P(Http1ClientConnectionImplTest, 204Response) { } // 204 No Content with Content-Length is barred by RFC 7230, Section 3.3.2. -TEST_P(Http1ClientConnectionImplTest, 204ResponseContentLengthNotAllowed) { +TEST_F(Http1ClientConnectionImplTest, 204ResponseContentLengthNotAllowed) { // By default, content-length is barred. { initialize(); @@ -2262,7 +2183,7 @@ TEST_P(Http1ClientConnectionImplTest, 204ResponseContentLengthNotAllowed) { // 204 No Content with Content-Length: 0 is technically barred by RFC 7230, Section 3.3.2, but we // allow it. -TEST_P(Http1ClientConnectionImplTest, 204ResponseWithContentLength0) { +TEST_F(Http1ClientConnectionImplTest, 204ResponseWithContentLength0) { { initialize(); @@ -2296,7 +2217,7 @@ TEST_P(Http1ClientConnectionImplTest, 204ResponseWithContentLength0) { } // 204 No Content with Transfer-Encoding headers is barred by RFC 7230, Section 3.3.1. -TEST_P(Http1ClientConnectionImplTest, 204ResponseTransferEncodingNotAllowed) { +TEST_F(Http1ClientConnectionImplTest, 204ResponseTransferEncodingNotAllowed) { // By default, transfer-encoding is barred. { initialize(); @@ -2331,7 +2252,7 @@ TEST_P(Http1ClientConnectionImplTest, 204ResponseTransferEncodingNotAllowed) { } // 100 response followed by 200 results in a [decode100ContinueHeaders, decodeHeaders] sequence. -TEST_P(Http1ClientConnectionImplTest, ContinueHeaders) { +TEST_F(Http1ClientConnectionImplTest, ContinueHeaders) { initialize(); NiceMock response_decoder; @@ -2353,7 +2274,7 @@ TEST_P(Http1ClientConnectionImplTest, ContinueHeaders) { } // Multiple 100 responses are passed to the response encoder (who is responsible for coalescing). -TEST_P(Http1ClientConnectionImplTest, MultipleContinueHeaders) { +TEST_F(Http1ClientConnectionImplTest, MultipleContinueHeaders) { initialize(); NiceMock response_decoder; @@ -2382,7 +2303,7 @@ TEST_P(Http1ClientConnectionImplTest, MultipleContinueHeaders) { // 101/102 headers etc. are passed to the response encoder (who is responsibly for deciding to // upgrade, ignore, etc.). -TEST_P(Http1ClientConnectionImplTest, 1xxNonContinueHeaders) { +TEST_F(Http1ClientConnectionImplTest, 1xxNonContinueHeaders) { initialize(); NiceMock response_decoder; @@ -2397,7 +2318,7 @@ TEST_P(Http1ClientConnectionImplTest, 1xxNonContinueHeaders) { } // 101 Switching Protocol with Transfer-Encoding headers is barred by RFC 7230, Section 3.3.1. -TEST_P(Http1ClientConnectionImplTest, 101ResponseTransferEncodingNotAllowed) { +TEST_F(Http1ClientConnectionImplTest, 101ResponseTransferEncodingNotAllowed) { // By default, transfer-encoding is barred. { initialize(); @@ -2433,7 +2354,7 @@ TEST_P(Http1ClientConnectionImplTest, 101ResponseTransferEncodingNotAllowed) { } } -TEST_P(Http1ClientConnectionImplTest, BadEncodeParams) { +TEST_F(Http1ClientConnectionImplTest, BadEncodeParams) { initialize(); NiceMock response_decoder; @@ -2445,24 +2366,15 @@ TEST_P(Http1ClientConnectionImplTest, BadEncodeParams) { // old codecs will still throw an exception (that presently will be uncaught in contexts like // sendLocalReply). Http::RequestEncoder& request_encoder = codec_->newStream(response_decoder); - if (testingNewCodec()) { - EXPECT_THAT( - request_encoder.encodeHeaders(TestRequestHeaderMapImpl{{":path", "/"}}, true).message(), - testing::HasSubstr("missing required")); - EXPECT_THAT( - request_encoder.encodeHeaders(TestRequestHeaderMapImpl{{":method", "GET"}}, true).message(), - testing::HasSubstr("missing required")); - } else { - EXPECT_THROW( - request_encoder.encodeHeaders(TestRequestHeaderMapImpl{{":path", "/"}}, true).IgnoreError(), - CodecClientException); - EXPECT_THROW(request_encoder.encodeHeaders(TestRequestHeaderMapImpl{{":method", "GET"}}, true) - .IgnoreError(), - CodecClientException); - } + EXPECT_THAT( + request_encoder.encodeHeaders(TestRequestHeaderMapImpl{{":path", "/"}}, true).message(), + testing::HasSubstr("missing required")); + EXPECT_THAT( + request_encoder.encodeHeaders(TestRequestHeaderMapImpl{{":method", "GET"}}, true).message(), + testing::HasSubstr("missing required")); } -TEST_P(Http1ClientConnectionImplTest, NoContentLengthResponse) { +TEST_F(Http1ClientConnectionImplTest, NoContentLengthResponse) { initialize(); NiceMock response_decoder; @@ -2484,7 +2396,7 @@ TEST_P(Http1ClientConnectionImplTest, NoContentLengthResponse) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, ResponseWithTrailers) { +TEST_F(Http1ClientConnectionImplTest, ResponseWithTrailers) { initialize(); NiceMock response_decoder; @@ -2499,7 +2411,7 @@ TEST_P(Http1ClientConnectionImplTest, ResponseWithTrailers) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, GiantPath) { +TEST_F(Http1ClientConnectionImplTest, GiantPath) { initialize(); NiceMock response_decoder; @@ -2514,7 +2426,7 @@ TEST_P(Http1ClientConnectionImplTest, GiantPath) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, PrematureUpgradeResponse) { +TEST_F(Http1ClientConnectionImplTest, PrematureUpgradeResponse) { initialize(); // make sure upgradeAllowed doesn't cause crashes if run with no pending response. @@ -2524,7 +2436,7 @@ TEST_P(Http1ClientConnectionImplTest, PrematureUpgradeResponse) { EXPECT_TRUE(isPrematureResponseError(status)); } -TEST_P(Http1ClientConnectionImplTest, UpgradeResponse) { +TEST_F(Http1ClientConnectionImplTest, UpgradeResponse) { initialize(); InSequence s; @@ -2560,7 +2472,7 @@ TEST_P(Http1ClientConnectionImplTest, UpgradeResponse) { // Same data as above, but make sure directDispatch immediately hands off any // outstanding data. -TEST_P(Http1ClientConnectionImplTest, UpgradeResponseWithEarlyData) { +TEST_F(Http1ClientConnectionImplTest, UpgradeResponseWithEarlyData) { initialize(); InSequence s; @@ -2584,7 +2496,7 @@ TEST_P(Http1ClientConnectionImplTest, UpgradeResponseWithEarlyData) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, ConnectResponse) { +TEST_F(Http1ClientConnectionImplTest, ConnectResponse) { initialize(); InSequence s; @@ -2615,7 +2527,7 @@ TEST_P(Http1ClientConnectionImplTest, ConnectResponse) { // Same data as above, but make sure directDispatch immediately hands off any // outstanding data. -TEST_P(Http1ClientConnectionImplTest, ConnectResponseWithEarlyData) { +TEST_F(Http1ClientConnectionImplTest, ConnectResponseWithEarlyData) { initialize(); InSequence s; @@ -2634,7 +2546,7 @@ TEST_P(Http1ClientConnectionImplTest, ConnectResponseWithEarlyData) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, ConnectRejected) { +TEST_F(Http1ClientConnectionImplTest, ConnectRejected) { initialize(); InSequence s; @@ -2652,7 +2564,7 @@ TEST_P(Http1ClientConnectionImplTest, ConnectRejected) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ClientConnectionImplTest, WatermarkTest) { +TEST_F(Http1ClientConnectionImplTest, WatermarkTest) { EXPECT_CALL(connection_, bufferLimit()).WillOnce(Return(10)); initialize(); @@ -2687,7 +2599,7 @@ TEST_P(Http1ClientConnectionImplTest, WatermarkTest) { // caller attempts to close the connection. This causes the network connection to attempt to write // pending data, even in the no flush scenario, which can cause us to go below low watermark // which then raises callbacks for a stream that no longer exists. -TEST_P(Http1ClientConnectionImplTest, HighwatermarkMultipleResponses) { +TEST_F(Http1ClientConnectionImplTest, HighwatermarkMultipleResponses) { initialize(); InSequence s; @@ -2721,7 +2633,7 @@ TEST_P(Http1ClientConnectionImplTest, HighwatermarkMultipleResponses) { // Regression test for https://github.com/envoyproxy/envoy/issues/10655. Make sure we correctly // handle going below low watermark when closing the connection during a completion callback. -TEST_P(Http1ClientConnectionImplTest, LowWatermarkDuringClose) { +TEST_F(Http1ClientConnectionImplTest, LowWatermarkDuringClose) { initialize(); InSequence s; @@ -2751,43 +2663,43 @@ TEST_P(Http1ClientConnectionImplTest, LowWatermarkDuringClose) { EXPECT_TRUE(status.ok()); } -TEST_P(Http1ServerConnectionImplTest, LargeTrailersRejected) { +TEST_F(Http1ServerConnectionImplTest, LargeTrailersRejected) { // Default limit of 60 KiB std::string long_string = "big: " + std::string(60 * 1024, 'q') + "\r\n\r\n\r\n"; testTrailersExceedLimit(long_string, true); } -TEST_P(Http1ServerConnectionImplTest, LargeTrailerFieldRejected) { +TEST_F(Http1ServerConnectionImplTest, LargeTrailerFieldRejected) { // Construct partial headers with a long field name that exceeds the default limit of 60KiB. std::string long_string = "bigfield" + std::string(60 * 1024, 'q'); testTrailersExceedLimit(long_string, true); } // Tests that the default limit for the number of request headers is 100. -TEST_P(Http1ServerConnectionImplTest, ManyTrailersRejected) { +TEST_F(Http1ServerConnectionImplTest, ManyTrailersRejected) { // Send a request with 101 headers. testTrailersExceedLimit(createHeaderFragment(101) + "\r\n\r\n", true); } -TEST_P(Http1ServerConnectionImplTest, LargeTrailersRejectedIgnored) { +TEST_F(Http1ServerConnectionImplTest, LargeTrailersRejectedIgnored) { // Default limit of 60 KiB std::string long_string = "big: " + std::string(60 * 1024, 'q') + "\r\n\r\n\r\n"; testTrailersExceedLimit(long_string, false); } -TEST_P(Http1ServerConnectionImplTest, LargeTrailerFieldRejectedIgnored) { +TEST_F(Http1ServerConnectionImplTest, LargeTrailerFieldRejectedIgnored) { // Default limit of 60 KiB std::string long_string = "bigfield" + std::string(60 * 1024, 'q') + ": value\r\n\r\n\r\n"; testTrailersExceedLimit(long_string, false); } // Tests that the default limit for the number of request headers is 100. -TEST_P(Http1ServerConnectionImplTest, ManyTrailersIgnored) { +TEST_F(Http1ServerConnectionImplTest, ManyTrailersIgnored) { // Send a request with 101 headers. testTrailersExceedLimit(createHeaderFragment(101) + "\r\n\r\n", false); } -TEST_P(Http1ServerConnectionImplTest, LargeRequestUrlRejected) { +TEST_F(Http1ServerConnectionImplTest, LargeRequestUrlRejected) { initialize(); std::string exception_reason; @@ -2809,19 +2721,19 @@ TEST_P(Http1ServerConnectionImplTest, LargeRequestUrlRejected) { EXPECT_EQ("http1.headers_too_large", response_encoder->getStream().responseDetails()); } -TEST_P(Http1ServerConnectionImplTest, LargeRequestHeadersRejected) { +TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersRejected) { // Default limit of 60 KiB std::string long_string = "big: " + std::string(60 * 1024, 'q') + "\r\n"; testRequestHeadersExceedLimit(long_string, ""); } // Tests that the default limit for the number of request headers is 100. -TEST_P(Http1ServerConnectionImplTest, ManyRequestHeadersRejected) { +TEST_F(Http1ServerConnectionImplTest, ManyRequestHeadersRejected) { // Send a request with 101 headers. testRequestHeadersExceedLimit(createHeaderFragment(101), "http1.too_many_headers"); } -TEST_P(Http1ServerConnectionImplTest, LargeRequestHeadersSplitRejected) { +TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersSplitRejected) { // Default limit of 60 KiB initialize(); @@ -2852,7 +2764,7 @@ TEST_P(Http1ServerConnectionImplTest, LargeRequestHeadersSplitRejected) { // Tests that the 101th request header causes overflow with the default max number of request // headers. -TEST_P(Http1ServerConnectionImplTest, ManyRequestHeadersSplitRejected) { +TEST_F(Http1ServerConnectionImplTest, ManyRequestHeadersSplitRejected) { // Default limit of 100. initialize(); @@ -2879,27 +2791,27 @@ TEST_P(Http1ServerConnectionImplTest, ManyRequestHeadersSplitRejected) { EXPECT_EQ(status.message(), "headers size exceeds limit"); } -TEST_P(Http1ServerConnectionImplTest, LargeRequestHeadersAccepted) { +TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersAccepted) { max_request_headers_kb_ = 65; std::string long_string = "big: " + std::string(64 * 1024, 'q') + "\r\n"; testRequestHeadersAccepted(long_string); } -TEST_P(Http1ServerConnectionImplTest, LargeRequestHeadersAcceptedMaxConfigurable) { +TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersAcceptedMaxConfigurable) { max_request_headers_kb_ = 96; std::string long_string = "big: " + std::string(95 * 1024, 'q') + "\r\n"; testRequestHeadersAccepted(long_string); } // Tests that the number of request headers is configurable. -TEST_P(Http1ServerConnectionImplTest, ManyRequestHeadersAccepted) { +TEST_F(Http1ServerConnectionImplTest, ManyRequestHeadersAccepted) { max_request_headers_count_ = 150; // Create a request with 150 headers. testRequestHeadersAccepted(createHeaderFragment(150)); } // Tests that incomplete response headers of 80 kB header value fails. -TEST_P(Http1ClientConnectionImplTest, ResponseHeadersWithLargeValueRejected) { +TEST_F(Http1ClientConnectionImplTest, ResponseHeadersWithLargeValueRejected) { initialize(); NiceMock response_decoder; @@ -2918,7 +2830,7 @@ TEST_P(Http1ClientConnectionImplTest, ResponseHeadersWithLargeValueRejected) { } // Tests that incomplete response headers with a 80 kB header field fails. -TEST_P(Http1ClientConnectionImplTest, ResponseHeadersWithLargeFieldRejected) { +TEST_F(Http1ClientConnectionImplTest, ResponseHeadersWithLargeFieldRejected) { initialize(); NiceMock decoder; @@ -2938,7 +2850,7 @@ TEST_P(Http1ClientConnectionImplTest, ResponseHeadersWithLargeFieldRejected) { } // Tests that the size of response headers for HTTP/1 must be under 80 kB. -TEST_P(Http1ClientConnectionImplTest, LargeResponseHeadersAccepted) { +TEST_F(Http1ClientConnectionImplTest, LargeResponseHeadersAccepted) { initialize(); NiceMock response_decoder; @@ -2956,7 +2868,7 @@ TEST_P(Http1ClientConnectionImplTest, LargeResponseHeadersAccepted) { // Regression test for CVE-2019-18801. Large method headers should not trigger // ASSERTs or ASAN, which they previously did. -TEST_P(Http1ClientConnectionImplTest, LargeMethodRequestEncode) { +TEST_F(Http1ClientConnectionImplTest, LargeMethodRequestEncode) { initialize(); NiceMock response_decoder; @@ -2974,7 +2886,7 @@ TEST_P(Http1ClientConnectionImplTest, LargeMethodRequestEncode) { // in CVE-2019-18801, but the related code does explicit size calculations on // both path and method (these are the two distinguished headers). So, // belt-and-braces. -TEST_P(Http1ClientConnectionImplTest, LargePathRequestEncode) { +TEST_F(Http1ClientConnectionImplTest, LargePathRequestEncode) { initialize(); NiceMock response_decoder; @@ -2990,7 +2902,7 @@ TEST_P(Http1ClientConnectionImplTest, LargePathRequestEncode) { // As with LargeMethodEncode, but for an arbitrary header. This was not an issue // in CVE-2019-18801. -TEST_P(Http1ClientConnectionImplTest, LargeHeaderRequestEncode) { +TEST_F(Http1ClientConnectionImplTest, LargeHeaderRequestEncode) { initialize(); NiceMock response_decoder; @@ -3007,7 +2919,7 @@ TEST_P(Http1ClientConnectionImplTest, LargeHeaderRequestEncode) { } // Exception called when the number of response headers exceeds the default value of 100. -TEST_P(Http1ClientConnectionImplTest, ManyResponseHeadersRejected) { +TEST_F(Http1ClientConnectionImplTest, ManyResponseHeadersRejected) { initialize(); NiceMock response_decoder; @@ -3025,7 +2937,7 @@ TEST_P(Http1ClientConnectionImplTest, ManyResponseHeadersRejected) { } // Tests that the number of response headers is configurable. -TEST_P(Http1ClientConnectionImplTest, ManyResponseHeadersAccepted) { +TEST_F(Http1ClientConnectionImplTest, ManyResponseHeadersAccepted) { max_response_headers_count_ = 152; initialize(); @@ -3042,27 +2954,27 @@ TEST_P(Http1ClientConnectionImplTest, ManyResponseHeadersAccepted) { status = codec_->dispatch(buffer); } -TEST_P(Http1ClientConnectionImplTest, TestResponseSplit0) { +TEST_F(Http1ClientConnectionImplTest, TestResponseSplit0) { testClientAllowChunkedContentLength(0, false); } -TEST_P(Http1ClientConnectionImplTest, TestResponseSplit1) { +TEST_F(Http1ClientConnectionImplTest, TestResponseSplit1) { testClientAllowChunkedContentLength(1, false); } -TEST_P(Http1ClientConnectionImplTest, TestResponseSplit100) { +TEST_F(Http1ClientConnectionImplTest, TestResponseSplit100) { testClientAllowChunkedContentLength(100, false); } -TEST_P(Http1ClientConnectionImplTest, TestResponseSplitAllowChunkedLength0) { +TEST_F(Http1ClientConnectionImplTest, TestResponseSplitAllowChunkedLength0) { testClientAllowChunkedContentLength(0, true); } -TEST_P(Http1ClientConnectionImplTest, TestResponseSplitAllowChunkedLength1) { +TEST_F(Http1ClientConnectionImplTest, TestResponseSplitAllowChunkedLength1) { testClientAllowChunkedContentLength(1, true); } -TEST_P(Http1ClientConnectionImplTest, TestResponseSplitAllowChunkedLength100) { +TEST_F(Http1ClientConnectionImplTest, TestResponseSplitAllowChunkedLength100) { testClientAllowChunkedContentLength(100, true); } diff --git a/test/common/http/http2/BUILD b/test/common/http/http2/BUILD index 8b95de6b6cf4..8dca77e677b5 100644 --- a/test/common/http/http2/BUILD +++ b/test/common/http/http2/BUILD @@ -10,49 +10,35 @@ licenses(["notice"]) # Apache 2 envoy_package() -CODEC_TEST_DEPS = [ - ":codec_impl_test_util", - "//source/common/event:dispatcher_lib", - "//source/common/http:exception_lib", - "//source/common/http:header_map_lib", - "//source/common/http:header_utility_lib", - "//source/common/http/http2:codec_legacy_lib", - "//source/common/http/http2:codec_lib", - "//source/common/runtime:runtime_lib", - "//source/common/stats:stats_lib", - "//test/common/http:common_lib", - "//test/common/http/http2:http2_frame", - "//test/common/stats:stat_test_utility_lib", - "//test/mocks/http:http_mocks", - "//test/mocks/init:init_mocks", - "//test/mocks/local_info:local_info_mocks", - "//test/mocks/network:network_mocks", - "//test/mocks/protobuf:protobuf_mocks", - "//test/mocks/thread_local:thread_local_mocks", - "//test/mocks/upstream:transport_socket_match_mocks", - "//test/mocks/upstream:upstream_mocks", - "//test/test_common:logging_lib", - "//test/test_common:registry_lib", - "//test/test_common:test_runtime_lib", - "//test/test_common:utility_lib", -] - envoy_cc_test( name = "codec_impl_test", srcs = ["codec_impl_test.cc"], shard_count = 5, - deps = CODEC_TEST_DEPS, -) - -envoy_cc_test( - name = "codec_impl_legacy_test", - srcs = ["codec_impl_test.cc"], - # The default codec is the new codec. Disable runtime flag for testing old codec. - args = [ - "--runtime-feature-disable-for-tests=envoy.reloadable_features.new_codec_behavior", + deps = [ + ":codec_impl_test_util", + "//source/common/event:dispatcher_lib", + "//source/common/http:exception_lib", + "//source/common/http:header_map_lib", + "//source/common/http:header_utility_lib", + "//source/common/http/http2:codec_lib", + "//source/common/runtime:runtime_lib", + "//source/common/stats:stats_lib", + "//test/common/http:common_lib", + "//test/common/http/http2:http2_frame", + "//test/common/stats:stat_test_utility_lib", + "//test/mocks/http:http_mocks", + "//test/mocks/init:init_mocks", + "//test/mocks/local_info:local_info_mocks", + "//test/mocks/network:network_mocks", + "//test/mocks/protobuf:protobuf_mocks", + "//test/mocks/thread_local:thread_local_mocks", + "//test/mocks/upstream:transport_socket_match_mocks", + "//test/mocks/upstream:upstream_mocks", + "//test/test_common:logging_lib", + "//test/test_common:registry_lib", + "//test/test_common:test_runtime_lib", + "//test/test_common:utility_lib", ], - shard_count = 5, - deps = CODEC_TEST_DEPS, ) envoy_cc_test_library( @@ -60,8 +46,8 @@ envoy_cc_test_library( hdrs = ["codec_impl_test_util.h"], external_deps = ["abseil_optional"], deps = [ - "//source/common/http/http2:codec_legacy_lib", "//source/common/http/http2:codec_lib", + "//test/mocks:common_lib", ], ) @@ -112,6 +98,7 @@ envoy_cc_test_library( "//source/common/http:utility_lib", "//source/common/http/http2:codec_lib", "//test/common/http:common_lib", + "//test/mocks:common_lib", "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", "//test/test_common:environment_lib", diff --git a/test/common/http/http2/codec_impl_test.cc b/test/common/http/http2/codec_impl_test.cc index dc266bfa68dd..a8ab0eaf146d 100644 --- a/test/common/http/http2/codec_impl_test.cc +++ b/test/common/http/http2/codec_impl_test.cc @@ -7,7 +7,6 @@ #include "common/http/exception.h" #include "common/http/header_map_impl.h" #include "common/http/http2/codec_impl.h" -#include "common/runtime/runtime_features.h" #include "test/common/http/common.h" #include "test/common/http/http2/http2_frame.h" @@ -74,7 +73,7 @@ class Http2CodecImplTestFixture { }; struct ConnectionWrapper { - Http::Status dispatch(const Buffer::Instance& data, Connection& connection) { + Http::Status dispatch(const Buffer::Instance& data, ConnectionImpl& connection) { Http::Status status = Http::okStatus(); buffer_.add(data); if (!dispatching_) { @@ -129,23 +128,12 @@ class Http2CodecImplTestFixture { virtual void initialize() { http2OptionsFromTuple(client_http2_options_, client_settings_); http2OptionsFromTuple(server_http2_options_, server_settings_); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - client_ = std::make_unique( - client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, - max_request_headers_kb_, max_response_headers_count_, - ProdNghttp2SessionFactoryNew::get()); - server_ = std::make_unique( - server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, - max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); - } else { - client_ = std::make_unique( - client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, - max_request_headers_kb_, max_response_headers_count_, - ProdNghttp2SessionFactoryLegacy::get()); - server_ = std::make_unique( - server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, - max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); - } + client_ = std::make_unique( + client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, random_, + max_request_headers_kb_, max_response_headers_count_, ProdNghttp2SessionFactory::get()); + server_ = std::make_unique( + server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, random_, + max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); request_encoder_ = &client_->newStream(response_decoder_); setupDefaultConnectionMocks(); @@ -240,13 +228,14 @@ class Http2CodecImplTestFixture { envoy::config::core::v3::Http2ProtocolOptions client_http2_options_; NiceMock client_connection_; MockConnectionCallbacks client_callbacks_; - std::unique_ptr client_; + std::unique_ptr client_; ConnectionWrapper client_wrapper_; Stats::TestUtil::TestStore server_stats_store_; envoy::config::core::v3::Http2ProtocolOptions server_http2_options_; + NiceMock random_; NiceMock server_connection_; MockServerConnectionCallbacks server_callbacks_; - std::unique_ptr server_; + std::unique_ptr server_; ConnectionWrapper server_wrapper_; MockResponseDecoder response_decoder_; RequestEncoder* request_encoder_; @@ -648,16 +637,10 @@ TEST_P(Http2CodecImplTest, RefusedStreamReset) { TEST_P(Http2CodecImplTest, InvalidHeadersFrame) { initialize(); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - const auto status = request_encoder_->encodeHeaders(TestRequestHeaderMapImpl{}, true); + const auto status = request_encoder_->encodeHeaders(TestRequestHeaderMapImpl{}, true); - EXPECT_FALSE(status.ok()); - EXPECT_THAT(status.message(), testing::HasSubstr("missing required")); - } else { - EXPECT_THROW(request_encoder_->encodeHeaders(TestRequestHeaderMapImpl{}, true).IgnoreError(), - ServerCodecError); - EXPECT_EQ(1, server_stats_store_.counter("http2.rx_messaging_error").value()); - } + EXPECT_FALSE(status.ok()); + EXPECT_THAT(status.message(), testing::HasSubstr("missing required")); } TEST_P(Http2CodecImplTest, TrailingHeaders) { @@ -956,7 +939,7 @@ TEST_P(Http2CodecImplTest, ConnectionKeepaliveJitter) { initialize(); for (uint64_t i = 0; i < 250; i++) { - EXPECT_CALL(client_->random_generator_, random()).WillOnce(Return(i)); + EXPECT_CALL(random_, random()).WillOnce(Return(i)); send_timer->callback_(); } @@ -1084,21 +1067,21 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { // stream. EXPECT_EQ(0, nghttp2_session_get_stream_local_window_size(server_->session(), 1)); EXPECT_EQ(0, nghttp2_session_get_stream_remote_window_size(client_->session(), 1)); - EXPECT_EQ(initial_stream_window, server_->getStreamUnconsumedBytes(1)); + EXPECT_EQ(initial_stream_window, server_->getStream(1)->unconsumed_bytes_); // Now that the flow control window is full, further data causes the send buffer to back up. Buffer::OwnedImpl more_long_data(std::string(initial_stream_window, 'a')); request_encoder_->encodeData(more_long_data, false); - EXPECT_EQ(initial_stream_window, client_->getStreamPendingSendDataLength(1)); + EXPECT_EQ(initial_stream_window, client_->getStream(1)->pending_send_data_.length()); EXPECT_EQ(initial_stream_window, TestUtility::findGauge(client_stats_store_, "http2.pending_send_bytes")->value()); - EXPECT_EQ(initial_stream_window, server_->getStreamUnconsumedBytes(1)); + EXPECT_EQ(initial_stream_window, server_->getStream(1)->unconsumed_bytes_); // If we go over the limit, the stream callbacks should fire. EXPECT_CALL(callbacks, onAboveWriteBufferHighWatermark()); Buffer::OwnedImpl last_byte("!"); request_encoder_->encodeData(last_byte, false); - EXPECT_EQ(initial_stream_window + 1, client_->getStreamPendingSendDataLength(1)); + EXPECT_EQ(initial_stream_window + 1, client_->getStream(1)->pending_send_data_.length()); EXPECT_EQ(initial_stream_window + 1, TestUtility::findGauge(client_stats_store_, "http2.pending_send_bytes")->value()); @@ -1143,7 +1126,7 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { EXPECT_CALL(callbacks2, onBelowWriteBufferLowWatermark()).Times(0); EXPECT_CALL(callbacks3, onBelowWriteBufferLowWatermark()); server_->getStream(1)->readDisable(false); - EXPECT_EQ(0, client_->getStreamPendingSendDataLength(1)); + EXPECT_EQ(0, client_->getStream(1)->pending_send_data_.length()); EXPECT_EQ(0, TestUtility::findGauge(client_stats_store_, "http2.pending_send_bytes")->value()); // The extra 1 byte sent won't trigger another window update, so the final window should be the // initial window minus the last 1 byte flush from the client to server. @@ -1188,7 +1171,7 @@ TEST_P(Http2CodecImplFlowControlTest, EarlyResetRestoresWindow) { // stream. EXPECT_EQ(0, nghttp2_session_get_stream_local_window_size(server_->session(), 1)); EXPECT_EQ(0, nghttp2_session_get_stream_remote_window_size(client_->session(), 1)); - EXPECT_EQ(initial_stream_window, server_->getStreamUnconsumedBytes(1)); + EXPECT_EQ(initial_stream_window, server_->getStream(1)->unconsumed_bytes_); EXPECT_GT(initial_connection_window, nghttp2_session_get_remote_window_size(client_->session())); EXPECT_CALL(server_stream_callbacks_, @@ -1230,7 +1213,7 @@ TEST_P(Http2CodecImplFlowControlTest, FlowControlPendingRecvData) { // the recv buffer can be overrun by a client which negotiates a larger // SETTINGS_MAX_FRAME_SIZE but there's no current easy way to tweak that in // envoy (without sending raw HTTP/2 frames) so we lower the buffer limit instead. - server_->setStreamWriteBufferWatermarks(1, 10, 20); + server_->getStream(1)->setWriteBufferWatermarks(10, 20); EXPECT_CALL(request_decoder_, decodeData(_, false)); Buffer::OwnedImpl data(std::string(40, 'a')); @@ -1416,7 +1399,7 @@ TEST_P(Http2CodecImplFlowControlTest, WindowUpdateOnReadResumingFlood) { Buffer::OwnedImpl long_data(std::string(initial_stream_window / 2, 'a')); request_encoder_->encodeData(long_data, false); - EXPECT_EQ(initial_stream_window / 2, server_->getStreamUnconsumedBytes(1)); + EXPECT_EQ(initial_stream_window / 2, server_->getStream(1)->unconsumed_bytes_); // pre-fill downstream outbound frame queue TestResponseHeaderMapImpl response_headers{{":status", "200"}}; @@ -1547,23 +1530,12 @@ class Http2CodecImplStreamLimitTest : public Http2CodecImplTest {}; TEST_P(Http2CodecImplStreamLimitTest, MaxClientStreams) { http2OptionsFromTuple(client_http2_options_, ::testing::get<0>(GetParam())); http2OptionsFromTuple(server_http2_options_, ::testing::get<1>(GetParam())); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - client_ = std::make_unique( - client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, - max_request_headers_kb_, max_response_headers_count_, ProdNghttp2SessionFactoryNew::get()); - server_ = std::make_unique( - server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, - max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); - - } else { - client_ = std::make_unique( - client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, - max_request_headers_kb_, max_response_headers_count_, - ProdNghttp2SessionFactoryLegacy::get()); - server_ = std::make_unique( - server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, - max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); - } + client_ = std::make_unique( + client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, random_, + max_request_headers_kb_, max_response_headers_count_, ProdNghttp2SessionFactory::get()); + server_ = std::make_unique( + server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, random_, + max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); for (int i = 0; i < 101; ++i) { request_encoder_ = &client_->newStream(response_decoder_); setupDefaultConnectionMocks(); @@ -2070,12 +2042,8 @@ TEST_P(Http2CodecImplTest, PingFlood) { buffer.move(frame); })); - // Legacy codec does not propagate error details and uses generic error message - EXPECT_THROW_WITH_MESSAGE( - client_->sendPendingFrames().IgnoreError(), ServerCodecError, - Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior") - ? "Too many control frames in the outbound queue." - : "Too many frames in the outbound queue."); + EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, + "Too many control frames in the outbound queue."); EXPECT_EQ(1, server_stats_store_.counter("http2.outbound_control_flood").value()); } @@ -2144,12 +2112,8 @@ TEST_P(Http2CodecImplTest, PingFloodCounterReset) { // 1 more ping frame should overflow the outbound frame limit. EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); - // Legacy codec does not propagate error details and uses generic error message - EXPECT_THROW_WITH_MESSAGE( - client_->sendPendingFrames().IgnoreError(), ServerCodecError, - Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior") - ? "Too many control frames in the outbound queue." - : "Too many frames in the outbound queue."); + EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, + "Too many control frames in the outbound queue."); } // Verify that codec detects flood of outbound HEADER frames @@ -2415,12 +2379,8 @@ TEST_P(Http2CodecImplTest, MetadataFlood) { TEST_P(Http2CodecImplTest, PriorityFlood) { priorityFlood(); - // Legacy codec does not propagate error details and uses generic error message - EXPECT_THROW_WITH_MESSAGE( - client_->sendPendingFrames().IgnoreError(), ServerCodecError, - Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior") - ? "Too many PRIORITY frames" - : "Flooding was detected in this HTTP/2 session, and it must be closed"); + EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, + "Too many PRIORITY frames"); } TEST_P(Http2CodecImplTest, PriorityFloodOverride) { @@ -2432,12 +2392,8 @@ TEST_P(Http2CodecImplTest, PriorityFloodOverride) { TEST_P(Http2CodecImplTest, WindowUpdateFlood) { windowUpdateFlood(); - // Legacy codec does not propagate error details and uses generic error message - EXPECT_THROW_WITH_MESSAGE( - client_->sendPendingFrames().IgnoreError(), ServerCodecError, - Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior") - ? "Too many WINDOW_UPDATE frames" - : "Flooding was detected in this HTTP/2 session, and it must be closed"); + EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, + "Too many WINDOW_UPDATE frames"); } TEST_P(Http2CodecImplTest, WindowUpdateFloodOverride) { @@ -2452,15 +2408,8 @@ TEST_P(Http2CodecImplTest, EmptyDataFlood) { EXPECT_CALL(request_decoder_, decodeData(_, false)); auto status = server_wrapper_.dispatch(data, *server_); EXPECT_FALSE(status.ok()); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - EXPECT_TRUE(isInboundFramesWithEmptyPayloadError(status)); - EXPECT_EQ("Too many consecutive frames with an empty payload", status.message()); - } else { - // Legacy codec does not propagate error details and uses generic error message - EXPECT_TRUE(isBufferFloodError(status)); - EXPECT_EQ("Flooding was detected in this HTTP/2 session, and it must be closed", - status.message()); - } + EXPECT_TRUE(isInboundFramesWithEmptyPayloadError(status)); + EXPECT_EQ("Too many consecutive frames with an empty payload", status.message()); } TEST_P(Http2CodecImplTest, EmptyDataFloodOverride) { @@ -2600,10 +2549,6 @@ TEST_P(Http2CodecImplTest, KeepAliveCausesOutboundFlood) { // Trigger sending a PING, which should overflow the outbound frame queue and cause // client to be disconnected - if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - // new codec does not schedule timeout callback if the PING had triggered flood protection - EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(timeout_ms), _)); - } send_timer->callback_(); EXPECT_TRUE(violation_callback->enabled_); @@ -2679,67 +2624,78 @@ TEST_P(Http2CodecImplTest, ConnectTest) { response_encoder_->getStream().resetStream(StreamResetReason::ConnectError); } -template class TestNghttp2SessionFactory; +class TestNghttp2SessionFactory; // Test client for H/2 METADATA frame edge cases. -template -class MetadataTestClientConnectionImpl : public TestClientConnectionImplType { +class MetadataTestClientConnectionImpl : public TestClientConnectionImpl { public: MetadataTestClientConnectionImpl( Network::Connection& connection, Http::ConnectionCallbacks& callbacks, Stats::Scope& scope, const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - uint32_t max_request_headers_kb, uint32_t max_request_headers_count, - typename TestClientConnectionImplType::SessionFactory& http2_session_factory) - : TestClientConnectionImplType(connection, callbacks, scope, http2_options, - max_request_headers_kb, max_request_headers_count, - http2_session_factory) {} + Random::RandomGenerator& random, uint32_t max_request_headers_kb, + uint32_t max_request_headers_count, Nghttp2SessionFactory& http2_session_factory) + : TestClientConnectionImpl(connection, callbacks, scope, http2_options, random, + max_request_headers_kb, max_request_headers_count, + http2_session_factory) {} // Overrides TestClientConnectionImpl::submitMetadata(). bool submitMetadata(const MetadataMapVector& metadata_map_vector, int32_t stream_id) override { // Creates metadata payload. encoder_.createPayload(metadata_map_vector); for (uint8_t flags : encoder_.payloadFrameFlagBytes()) { - int result = - nghttp2_submit_extension(TestClientConnectionImplType::session(), - ::Envoy::Http::METADATA_FRAME_TYPE, flags, stream_id, nullptr); + int result = nghttp2_submit_extension(session(), ::Envoy::Http::METADATA_FRAME_TYPE, flags, + stream_id, nullptr); if (result != 0) { return false; } } // Triggers nghttp2 to populate the payloads of the METADATA frames. - int result = nghttp2_session_send(TestClientConnectionImplType::session()); + int result = nghttp2_session_send(session()); return result == 0; } protected: - template friend class TestNghttp2SessionFactory; + friend class TestNghttp2SessionFactory; MetadataEncoder encoder_; }; -using MetadataTestClientConnectionImplNew = - MetadataTestClientConnectionImpl; -using MetadataTestClientConnectionImplLegacy = - MetadataTestClientConnectionImpl; - -struct Nghttp2SessionFactoryDeleter { - virtual ~Nghttp2SessionFactoryDeleter() = default; -}; - -template -class TestNghttp2SessionFactory : public Nghttp2SessionFactoryType, - public Nghttp2SessionFactoryDeleter { +class TestNghttp2SessionFactory : public Nghttp2SessionFactory { public: ~TestNghttp2SessionFactory() override { nghttp2_session_callbacks_del(callbacks_); nghttp2_option_del(options_); } - nghttp2_session* create(const nghttp2_session_callbacks*, - typename Nghttp2SessionFactoryType::ConnectionImplType* connection, - const nghttp2_option*) override; + nghttp2_session* create(const nghttp2_session_callbacks*, ConnectionImpl* connection, + const nghttp2_option*) override { + // Only need to provide callbacks required to send METADATA frames. + nghttp2_session_callbacks_new(&callbacks_); + nghttp2_session_callbacks_set_pack_extension_callback( + callbacks_, + [](nghttp2_session*, uint8_t* data, size_t length, const nghttp2_frame*, + void* user_data) -> ssize_t { + // Double cast required due to multiple inheritance. + return static_cast( + static_cast(user_data)) + ->encoder_.packNextFramePayload(data, length); + }); + nghttp2_session_callbacks_set_send_callback( + callbacks_, + [](nghttp2_session*, const uint8_t* data, size_t length, int, void* user_data) -> ssize_t { + // Cast down to MetadataTestClientConnectionImpl to leverage friendship. + return static_cast( + static_cast(user_data)) + ->onSend(data, length); + }); + nghttp2_option_new(&options_); + nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE); + nghttp2_session* session; + nghttp2_session_client_new2(&session, callbacks_, connection, options_); + return session; + } - void init(nghttp2_session*, typename Nghttp2SessionFactoryType::ConnectionImplType*, + void init(nghttp2_session*, ConnectionImpl*, const envoy::config::core::v3::Http2ProtocolOptions&) override {} private: @@ -2747,78 +2703,6 @@ class TestNghttp2SessionFactory : public Nghttp2SessionFactoryType, nghttp2_option* options_; }; -template -nghttp2_session* -TestNghttp2SessionFactory::create( - const nghttp2_session_callbacks*, - typename Nghttp2SessionFactoryType::ConnectionImplType* connection, const nghttp2_option*) { - // Only need to provide callbacks required to send METADATA frames. - nghttp2_session_callbacks_new(&callbacks_); - nghttp2_session_callbacks_set_pack_extension_callback( - callbacks_, - [](nghttp2_session*, uint8_t* data, size_t length, const nghttp2_frame*, - void* user_data) -> ssize_t { - // Double cast required due to multiple inheritance. - return static_cast*>( - static_cast(user_data)) - ->encoder_.packNextFramePayload(data, length); - }); - nghttp2_session_callbacks_set_send_callback( - callbacks_, - [](nghttp2_session*, const uint8_t* data, size_t length, int, void* user_data) -> ssize_t { - // Cast down to MetadataTestClientConnectionImpl to leverage friendship. - return static_cast*>( - static_cast(user_data)) - ->onSend(data, length); - }); - nghttp2_option_new(&options_); - nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE); - nghttp2_session* session; - nghttp2_session_client_new2(&session, callbacks_, connection, options_); - return session; -} - -template <> -nghttp2_session* TestNghttp2SessionFactory:: - create(const nghttp2_session_callbacks*, - Envoy::Http::Legacy::Http2::ProdNghttp2SessionFactory::ConnectionImplType* connection, - const nghttp2_option*) { - // Only need to provide callbacks required to send METADATA frames. - nghttp2_session_callbacks_new(&callbacks_); - nghttp2_session_callbacks_set_pack_extension_callback( - callbacks_, - [](nghttp2_session*, uint8_t* data, size_t length, const nghttp2_frame*, - void* user_data) -> ssize_t { - // Double cast required due to multiple inheritance. - return static_cast*>( - static_cast< - Envoy::Http::Legacy::Http2::ProdNghttp2SessionFactory::ConnectionImplType*>( - user_data)) - ->encoder_.packNextFramePayload(data, length); - }); - nghttp2_session_callbacks_set_send_callback( - callbacks_, - [](nghttp2_session*, const uint8_t* data, size_t length, int, void* user_data) -> ssize_t { - // Cast down to MetadataTestClientConnectionImpl to leverage friendship. - return static_cast*>( - static_cast(user_data)) - ->onSend(data, length); - }); - nghttp2_option_new(&options_); - nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE); - nghttp2_session* session; - nghttp2_session_client_new2(&session, callbacks_, connection, options_); - return session; -} - -using TestNghttp2SessionFactoryNew = - TestNghttp2SessionFactory; -using TestNghttp2SessionFactoryLegacy = - TestNghttp2SessionFactory; - class Http2CodecMetadataTest : public Http2CodecImplTestFixture, public ::testing::Test { public: Http2CodecMetadataTest() = default; @@ -2828,27 +2712,12 @@ class Http2CodecMetadataTest : public Http2CodecImplTestFixture, public ::testin allow_metadata_ = true; http2OptionsFromTuple(client_http2_options_, client_settings_); http2OptionsFromTuple(server_http2_options_, server_settings_); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - std::unique_ptr session_factory = - std::make_unique(); - client_ = std::make_unique( - client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, - max_request_headers_kb_, max_response_headers_count_, *session_factory); - server_ = std::make_unique( - server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, - max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); - http2_session_factory_ = std::move(session_factory); - } else { - std::unique_ptr session_factory = - std::make_unique(); - client_ = std::make_unique( - client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, - max_request_headers_kb_, max_response_headers_count_, *session_factory); - server_ = std::make_unique( - server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, - max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); - http2_session_factory_ = std::move(session_factory); - } + client_ = std::make_unique( + client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, random_, + max_request_headers_kb_, max_response_headers_count_, http2_session_factory_); + server_ = std::make_unique( + server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, random_, + max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); ON_CALL(client_connection_, write(_, _)) .WillByDefault(Invoke([&](Buffer::Instance& data, bool) -> void { ASSERT_TRUE(server_wrapper_.dispatch(data, *server_).ok()); @@ -2860,7 +2729,7 @@ class Http2CodecMetadataTest : public Http2CodecImplTestFixture, public ::testin } private: - std::unique_ptr http2_session_factory_; + TestNghttp2SessionFactory http2_session_factory_; }; // Validates noop handling of METADATA frames without a known stream ID. diff --git a/test/common/http/http2/codec_impl_test_util.h b/test/common/http/http2/codec_impl_test_util.h index 20438d42f26e..dcaeff88c534 100644 --- a/test/common/http/http2/codec_impl_test_util.h +++ b/test/common/http/http2/codec_impl_test_util.h @@ -3,7 +3,6 @@ #include "envoy/http/codec.h" #include "common/http/http2/codec_impl.h" -#include "common/http/http2/codec_impl_legacy.h" #include "common/http/utility.h" #include "test/mocks/common.h" @@ -35,7 +34,7 @@ class TestCodecSettingsProvider { return it->second; } - // protected: +protected: // Stores SETTINGS parameters contained in |settings_frame| to make them available via // getRemoteSettingsParameterValue(). void onSettingsFrame(const nghttp2_settings& settings_frame) { @@ -60,134 +59,62 @@ class TestCodecSettingsProvider { absl::node_hash_map settings_; }; -struct ServerCodecFacade : public virtual Connection { - virtual nghttp2_session* session() PURE; - virtual Http::Stream* getStream(int32_t stream_id) PURE; - virtual uint32_t getStreamUnconsumedBytes(int32_t stream_id) PURE; - virtual void setStreamWriteBufferWatermarks(int32_t stream_id, uint32_t low_watermark, - uint32_t high_watermark) PURE; -}; - -class TestServerConnection : public TestCodecStatsProvider, - public TestCodecSettingsProvider, - public ServerCodecFacade { -public: - TestServerConnection(Stats::Scope& scope) : TestCodecStatsProvider(scope) {} -}; - -template -class TestServerConnectionImpl : public TestServerConnection, public CodecImplType { +class TestServerConnectionImpl : public TestCodecStatsProvider, + public TestCodecSettingsProvider, + public ServerConnectionImpl { public: TestServerConnectionImpl( Network::Connection& connection, ServerConnectionCallbacks& callbacks, Stats::Scope& scope, const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - uint32_t max_request_headers_kb, uint32_t max_request_headers_count, + Random::RandomGenerator& random, uint32_t max_request_headers_kb, + uint32_t max_request_headers_count, envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction headers_with_underscores_action) - : TestServerConnection(scope), - CodecImplType(connection, callbacks, http2CodecStats(), random_, http2_options, - max_request_headers_kb, max_request_headers_count, - headers_with_underscores_action) {} - - // ServerCodecFacade - nghttp2_session* session() override { return CodecImplType::session_; } - Http::Stream* getStream(int32_t stream_id) override { - return CodecImplType::getStream(stream_id); - } - uint32_t getStreamUnconsumedBytes(int32_t stream_id) override { - return CodecImplType::getStream(stream_id)->unconsumed_bytes_; - } - void setStreamWriteBufferWatermarks(int32_t stream_id, uint32_t low_watermark, - uint32_t high_watermark) override { - CodecImplType::getStream(stream_id)->setWriteBufferWatermarks(low_watermark, high_watermark); - } + : TestCodecStatsProvider(scope), + ServerConnectionImpl(connection, callbacks, http2CodecStats(), random, http2_options, + max_request_headers_kb, max_request_headers_count, + headers_with_underscores_action) {} + + nghttp2_session* session() { return session_; } + using ServerConnectionImpl::getStream; protected: // Overrides ServerConnectionImpl::onSettingsForTest(). void onSettingsForTest(const nghttp2_settings& settings) override { onSettingsFrame(settings); } - - testing::NiceMock random_; }; -using TestServerConnectionImplLegacy = - TestServerConnectionImpl; -using TestServerConnectionImplNew = - TestServerConnectionImpl; - -struct ClientCodecFacade : public ClientConnection { - virtual nghttp2_session* session() PURE; - virtual Http::Stream* getStream(int32_t stream_id) PURE; - virtual uint64_t getStreamPendingSendDataLength(int32_t stream_id) PURE; - virtual Status sendPendingFrames() PURE; - virtual bool submitMetadata(const MetadataMapVector& mm_vector, int32_t stream_id) PURE; -}; - -class TestClientConnection : public TestCodecStatsProvider, - public TestCodecSettingsProvider, - public ClientCodecFacade { -public: - TestClientConnection(Stats::Scope& scope) : TestCodecStatsProvider(scope) {} - - testing::NiceMock random_generator_; -}; - -template -class TestClientConnectionImpl : public TestClientConnection, public CodecImplType { +class TestClientConnectionImpl : public TestCodecStatsProvider, + public TestCodecSettingsProvider, + public ClientConnectionImpl { public: TestClientConnectionImpl(Network::Connection& connection, Http::ConnectionCallbacks& callbacks, Stats::Scope& scope, const envoy::config::core::v3::Http2ProtocolOptions& http2_options, - uint32_t max_request_headers_kb, uint32_t max_request_headers_count, - typename CodecImplType::SessionFactory& http2_session_factory) - : TestClientConnection(scope), - CodecImplType(connection, callbacks, http2CodecStats(), random_generator_, http2_options, - max_request_headers_kb, max_request_headers_count, http2_session_factory) {} - - // ClientCodecFacade - RequestEncoder& newStream(ResponseDecoder& response_decoder) override { - return CodecImplType::newStream(response_decoder); - } - nghttp2_session* session() override { return CodecImplType::session_; } - Http::Stream* getStream(int32_t stream_id) override { - return CodecImplType::getStream(stream_id); - } - uint64_t getStreamPendingSendDataLength(int32_t stream_id) override { - return CodecImplType::getStream(stream_id)->pending_send_data_.length(); - } - Status sendPendingFrames() override; + Random::RandomGenerator& random, uint32_t max_request_headers_kb, + uint32_t max_request_headers_count, + Nghttp2SessionFactory& http2_session_factory) + : TestCodecStatsProvider(scope), + ClientConnectionImpl(connection, callbacks, http2CodecStats(), random, http2_options, + max_request_headers_kb, max_request_headers_count, + http2_session_factory) {} + + nghttp2_session* session() { return session_; } // Submits an H/2 METADATA frame to the peer. // Returns true on success, false otherwise. - bool submitMetadata(const MetadataMapVector& mm_vector, int32_t stream_id) override { + virtual bool submitMetadata(const MetadataMapVector& mm_vector, int32_t stream_id) { UNREFERENCED_PARAMETER(mm_vector); UNREFERENCED_PARAMETER(stream_id); return false; } + using ClientConnectionImpl::getStream; + using ConnectionImpl::sendPendingFrames; + protected: // Overrides ClientConnectionImpl::onSettingsForTest(). void onSettingsForTest(const nghttp2_settings& settings) override { onSettingsFrame(settings); } }; -template -Status TestClientConnectionImpl::sendPendingFrames() { - return CodecImplType::sendPendingFrames(); -} - -template <> -Status -TestClientConnectionImpl::sendPendingFrames() { - Envoy::Http::Legacy::Http2::ClientConnectionImpl::sendPendingFrames(); - return okStatus(); -} - -using TestClientConnectionImplLegacy = - TestClientConnectionImpl; -using TestClientConnectionImplNew = - TestClientConnectionImpl; - -using ProdNghttp2SessionFactoryLegacy = Envoy::Http::Legacy::Http2::ProdNghttp2SessionFactory; -using ProdNghttp2SessionFactoryNew = Envoy::Http::Http2::ProdNghttp2SessionFactory; - } // namespace Http2 } // namespace Http } // namespace Envoy diff --git a/test/common/http/http2/frame_replay.h b/test/common/http/http2/frame_replay.h index 2922d6a19110..36069dfa8436 100644 --- a/test/common/http/http2/frame_replay.h +++ b/test/common/http/http2/frame_replay.h @@ -4,6 +4,7 @@ #include "common/stats/isolated_store_impl.h" +#include "test/mocks/common.h" #include "test/mocks/http/mocks.h" #include "test/mocks/network/mocks.h" #include "test/test_common/utility.h" @@ -70,6 +71,7 @@ class ClientCodecFrameInjector : public CodecFrameInjector { MockResponseDecoder response_decoder_; RequestEncoder* request_encoder_; MockStreamCallbacks client_stream_callbacks_; + Random::MockRandomGenerator random_; }; // Holds mock and environment placeholders for an HTTP/2 server codec. Sets up expectations for @@ -80,6 +82,7 @@ class ServerCodecFrameInjector : public CodecFrameInjector { ::testing::NiceMock server_connection_; MockServerConnectionCallbacks server_callbacks_; + Random::MockRandomGenerator random_; MockRequestDecoder request_decoder_; MockStreamCallbacks server_stream_callbacks_; }; diff --git a/test/common/http/http2/frame_replay_test.cc b/test/common/http/http2/frame_replay_test.cc index 510002cf3fde..91a2ba4894e1 100644 --- a/test/common/http/http2/frame_replay_test.cc +++ b/test/common/http/http2/frame_replay_test.cc @@ -27,7 +27,7 @@ class RequestFrameCommentTest : public ::testing::Test {}; class ResponseFrameCommentTest : public ::testing::Test {}; // Creates and sets up a stream to reply to. -void setupStream(ClientCodecFrameInjector& codec, TestClientConnectionImplNew& connection) { +void setupStream(ClientCodecFrameInjector& codec, TestClientConnectionImpl& connection) { codec.request_encoder_ = &connection.newStream(codec.response_decoder_); codec.request_encoder_->getStream().addCallbacks(codec.client_stream_callbacks_); // Setup a single stream to inject frames as a reply to. @@ -57,9 +57,9 @@ TEST_F(RequestFrameCommentTest, SimpleExampleHuffman) { // Validate HEADERS decode. ServerCodecFrameInjector codec; - TestServerConnectionImplNew connection( + TestServerConnectionImpl connection( codec.server_connection_, codec.server_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, envoy::config::core::v3::HttpProtocolOptions::ALLOW); EXPECT_TRUE(codec.write(WellKnownFrames::clientConnectionPrefaceFrame(), connection).ok()); EXPECT_TRUE(codec.write(WellKnownFrames::defaultSettingsFrame(), connection).ok()); @@ -90,9 +90,9 @@ TEST_F(ResponseFrameCommentTest, SimpleExampleHuffman) { // Validate HEADERS decode. ClientCodecFrameInjector codec; - TestClientConnectionImplNew connection( + TestClientConnectionImpl connection( codec.client_connection_, codec.client_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, ProdNghttp2SessionFactory::get()); setupStream(codec, connection); @@ -135,9 +135,9 @@ TEST_F(RequestFrameCommentTest, SimpleExamplePlain) { // Validate HEADERS decode. ServerCodecFrameInjector codec; - TestServerConnectionImplNew connection( + TestServerConnectionImpl connection( codec.server_connection_, codec.server_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, envoy::config::core::v3::HttpProtocolOptions::ALLOW); EXPECT_TRUE(codec.write(WellKnownFrames::clientConnectionPrefaceFrame(), connection).ok()); EXPECT_TRUE(codec.write(WellKnownFrames::defaultSettingsFrame(), connection).ok()); @@ -170,9 +170,9 @@ TEST_F(ResponseFrameCommentTest, SimpleExamplePlain) { // Validate HEADERS decode. ClientCodecFrameInjector codec; - TestClientConnectionImplNew connection( + TestClientConnectionImpl connection( codec.client_connection_, codec.client_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, ProdNghttp2SessionFactory::get()); setupStream(codec, connection); @@ -200,9 +200,9 @@ TEST_F(RequestFrameCommentTest, SingleByteNulCrLfInHeaderFrame) { header.frame()[offset] = c; // Play the frames back. ServerCodecFrameInjector codec; - TestServerConnectionImplNew connection( + TestServerConnectionImpl connection( codec.server_connection_, codec.server_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, envoy::config::core::v3::HttpProtocolOptions::ALLOW); EXPECT_TRUE(codec.write(WellKnownFrames::clientConnectionPrefaceFrame(), connection).ok()); EXPECT_TRUE(codec.write(WellKnownFrames::defaultSettingsFrame(), connection).ok()); @@ -233,9 +233,9 @@ TEST_F(ResponseFrameCommentTest, SingleByteNulCrLfInHeaderFrame) { header.frame()[offset] = c; // Play the frames back. ClientCodecFrameInjector codec; - TestClientConnectionImplNew connection( + TestClientConnectionImpl connection( codec.client_connection_, codec.client_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, ProdNghttp2SessionFactory::get()); setupStream(codec, connection); @@ -268,9 +268,9 @@ TEST_F(RequestFrameCommentTest, SingleByteNulCrLfInHeaderField) { header.frame()[offset] = c; // Play the frames back. ServerCodecFrameInjector codec; - TestServerConnectionImplNew connection( + TestServerConnectionImpl connection( codec.server_connection_, codec.server_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, envoy::config::core::v3::HttpProtocolOptions::ALLOW); EXPECT_TRUE(codec.write(WellKnownFrames::clientConnectionPrefaceFrame(), connection).ok()); EXPECT_TRUE(codec.write(WellKnownFrames::defaultSettingsFrame(), connection).ok()); @@ -306,9 +306,9 @@ TEST_F(ResponseFrameCommentTest, SingleByteNulCrLfInHeaderField) { header.frame()[offset] = c; // Play the frames back. ClientCodecFrameInjector codec; - TestClientConnectionImplNew connection( + TestClientConnectionImpl connection( codec.client_connection_, codec.client_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, ProdNghttp2SessionFactory::get()); setupStream(codec, connection); diff --git a/test/common/http/http2/request_header_fuzz_test.cc b/test/common/http/http2/request_header_fuzz_test.cc index 3af7f5c594ce..90b8cdd758e3 100644 --- a/test/common/http/http2/request_header_fuzz_test.cc +++ b/test/common/http/http2/request_header_fuzz_test.cc @@ -13,11 +13,11 @@ namespace Http { namespace Http2 { namespace { -void Replay(const Frame& frame, ServerCodecFrameInjector& codec) { +void replay(const Frame& frame, ServerCodecFrameInjector& codec) { // Create the server connection containing the nghttp2 session. - TestServerConnectionImplNew connection( + TestServerConnectionImpl connection( codec.server_connection_, codec.server_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, envoy::config::core::v3::HttpProtocolOptions::ALLOW); Http::Status status = Http::okStatus(); status = codec.write(WellKnownFrames::clientConnectionPrefaceFrame(), connection); @@ -32,10 +32,10 @@ DEFINE_FUZZER(const uint8_t* buf, size_t len) { Frame frame; frame.assign(buf, buf + len); // Replay with the fuzzer bytes. - Replay(frame, codec); + replay(frame, codec); // Try again, but fixup the HEADERS frame to make it a valid HEADERS. FrameUtils::fixupHeaders(frame); - Replay(frame, codec); + replay(frame, codec); } } // namespace diff --git a/test/common/http/http2/response_header_fuzz_test.cc b/test/common/http/http2/response_header_fuzz_test.cc index 112f1392921a..93e1baff480b 100644 --- a/test/common/http/http2/response_header_fuzz_test.cc +++ b/test/common/http/http2/response_header_fuzz_test.cc @@ -14,11 +14,11 @@ namespace Http { namespace Http2 { namespace { -void Replay(const Frame& frame, ClientCodecFrameInjector& codec) { +void replay(const Frame& frame, ClientCodecFrameInjector& codec) { // Create the client connection containing the nghttp2 session. - TestClientConnectionImplNew connection( + TestClientConnectionImpl connection( codec.client_connection_, codec.client_callbacks_, codec.stats_store_, codec.options_, - Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + codec.random_, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, ProdNghttp2SessionFactory::get()); // Create a new stream. Http::Status status = Http::okStatus(); @@ -40,10 +40,10 @@ DEFINE_FUZZER(const uint8_t* buf, size_t len) { Frame frame; frame.assign(buf, buf + len); // Replay with the fuzzer bytes. - Replay(frame, codec); + replay(frame, codec); // Try again, but fixup the HEADERS frame to make it a valid HEADERS. FrameUtils::fixupHeaders(frame); - Replay(frame, codec); + replay(frame, codec); } } // namespace diff --git a/test/config/utility.cc b/test/config/utility.cc index a5fa2ddab6d0..e0e6012e520c 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -714,10 +714,6 @@ void ConfigHelper::enableDeprecatedV2Api() { addRuntimeOverride("envoy.features.enable_all_deprecated_features", "true"); } -void ConfigHelper::setNewCodecs() { - addRuntimeOverride("envoy.reloadable_features.new_codec_behavior", "true"); -} - void ConfigHelper::setProtocolOptions(envoy::config::cluster::v3::Cluster& cluster, HttpProtocolOptions& protocol_options) { if (cluster.typed_extension_protocol_options().contains( diff --git a/test/config/utility.h b/test/config/utility.h index 65e20e0a9052..b51843eb3341 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -279,9 +279,6 @@ class ConfigHelper { const envoy::extensions::filters::network::http_connection_manager::v3::LocalReplyConfig& config); - // Set new codecs to use for upstream and downstream codecs. - void setNewCodecs(); - using HttpProtocolOptions = envoy::extensions::upstreams::http::v3::HttpProtocolOptions; static void setProtocolOptions(envoy::config::cluster::v3::Cluster& cluster, HttpProtocolOptions& protocol_options); diff --git a/test/extensions/filters/network/http_connection_manager/config_test.cc b/test/extensions/filters/network/http_connection_manager/config_test.cc index 192b7de90908..8b9724db98bd 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_test.cc @@ -1685,70 +1685,6 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtension) { ASSERT_NE(nullptr, request_id_extension); } -TEST_F(HttpConnectionManagerConfigTest, LegacyH1Codecs) { - const std::string yaml_string = R"EOF( -codec_type: http1 -server_name: foo -stat_prefix: router -route_config: - virtual_hosts: - - name: service - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: cluster -http_filters: -- name: envoy.filters.http.router - )EOF"; - - envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager - proto_config; - TestUtility::loadFromYaml(yaml_string, proto_config); - NiceMock filter_callbacks; - EXPECT_CALL(context_.runtime_loader_.snapshot_, runtimeFeatureEnabled(_)).WillOnce(Return(false)); - context_.cluster_manager_.initializeClusters({"cluster"}, {}); - context_.server_factory_context_.cluster_manager_.initializeClusters({"cluster"}, {}); - auto http_connection_manager_factory = - HttpConnectionManagerFactory::createHttpConnectionManagerFactoryFromProto( - proto_config, context_, filter_callbacks); - http_connection_manager_factory(); -} - -TEST_F(HttpConnectionManagerConfigTest, LegacyH2Codecs) { - const std::string yaml_string = R"EOF( -codec_type: http2 -server_name: foo -stat_prefix: router -route_config: - virtual_hosts: - - name: service - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: cluster -http_filters: -- name: envoy.filters.http.router - )EOF"; - - envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager - proto_config; - TestUtility::loadFromYaml(yaml_string, proto_config); - NiceMock filter_callbacks; - EXPECT_CALL(context_.runtime_loader_.snapshot_, runtimeFeatureEnabled(_)).WillOnce(Return(false)); - context_.cluster_manager_.initializeClusters({"cluster"}, {}); - context_.server_factory_context_.cluster_manager_.initializeClusters({"cluster"}, {}); - auto http_connection_manager_factory = - HttpConnectionManagerFactory::createHttpConnectionManagerFactoryFromProto( - proto_config, context_, filter_callbacks); - http_connection_manager_factory(); -} - TEST_F(HttpConnectionManagerConfigTest, DynamicFilterWarmingNoDefault) { const std::string yaml_string = R"EOF( codec_type: http1 diff --git a/test/integration/BUILD b/test/integration/BUILD index 0778f624670d..f960c0503ae1 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -8,7 +8,6 @@ load( "envoy_package", "envoy_proto_library", "envoy_select_hot_restart", - "envoy_select_new_codecs_in_integration_tests", "envoy_sh_test", ) @@ -649,10 +648,6 @@ envoy_cc_test_library( hdrs = [ "fake_upstream.h", ], - copts = envoy_select_new_codecs_in_integration_tests( - ["-DENVOY_USE_NEW_CODECS_IN_INTEGRATION_TESTS"], - "@envoy", - ), deps = [ "//include/envoy/api:api_interface", "//include/envoy/grpc:status", @@ -670,9 +665,7 @@ envoy_cc_test_library( "//source/common/common:thread_lib", "//source/common/grpc:codec_lib", "//source/common/grpc:common_lib", - "//source/common/http/http1:codec_legacy_lib", "//source/common/http/http1:codec_lib", - "//source/common/http/http2:codec_legacy_lib", "//source/common/http/http2:codec_lib", "//source/common/network:connection_balancer_lib", "//source/common/network:filter_lib", @@ -786,10 +779,6 @@ envoy_cc_test_library( "integration.h", "ssl_utility.h", ], - copts = envoy_select_new_codecs_in_integration_tests( - ["-DENVOY_USE_NEW_CODECS_IN_INTEGRATION_TESTS"], - "@envoy", - ), data = ["//test/common/runtime:filesystem_test_data"], deps = [ ":autonomous_upstream_lib", @@ -825,9 +814,7 @@ envoy_cc_test_library( "//source/common/http:codec_client_lib", "//source/common/http:header_map_lib", "//source/common/http:headers_lib", - "//source/common/http/http1:codec_legacy_lib", "//source/common/http/http1:codec_lib", - "//source/common/http/http2:codec_legacy_lib", "//source/common/http/http2:codec_lib", "//source/common/local_info:local_info_lib", "//source/common/network:filter_lib", diff --git a/test/integration/base_integration_test.cc b/test/integration/base_integration_test.cc index 45a7019ed9ac..d4d8c2af371a 100644 --- a/test/integration/base_integration_test.cc +++ b/test/integration/base_integration_test.cc @@ -65,11 +65,6 @@ BaseIntegrationTest::BaseIntegrationTest(const InstanceConstSharedPtrFn& upstrea return new Buffer::WatermarkBuffer(below_low, above_high, above_overflow); })); ON_CALL(factory_context_, api()).WillByDefault(ReturnRef(*api_)); - // In ENVOY_USE_NEW_CODECS_IN_INTEGRATION_TESTS mode, set runtime config to use legacy codecs. -#ifdef ENVOY_USE_NEW_CODECS_IN_INTEGRATION_TESTS - ENVOY_LOG_MISC(debug, "Using new codecs"); - setNewCodecs(); -#endif } BaseIntegrationTest::BaseIntegrationTest(Network::Address::IpVersion version, diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index 3180cf96ef3e..bc4b2d544097 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -80,7 +80,6 @@ class BaseIntegrationTest : protected Logger::Loggable { void skipPortUsageValidation() { config_helper_.skipPortUsageValidation(); } // Make test more deterministic by using a fixed RNG value. void setDeterministic() { deterministic_ = true; } - void setNewCodecs() { config_helper_.setNewCodecs(); } FakeHttpConnection::Type upstreamProtocol() const { return upstream_config_.upstream_protocol_; } diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index c0aadbbc5f62..06234a2a7c78 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -8,8 +8,7 @@ #include "common/buffer/buffer_impl.h" #include "common/http/header_map_impl.h" #include "common/http/http1/codec_impl.h" -#include "common/http/http1/codec_impl_legacy.h" -#include "common/http/http2/codec_impl_legacy.h" +#include "common/http/http2/codec_impl.h" #include "common/network/address_impl.h" #include "common/network/listen_socket_impl.h" #include "common/network/socket_option_factory.h" @@ -311,29 +310,6 @@ class TestHttp1ServerConnectionImpl : public Http::Http1::ServerConnectionImpl { } }; -namespace Legacy { -class TestHttp1ServerConnectionImpl : public Http::Legacy::Http1::ServerConnectionImpl { -public: - using Http::Legacy::Http1::ServerConnectionImpl::ServerConnectionImpl; - - void onMessageComplete() override { - ServerConnectionImpl::onMessageComplete(); - - if (activeRequest().has_value() && activeRequest().value().request_decoder_) { - // Undo the read disable from the base class - we have many tests which - // waitForDisconnect after a full request has been read which will not - // receive the disconnect if reading is disabled. - activeRequest().value().response_encoder_.readDisable(false); - } - } - ~TestHttp1ServerConnectionImpl() override { - if (activeRequest().has_value()) { - activeRequest().value().response_encoder_.clearReadDisableCallsForTests(); - } - } -}; -} // namespace Legacy - FakeHttpConnection::FakeHttpConnection( FakeUpstream& fake_upstream, SharedConnectionWrapper& shared_connection, Type type, Event::TestTimeSystem& time_system, uint32_t max_request_headers_kb, @@ -346,15 +322,9 @@ FakeHttpConnection::FakeHttpConnection( // For the purpose of testing, we always have the upstream encode the trailers if any http1_settings.enable_trailers_ = true; Http::Http1::CodecStats& stats = fake_upstream.http1CodecStats(); -#ifdef ENVOY_USE_NEW_CODECS_IN_INTEGRATION_TESTS codec_ = std::make_unique( shared_connection_.connection(), stats, *this, http1_settings, max_request_headers_kb, max_request_headers_count, headers_with_underscores_action); -#else - codec_ = std::make_unique( - shared_connection_.connection(), stats, *this, http1_settings, max_request_headers_kb, - max_request_headers_count, headers_with_underscores_action); -#endif } else { envoy::config::core::v3::Http2ProtocolOptions http2_options = ::Envoy::Http2::Utility::initializeAndValidateOptions( @@ -362,15 +332,9 @@ FakeHttpConnection::FakeHttpConnection( http2_options.set_allow_connect(true); http2_options.set_allow_metadata(true); Http::Http2::CodecStats& stats = fake_upstream.http2CodecStats(); -#ifdef ENVOY_USE_NEW_CODECS_IN_INTEGRATION_TESTS codec_ = std::make_unique( shared_connection_.connection(), *this, stats, random_, http2_options, max_request_headers_kb, max_request_headers_count, headers_with_underscores_action); -#else - codec_ = std::make_unique( - shared_connection_.connection(), *this, stats, random_, http2_options, - max_request_headers_kb, max_request_headers_count, headers_with_underscores_action); -#endif ASSERT(type == Type::HTTP2); } shared_connection_.connection().addReadFilter( diff --git a/test/integration/http2_flood_integration_test.cc b/test/integration/http2_flood_integration_test.cc index d88ac81b2e3b..780b087e9c76 100644 --- a/test/integration/http2_flood_integration_test.cc +++ b/test/integration/http2_flood_integration_test.cc @@ -120,10 +120,6 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, Http2FloodMitigationTest, TestUtility::ipTestParamsToString); bool Http2FloodMitigationTest::initializeUpstreamFloodTest() { - if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.new_codec_behavior")) { - // Upstream flood checks are not implemented in the old codec based on exceptions - return false; - } config_helper_.addRuntimeOverride("envoy.reloadable_features.upstream_http2_flood_checks", "true"); setDownstreamProtocol(Http::CodecClient::Type::HTTP2); From 5228a84cd6fe613053e2bbadffd700b13d0c810c Mon Sep 17 00:00:00 2001 From: Lizan Zhou Date: Wed, 16 Dec 2020 12:25:12 -0800 Subject: [PATCH 49/49] sds: allow multiple init managers share sds target (#14357) Fixes #11120, allows more than one init manager to watch the same SDS init target so clusters/listeners won't be marked active immediately. Additional Description: Risk Level: Medium Testing: integration test Docs Changes: N/A Release Notes: Added. Signed-off-by: Lizan Zhou --- docs/root/version_history/current.rst | 1 + source/common/secret/sds_api.cc | 5 -- source/common/secret/sds_api.h | 16 ++---- source/common/secret/secret_manager_impl.h | 4 ++ test/integration/BUILD | 1 + test/integration/ads_integration_test.cc | 58 ++++++++++++++++++++++ test/integration/base_integration_test.h | 17 +++---- 7 files changed, 76 insertions(+), 26 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index bff52edb3330..cec10207a5b3 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -43,6 +43,7 @@ Bug Fixes * http: sending CONNECT_ERROR for HTTP/2 where appropriate during CONNECT requests. * proxy_proto: fixed a bug where the wrong downstream address got sent to upstream connections. * proxy_proto: fixed a bug where network filters would not have the correct downstreamRemoteAddress() when accessed from the StreamInfo. This could result in incorrect enforcement of RBAC rules in the RBAC network filter (but not in the RBAC HTTP filter), or incorrect access log addresses from tcp_proxy. +* sds: fix a bug that clusters sharing same sds target are marked active immediately. * tls: fix detection of the upstream connection close event. * tls: fix read resumption after triggering buffer high-watermark and all remaining request/response bytes are stored in the SSL connection's internal buffers. * udp: fixed issue in which receiving truncated UDP datagrams would cause Envoy to crash. diff --git a/source/common/secret/sds_api.cc b/source/common/secret/sds_api.cc index 3f319f03e638..f239b725b1c8 100644 --- a/source/common/secret/sds_api.cc +++ b/source/common/secret/sds_api.cc @@ -34,11 +34,6 @@ SdsApi::SdsApi(envoy::config::core::v3::ConfigSource sds_config, absl::string_vi // This has to happen here (rather than in initialize()) as it can throw exceptions. subscription_ = subscription_factory_.subscriptionFromConfigSource( sds_config_, Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_); - - // TODO(JimmyCYJ): Implement chained_init_manager, so that multiple init_manager - // can be chained together to behave as one init_manager. In that way, we let - // two listeners which share same SdsApi to register at separate init managers, and - // each init manager has a chance to initialize its targets. } void SdsApi::resolveDataSource(const FileContentMap& files, diff --git a/source/common/secret/sds_api.h b/source/common/secret/sds_api.h index 4be8fb63d4fb..9bba1d71db66 100644 --- a/source/common/secret/sds_api.h +++ b/source/common/secret/sds_api.h @@ -138,13 +138,11 @@ class TlsCertificateSdsApi : public SdsApi, public TlsCertificateConfigProvider // We need to do this early as we invoke the subscription factory during initialization, which // is too late to throw. Config::Utility::checkLocalInfo("TlsCertificateSdsApi", secret_provider_context.localInfo()); - auto ret = std::make_shared( + return std::make_shared( sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(), secret_provider_context.dispatcher().timeSource(), secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(), destructor_cb, secret_provider_context.dispatcher(), secret_provider_context.api()); - secret_provider_context.initManager().add(*ret->initTarget()); - return ret; } TlsCertificateSdsApi(const envoy::config::core::v3::ConfigSource& sds_config, @@ -223,13 +221,11 @@ class CertificateValidationContextSdsApi : public SdsApi, // is too late to throw. Config::Utility::checkLocalInfo("CertificateValidationContextSdsApi", secret_provider_context.localInfo()); - auto ret = std::make_shared( + return std::make_shared( sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(), secret_provider_context.dispatcher().timeSource(), secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(), destructor_cb, secret_provider_context.dispatcher(), secret_provider_context.api()); - secret_provider_context.initManager().add(*ret->initTarget()); - return ret; } CertificateValidationContextSdsApi(const envoy::config::core::v3::ConfigSource& sds_config, const std::string& sds_config_name, @@ -318,13 +314,11 @@ class TlsSessionTicketKeysSdsApi : public SdsApi, public TlsSessionTicketKeysCon // is too late to throw. Config::Utility::checkLocalInfo("TlsSessionTicketKeysSdsApi", secret_provider_context.localInfo()); - auto ret = std::make_shared( + return std::make_shared( sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(), secret_provider_context.dispatcher().timeSource(), secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(), destructor_cb, secret_provider_context.dispatcher(), secret_provider_context.api()); - secret_provider_context.initManager().add(*ret->initTarget()); - return ret; } TlsSessionTicketKeysSdsApi(const envoy::config::core::v3::ConfigSource& sds_config, @@ -391,13 +385,11 @@ class GenericSecretSdsApi : public SdsApi, public GenericSecretConfigProvider { // We need to do this early as we invoke the subscription factory during initialization, which // is too late to throw. Config::Utility::checkLocalInfo("GenericSecretSdsApi", secret_provider_context.localInfo()); - auto ret = std::make_shared( + return std::make_shared( sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(), secret_provider_context.dispatcher().timeSource(), secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(), destructor_cb, secret_provider_context.dispatcher(), secret_provider_context.api()); - secret_provider_context.initManager().add(*ret->initTarget()); - return ret; } GenericSecretSdsApi(const envoy::config::core::v3::ConfigSource& sds_config, diff --git a/source/common/secret/secret_manager_impl.h b/source/common/secret/secret_manager_impl.h index 799c7415d7ce..a3045a87b4da 100644 --- a/source/common/secret/secret_manager_impl.h +++ b/source/common/secret/secret_manager_impl.h @@ -92,6 +92,10 @@ class SecretManagerImpl : public SecretManager { config_name, unregister_secret_provider); dynamic_secret_providers_[map_key] = secret_provider; } + // It is important to add the init target to the manager regardless the secret provider is new + // or existing. Different clusters / listeners can share same secret so they have to be marked + // warming correctly. + secret_provider_context.initManager().add(*secret_provider->initTarget()); return secret_provider; } diff --git a/test/integration/BUILD b/test/integration/BUILD index f960c0503ae1..79b8ed1e71b1 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -63,6 +63,7 @@ envoy_cc_test( "//test/test_common:utility_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/config/endpoint/v3:pkg_cc_proto", "@envoy_api//envoy/config/listener/v3:pkg_cc_proto", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", diff --git a/test/integration/ads_integration_test.cc b/test/integration/ads_integration_test.cc index b178297fe127..afb5a1822b57 100644 --- a/test/integration/ads_integration_test.cc +++ b/test/integration/ads_integration_test.cc @@ -1,5 +1,6 @@ #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/cluster/v3/cluster.pb.h" +#include "envoy/config/core/v3/base.pb.h" #include "envoy/config/endpoint/v3/endpoint.pb.h" #include "envoy/config/listener/v3/listener.pb.h" #include "envoy/config/route/v3/route.pb.h" @@ -168,6 +169,63 @@ TEST_P(AdsIntegrationTest, ClusterInitializationUpdateOneOfThe2Warming) { test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 0); test_server_->waitForGaugeGe("cluster_manager.active_clusters", 4); } + +// Make sure two clusters sharing same secret are both kept warming before secret +// arrives. Verify that the clusters are eventually initialized. +// This is a regression test of #11120. +TEST_P(AdsIntegrationTest, ClusterSharingSecretWarming) { + initialize(); + const auto cds_type_url = Config::getTypeUrl( + envoy::config::core::v3::ApiVersion::V3); + const auto sds_type_url = + Config::getTypeUrl( + envoy::config::core::v3::ApiVersion::V3); + + envoy::config::core::v3::TransportSocket sds_transport_socket; + TestUtility::loadFromYaml(R"EOF( + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + validation_context_sds_secret_config: + name: validation_context + sds_config: + resource_api_version: V3 + ads: {} + )EOF", + sds_transport_socket); + auto cluster_template = ConfigHelper::buildStaticCluster("cluster", 8000, "127.0.0.1"); + *cluster_template.mutable_transport_socket() = sds_transport_socket; + + auto cluster_0 = cluster_template; + cluster_0.set_name("cluster_0"); + auto cluster_1 = cluster_template; + cluster_1.set_name("cluster_1"); + + EXPECT_TRUE(compareDiscoveryRequest(cds_type_url, "", {}, {}, {}, true)); + sendDiscoveryResponse( + cds_type_url, {cluster_0, cluster_1}, {cluster_0, cluster_1}, {}, "1", false); + + EXPECT_TRUE(compareDiscoveryRequest(sds_type_url, "", {"validation_context"}, + {"validation_context"}, {})); + test_server_->waitForGaugeGe("cluster_manager.warming_clusters", 2); + + envoy::extensions::transport_sockets::tls::v3::Secret validation_context; + TestUtility::loadFromYaml(fmt::format(R"EOF( + name: validation_context + validation_context: + trusted_ca: + filename: {} + )EOF", + TestEnvironment::runfilesPath( + "test/config/integration/certs/upstreamcacert.pem")), + validation_context); + + sendDiscoveryResponse( + sds_type_url, {validation_context}, {validation_context}, {}, "1"); + test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 0); +} + // Validate basic config delivery and upgrade with RateLimiting. TEST_P(AdsIntegrationTest, BasicWithRateLimiting) { initializeAds(true); diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index bc4b2d544097..a2844063eb3b 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -252,16 +252,15 @@ class BaseIntegrationTest : protected Logger::Loggable { } private: - std::string intResourceName(const envoy::config::listener::v3::Listener& m) { return m.name(); } - std::string intResourceName(const envoy::config::route::v3::RouteConfiguration& m) { - return m.name(); - } - std::string intResourceName(const envoy::config::cluster::v3::Cluster& m) { return m.name(); } - std::string intResourceName(const envoy::config::endpoint::v3::ClusterLoadAssignment& m) { - return m.cluster_name(); + template std::string intResourceName(const T& m) { + // gcc doesn't allow inline template function to be specialized, using a constexpr if to + // workaround. + if constexpr (std::is_same_v) { + return m.cluster_name(); + } else { + return m.name(); + } } - std::string intResourceName(const envoy::config::route::v3::VirtualHost& m) { return m.name(); } - std::string intResourceName(const envoy::service::runtime::v3::Runtime& m) { return m.name(); } Event::GlobalTimeSystem time_system_;