diff --git a/docs/root/configuration/http/http_conn_man/stats.rst b/docs/root/configuration/http/http_conn_man/stats.rst index dd6891ce2d220..30a016594812e 100644 --- a/docs/root/configuration/http/http_conn_man/stats.rst +++ b/docs/root/configuration/http/http_conn_man/stats.rst @@ -87,7 +87,12 @@ the following statistics: Per listener statistics ----------------------- -Additional per listener statistics are rooted at *listener.
.http..* with the +Per listener statistics are rooted at *listener.
*. + +HTTP per listener statistics +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Additional HTTP statistics are of the form *http..* with the following statistics: .. csv-table:: @@ -101,15 +106,27 @@ following statistics: downstream_rq_4xx, Counter, Total 4xx responses downstream_rq_5xx, Counter, Total 5xx responses +HTTP/3 per listener statistics +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +HTTP/3 statistics with the form of *http3.downstream..*: + +.. csv-table:: + :header: Name, Type, Description + :widths: 1, 1, 2 + + .quic_connection_close_error_code_, Counter, A collection of counters that are lazily initialized to record each quic connection close error code that's present. + + .. _config_http_conn_man_stats_per_codec: Per codec statistics ----------------------- -Each codec has the option of adding per-codec statistics. Both http1 and http2 have codec stats. +Each codec has the option of adding per-codec statistics. http1, http2, and http3 all have codec stats. -Http1 codec statistics -~~~~~~~~~~~~~~~~~~~~~~ +HTTP/1 codec statistics +~~~~~~~~~~~~~~~~~~~~~~~ On the downstream side all http1 statistics are rooted at *http1.* @@ -124,8 +141,8 @@ On the upstream side all http1 statistics are rooted at *cluster..http1.* response_flood, Counter, Total number of connections closed due to response flooding requests_rejected_with_underscores_in_headers, Counter, Total numbers of rejected requests due to header names containing underscores. This action is configured by setting the :ref:`headers_with_underscores_action config setting `. -Http2 codec statistics -~~~~~~~~~~~~~~~~~~~~~~ +HTTP/2 codec statistics +~~~~~~~~~~~~~~~~~~~~~~~ On the downstream side all http2 statistics are rooted at *http2.* @@ -160,8 +177,8 @@ On the upstream side all http2 statistics are rooted at *cluster..http2.* `downstream_rq_active` gauge due to differences in stream accounting between the codec and the HTTP connection manager. -Http3 codec statistics -~~~~~~~~~~~~~~~~~~~~~~ +HTTP/3 codec statistics +~~~~~~~~~~~~~~~~~~~~~~~ On the downstream side all http3 statistics are rooted at *http3.* diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index ba0c0401ea00c..60061ec64963a 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -51,6 +51,19 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "quic_stat_names_lib", + srcs = ["quic_stat_names.cc"], + hdrs = ["quic_stat_names.h"], + tags = ["nofips"], + deps = [ + "//include/envoy/stats:stats_interface", + "//source/common/stats:symbol_table_lib", + "@com_googlesource_quiche//:quic_core_error_codes_lib", + "@com_googlesource_quiche//:quic_core_types_lib", + ], +) + envoy_cc_library( name = "envoy_quic_proof_source_base_lib", srcs = ["envoy_quic_proof_source_base.cc"], @@ -314,6 +327,7 @@ envoy_cc_library( hdrs = ["envoy_quic_dispatcher.h"], tags = ["nofips"], deps = [ + "quic_stat_names_lib", ":envoy_quic_proof_source_lib", ":envoy_quic_server_connection_lib", ":envoy_quic_server_session_lib", diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index 75d32d5792cfe..eaa3991c94d55 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -28,18 +28,18 @@ ActiveQuicListener::ActiveQuicListener( Network::UdpConnectionHandler& parent, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, Network::Socket::OptionsSharedPtr options, bool kernel_worker_routing, const envoy::config::core::v3::RuntimeFeatureFlag& enabled, - uint32_t packets_received_to_connection_count_ratio) + QuicStatNames& quic_stat_names, uint32_t packets_received_to_connection_count_ratio) : ActiveQuicListener(worker_index, concurrency, dispatcher, parent, listener_config.listenSocketFactory().getListenSocket(), listener_config, quic_config, std::move(options), kernel_worker_routing, enabled, - packets_received_to_connection_count_ratio) {} + quic_stat_names, packets_received_to_connection_count_ratio) {} ActiveQuicListener::ActiveQuicListener( uint32_t worker_index, uint32_t concurrency, Event::Dispatcher& dispatcher, Network::UdpConnectionHandler& parent, Network::SocketSharedPtr listen_socket, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, Network::Socket::OptionsSharedPtr options, bool kernel_worker_routing, - const envoy::config::core::v3::RuntimeFeatureFlag& enabled, + const envoy::config::core::v3::RuntimeFeatureFlag& enabled, QuicStatNames& quic_stat_names, uint32_t packets_to_read_to_connection_count_ratio) : Server::ActiveUdpListenerBase( worker_index, concurrency, parent, *listen_socket, @@ -87,7 +87,7 @@ ActiveQuicListener::ActiveQuicListener( quic_dispatcher_ = std::make_unique( crypto_config_.get(), quic_config, &version_manager_, std::move(connection_helper), std::move(alarm_factory), quic::kQuicDefaultConnectionIdLength, parent, *config_, stats_, - per_worker_stats_, dispatcher, listen_socket_); + per_worker_stats_, dispatcher, listen_socket_, quic_stat_names); // Create udp_packet_writer Network::UdpPacketWriterPtr udp_packet_writer = @@ -227,8 +227,9 @@ size_t ActiveQuicListener::numPacketsExpectedPerEventLoop() const { } ActiveQuicListenerFactory::ActiveQuicListenerFactory( - const envoy::config::listener::v3::QuicProtocolOptions& config, uint32_t concurrency) - : concurrency_(concurrency), enabled_(config.enabled()), + const envoy::config::listener::v3::QuicProtocolOptions& config, uint32_t concurrency, + QuicStatNames& quic_stat_names) + : concurrency_(concurrency), enabled_(config.enabled()), quic_stat_names_(quic_stat_names), packets_to_read_to_connection_count_ratio_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, packets_to_read_to_connection_count_ratio, DEFAULT_PACKETS_TO_READ_PER_CONNECTION)) { @@ -314,9 +315,10 @@ Network::ConnectionHandler::ActiveUdpListenerPtr ActiveQuicListenerFactory::crea } #endif - return std::make_unique( - worker_index, concurrency_, disptacher, parent, config, quic_config_, std::move(options), - kernel_worker_routing, enabled_, packets_to_read_to_connection_count_ratio_); + return std::make_unique(worker_index, concurrency_, disptacher, parent, + config, quic_config_, std::move(options), + kernel_worker_routing, enabled_, quic_stat_names_, + packets_to_read_to_connection_count_ratio_); } // namespace Quic } // namespace Quic diff --git a/source/common/quic/active_quic_listener.h b/source/common/quic/active_quic_listener.h index 7c2264d2da9e1..8eccfa075333e 100644 --- a/source/common/quic/active_quic_listener.h +++ b/source/common/quic/active_quic_listener.h @@ -29,6 +29,7 @@ class ActiveQuicListener : public Envoy::Server::ActiveUdpListenerBase, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, Network::Socket::OptionsSharedPtr options, bool kernel_worker_routing, const envoy::config::core::v3::RuntimeFeatureFlag& enabled, + QuicStatNames& quic_stat_names, uint32_t packets_to_read_to_connection_count_ratio); ActiveQuicListener(uint32_t worker_index, uint32_t concurrency, Event::Dispatcher& dispatcher, @@ -36,6 +37,7 @@ class ActiveQuicListener : public Envoy::Server::ActiveUdpListenerBase, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, Network::Socket::OptionsSharedPtr options, bool kernel_worker_routing, const envoy::config::core::v3::RuntimeFeatureFlag& enabled, + QuicStatNames& quic_stat_names, uint32_t packets_to_read_to_connection_count_ratio); ~ActiveQuicListener() override; @@ -87,7 +89,7 @@ class ActiveQuicListenerFactory : public Network::ActiveUdpListenerFactory, Logger::Loggable { public: ActiveQuicListenerFactory(const envoy::config::listener::v3::QuicProtocolOptions& config, - uint32_t concurrency); + uint32_t concurrency, QuicStatNames& quic_stat_names); // Network::ActiveUdpListenerFactory. Network::ConnectionHandler::ActiveUdpListenerPtr @@ -102,6 +104,7 @@ class ActiveQuicListenerFactory : public Network::ActiveUdpListenerFactory, const uint32_t concurrency_; absl::once_flag install_bpf_once_; envoy::config::core::v3::RuntimeFeatureFlag enabled_; + QuicStatNames& quic_stat_names_; const uint32_t packets_to_read_to_connection_count_ratio_; }; diff --git a/source/common/quic/envoy_quic_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index 4191e80d4d793..602a610736ca7 100644 --- a/source/common/quic/envoy_quic_dispatcher.cc +++ b/source/common/quic/envoy_quic_dispatcher.cc @@ -17,13 +17,13 @@ EnvoyQuicDispatcher::EnvoyQuicDispatcher( uint8_t expected_server_connection_id_length, Network::ConnectionHandler& connection_handler, Network::ListenerConfig& listener_config, Server::ListenerStats& listener_stats, Server::PerHandlerListenerStats& per_worker_stats, Event::Dispatcher& dispatcher, - Network::Socket& listen_socket) + Network::Socket& listen_socket, QuicStatNames& quic_stat_names) : quic::QuicDispatcher(&quic_config, crypto_config, version_manager, std::move(helper), std::make_unique(), std::move(alarm_factory), expected_server_connection_id_length), connection_handler_(connection_handler), listener_config_(listener_config), listener_stats_(listener_stats), per_worker_stats_(per_worker_stats), dispatcher_(dispatcher), - listen_socket_(listen_socket) { + listen_socket_(listen_socket), quic_stat_names_(quic_stat_names) { // Set send buffer twice of max flow control window to ensure that stream send // buffer always takes all the data. // The max amount of data buffered is the per-stream high watermark + the max @@ -46,6 +46,8 @@ void EnvoyQuicDispatcher::OnConnectionClosed(quic::QuicConnectionId connection_i listener_stats_.downstream_cx_active_.dec(); per_worker_stats_.downstream_cx_active_.dec(); connection_handler_.decNumConnections(); + quic_stat_names_.chargeQuicConnectionCloseStats(listener_config_.listenerScope(), error, source, + /*is_upstream*/ false); } std::unique_ptr EnvoyQuicDispatcher::CreateQuicSession( diff --git a/source/common/quic/envoy_quic_dispatcher.h b/source/common/quic/envoy_quic_dispatcher.h index 987dd2b450651..81058fcedf808 100644 --- a/source/common/quic/envoy_quic_dispatcher.h +++ b/source/common/quic/envoy_quic_dispatcher.h @@ -19,6 +19,7 @@ #include "envoy/network/listener.h" #include "server/connection_handler_impl.h" #include "server/active_listener_base.h" +#include "common/quic/quic_stat_names.h" namespace Envoy { namespace Quic { @@ -43,17 +44,15 @@ class EnvoyQuicCryptoServerStreamHelper : public quic::QuicCryptoServerStreamBas class EnvoyQuicDispatcher : public quic::QuicDispatcher { public: - EnvoyQuicDispatcher(const quic::QuicCryptoServerConfig* crypto_config, - const quic::QuicConfig& quic_config, - quic::QuicVersionManager* version_manager, - std::unique_ptr helper, - std::unique_ptr alarm_factory, - uint8_t expected_server_connection_id_length, - Network::ConnectionHandler& connection_handler, - Network::ListenerConfig& listener_config, - Server::ListenerStats& listener_stats, - Server::PerHandlerListenerStats& per_worker_stats, - Event::Dispatcher& dispatcher, Network::Socket& listen_socket); + EnvoyQuicDispatcher( + const quic::QuicCryptoServerConfig* crypto_config, const quic::QuicConfig& quic_config, + quic::QuicVersionManager* version_manager, + std::unique_ptr helper, + std::unique_ptr alarm_factory, + uint8_t expected_server_connection_id_length, Network::ConnectionHandler& connection_handler, + Network::ListenerConfig& listener_config, Server::ListenerStats& listener_stats, + Server::PerHandlerListenerStats& per_worker_stats, Event::Dispatcher& dispatcher, + Network::Socket& listen_socket, QuicStatNames& quic_stat_names); void OnConnectionClosed(quic::QuicConnectionId connection_id, quic::QuicErrorCode error, const std::string& error_details, @@ -81,6 +80,7 @@ class EnvoyQuicDispatcher : public quic::QuicDispatcher { Server::PerHandlerListenerStats& per_worker_stats_; Event::Dispatcher& dispatcher_; Network::Socket& listen_socket_; + QuicStatNames& quic_stat_names_; }; } // namespace Quic diff --git a/source/common/quic/quic_stat_names.cc b/source/common/quic/quic_stat_names.cc new file mode 100644 index 0000000000000..d5d4eb0e49396 --- /dev/null +++ b/source/common/quic/quic_stat_names.cc @@ -0,0 +1,52 @@ +#include "common/quic/quic_stat_names.h" + +namespace Envoy { +namespace Quic { + +// TODO(renjietang): Currently these stats are only available in downstream. Wire it up to upstream +// QUIC also. +QuicStatNames::QuicStatNames(Stats::SymbolTable& symbol_table) + : stat_name_pool_(symbol_table), symbol_table_(symbol_table), + http3_prefix_(stat_name_pool_.add("http3")), downstream_(stat_name_pool_.add("downstream")), + upstream_(stat_name_pool_.add("upstream")), from_self_(stat_name_pool_.add("tx")), + from_peer_(stat_name_pool_.add("rx")) { + // Preallocate most used counters + // Most popular in client initiated connection close. + connectionCloseStatName(quic::QUIC_NETWORK_IDLE_TIMEOUT); + // Most popular in server initiated connection close. + connectionCloseStatName(quic::QUIC_SILENT_IDLE_TIMEOUT); +} + +void QuicStatNames::incCounter(Stats::Scope& scope, const Stats::StatNameVec& names) { + Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); + scope.counterFromStatName(Stats::StatName(stat_name_storage.get())).inc(); +} + +void QuicStatNames::chargeQuicConnectionCloseStats(Stats::Scope& scope, + quic::QuicErrorCode error_code, + quic::ConnectionCloseSource source, + bool is_upstream) { + ASSERT(&symbol_table_ == &scope.symbolTable()); + + if (error_code > quic::QUIC_LAST_ERROR) { + error_code = quic::QUIC_LAST_ERROR; + } + + const Stats::StatName connection_close = connectionCloseStatName(error_code); + incCounter(scope, {http3_prefix_, is_upstream ? upstream_ : downstream_, + source == quic::ConnectionCloseSource::FROM_SELF ? from_self_ : from_peer_, + connection_close}); +} + +Stats::StatName QuicStatNames::connectionCloseStatName(quic::QuicErrorCode error_code) { + ASSERT(error_code <= quic::QUIC_LAST_ERROR); + + return Stats::StatName( + connection_error_stat_names_.get(error_code, [this, error_code]() -> const uint8_t* { + return stat_name_pool_.addReturningStorage( + absl::StrCat("quic_connection_close_error_code_", QuicErrorCodeToString(error_code))); + })); +} + +} // namespace Quic +} // namespace Envoy diff --git a/source/common/quic/quic_stat_names.h b/source/common/quic/quic_stat_names.h new file mode 100644 index 0000000000000..89255aa99cfda --- /dev/null +++ b/source/common/quic/quic_stat_names.h @@ -0,0 +1,42 @@ +#pragma once + +#include "envoy/stats/scope.h" + +#include "common/common/thread.h" +#include "common/stats/symbol_table_impl.h" + +#include "quiche/quic/core/quic_error_codes.h" +#include "quiche/quic/core/quic_types.h" + +namespace Envoy { +namespace Quic { + +class QuicStatNames { +public: + // This class holds lazily symbolized stat names and is responsible for charging them. + explicit QuicStatNames(Stats::SymbolTable& symbol_table); + + void chargeQuicConnectionCloseStats(Stats::Scope& scope, quic::QuicErrorCode error_code, + quic::ConnectionCloseSource source, bool is_upstream); + +private: + // Find the actual counter in |scope| and increment it. + // An example counter name: "http3.downstream.tx.quic_connection_close_error_code_QUIC_NO_ERROR". + void incCounter(Stats::Scope& scope, const Stats::StatNameVec& names); + + Stats::StatName connectionCloseStatName(quic::QuicErrorCode error_code); + + Stats::StatNamePool stat_name_pool_; + Stats::SymbolTable& symbol_table_; + const Stats::StatName http3_prefix_; + const Stats::StatName downstream_; + const Stats::StatName upstream_; + const Stats::StatName from_self_; + const Stats::StatName from_peer_; + Thread::AtomicPtrArray + connection_error_stat_names_; +}; + +} // namespace Quic +} // namespace Envoy diff --git a/source/server/BUILD b/source/server/BUILD index bd71a70146747..c9004b42cdf00 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -421,6 +421,7 @@ envoy_cc_library( "//source/common/quic:quic_factory_lib", "//source/common/quic:quic_transport_socket_factory_lib", "//source/common/quic:udp_gso_batch_writer_lib", + "//source/common/quic:quic_stat_names_lib", ]), ) diff --git a/source/server/listener_impl.cc b/source/server/listener_impl.cc index 405937f7cf7b1..852dbe5d4137c 100644 --- a/source/server/listener_impl.cc +++ b/source/server/listener_impl.cc @@ -33,7 +33,7 @@ #include "extensions/filters/listener/well_known_names.h" -#if defined(ENVOY_ENABLE_QUIC) +#ifdef ENVOY_ENABLE_QUIC #include "common/quic/active_quic_listener.h" #include "common/quic/udp_gso_batch_writer.h" #endif @@ -289,15 +289,21 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, open_connections_(std::make_shared( std::numeric_limits::max(), listener_factory_context_->runtime(), cx_limit_runtime_key_)), - local_init_watcher_(fmt::format("Listener-local-init-watcher {}", name), [this] { - if (workers_started_) { - parent_.onListenerWarmed(*this); - } else { - // Notify Server that this listener is - // ready. - listener_init_target_.ready(); - } - }) { + local_init_watcher_(fmt::format("Listener-local-init-watcher {}", name), + [this] { + if (workers_started_) { + parent_.onListenerWarmed(*this); + } else { + // Notify Server that this listener is + // ready. + listener_init_target_.ready(); + } + }) +#ifdef ENVOY_ENABLE_QUIC + , + quic_stat_names_(parent_.quicStatNames()) +#endif +{ const absl::optional runtime_val = listener_factory_context_->runtime().snapshot().get(cx_limit_runtime_key_); @@ -360,10 +366,16 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, origin.listener_factory_context_->listener_factory_context_base_, this, *this)), filter_chain_manager_(address_, origin.listener_factory_context_->parentFactoryContext(), initManager(), origin.filter_chain_manager_), - local_init_watcher_(fmt::format("Listener-local-init-watcher {}", name), [this] { - ASSERT(workers_started_); - parent_.inPlaceFilterChainUpdate(*this); - }) { + local_init_watcher_(fmt::format("Listener-local-init-watcher {}", name), + [this] { + ASSERT(workers_started_); + parent_.inPlaceFilterChainUpdate(*this); + }) +#ifdef ENVOY_ENABLE_QUIC + , + quic_stat_names_(parent_.quicStatNames()) +#endif +{ buildAccessLog(); auto socket_type = Network::Utility::protobufAddressSocketType(config.address()); buildListenSocketOptions(socket_type); @@ -401,9 +413,9 @@ void ListenerImpl::buildUdpListenerFactory(Network::Socket::Type socket_type, udp_listener_config_ = std::make_unique(config_.udp_listener_config()); if (config_.udp_listener_config().has_quic_options()) { -#if defined(ENVOY_ENABLE_QUIC) +#ifdef ENVOY_ENABLE_QUIC udp_listener_config_->listener_factory_ = std::make_unique( - config_.udp_listener_config().quic_options(), concurrency); + config_.udp_listener_config().quic_options(), concurrency, quic_stat_names_); #if UDP_GSO_BATCH_WRITER_COMPILETIME_SUPPORT // TODO(mattklein123): We should be able to use GSO without QUICHE/QUIC. Right now this causes // non-QUIC integration tests to fail, which I haven't investigated yet. Additionally, from diff --git a/source/server/listener_impl.h b/source/server/listener_impl.h index 2f0b860bd1332..10f5e3a4a760f 100644 --- a/source/server/listener_impl.h +++ b/source/server/listener_impl.h @@ -22,6 +22,10 @@ #include "absl/base/call_once.h" +#ifdef ENVOY_ENABLE_QUIC +#include "common/quic/quic_stat_names.h" +#endif + namespace Envoy { namespace Server { @@ -424,6 +428,10 @@ class ListenerImpl final : public Network::ListenerConfig, // callback during the destroy of ListenerImpl. Init::WatcherImpl local_init_watcher_; +#ifdef ENVOY_ENABLE_QUIC + Quic::QuicStatNames& quic_stat_names_; +#endif + // to access ListenerManagerImpl::factory_. friend class ListenerFilterChainFactoryBuilder; }; diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index 045465f837cee..ad09d62ffb0e2 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -23,6 +23,10 @@ #include "server/lds_api.h" #include "server/listener_impl.h" +#ifdef ENVOY_ENABLE_QUIC +#include "common/quic/quic_stat_names.h" +#endif + namespace Envoy { namespace Server { @@ -202,6 +206,10 @@ class ListenerManagerImpl : public ListenerManager, Logger::Loggable> error_state_tracker_; FailureStates overall_error_state_; +#ifdef ENVOY_ENABLE_QUIC + Quic::QuicStatNames quic_stat_names_ = Quic::QuicStatNames(server_.stats().symbolTable()); +#endif }; class ListenerFilterChainFactoryBuilder : public FilterChainFactoryBuilder { diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index 9f432b55c200a..950bd64e15802 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -54,6 +54,18 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "quic_stat_names_test", + srcs = ["quic_stat_names_test.cc"], + tags = ["nofips"], + deps = [ + "//source/common/quic:quic_stat_names_lib", + "//source/common/stats:stats_lib", + "//test/mocks/stats:stats_mocks", + "//test/test_common:utility_lib", + ], +) + envoy_cc_test( name = "envoy_quic_session_cache_test", srcs = ["envoy_quic_session_cache_test.cc"], diff --git a/test/common/quic/active_quic_listener_test.cc b/test/common/quic/active_quic_listener_test.cc index ffb12a05b56bf..6cbda761c2df2 100644 --- a/test/common/quic/active_quic_listener_test.cc +++ b/test/common/quic/active_quic_listener_test.cc @@ -87,7 +87,8 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { bool use_http3 = GetParam().second == QuicVersionType::Iquic; SetQuicReloadableFlag(quic_disable_version_draft_29, !use_http3); return quic::CurrentSupportedVersions(); - }()[0]) {} + }()[0]), + quic_stat_names_(listener_config_.listenerScope().symbolTable()) {} template std::unique_ptr staticUniquePointerCast(std::unique_ptr&& source) { @@ -155,7 +156,8 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { Network::ActiveUdpListenerFactoryPtr createQuicListenerFactory(const std::string& yaml) { envoy::config::listener::v3::QuicProtocolOptions options; TestUtility::loadFromYamlAndValidate(yaml, options); - return std::make_unique(options, /*concurrency=*/1); + return std::make_unique(options, /*concurrency=*/1, + quic_stat_names_); } void maybeConfigureMocks(int connection_count) { @@ -311,6 +313,7 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { quic::ParsedQuicVersion quic_version_; uint32_t connection_window_size_{1024u}; uint32_t stream_window_size_{1024u}; + QuicStatNames quic_stat_names_; }; INSTANTIATE_TEST_SUITE_P(ActiveQuicListenerTests, ActiveQuicListenerTest, @@ -329,7 +332,7 @@ TEST_P(ActiveQuicListenerTest, FailSocketOptionUponCreation) { listener_config_, quic_config_, options, false, ActiveQuicListenerFactoryPeer::runtimeEnabled( static_cast(listener_factory_.get())), - 32u), + quic_stat_names_, 32u), Network::CreateListenerException, "Failed to apply socket options."); } diff --git a/test/common/quic/envoy_quic_dispatcher_test.cc b/test/common/quic/envoy_quic_dispatcher_test.cc index f46bac22788c2..e9b3844bd903b 100644 --- a/test/common/quic/envoy_quic_dispatcher_test.cc +++ b/test/common/quic/envoy_quic_dispatcher_test.cc @@ -76,13 +76,14 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, per_worker_stats_({ALL_PER_HANDLER_LISTENER_STATS( POOL_COUNTER_PREFIX(listener_config_.listenerScope(), "worker."), POOL_GAUGE_PREFIX(listener_config_.listenerScope(), "worker."))}), + quic_stat_names_(listener_config_.listenerScope().symbolTable()), connection_handler_(*dispatcher_, absl::nullopt), envoy_quic_dispatcher_( &crypto_config_, quic_config_, &version_manager_, std::make_unique(*dispatcher_), std::make_unique(*dispatcher_, *connection_helper_.GetClock()), quic::kQuicDefaultConnectionIdLength, connection_handler_, listener_config_, - listener_stats_, per_worker_stats_, *dispatcher_, *listen_socket_), + listener_stats_, per_worker_stats_, *dispatcher_, *listen_socket_, quic_stat_names_), connection_id_(quic::test::TestConnectionId(1)) { auto writer = new testing::NiceMock(); envoy_quic_dispatcher_.InitializeWithWriter(writer); @@ -252,6 +253,7 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, testing::NiceMock listener_config_; Server::ListenerStats listener_stats_; Server::PerHandlerListenerStats per_worker_stats_; + QuicStatNames quic_stat_names_; Server::ConnectionHandlerImpl connection_handler_; EnvoyQuicDispatcher envoy_quic_dispatcher_; const quic::QuicConnectionId connection_id_; diff --git a/test/common/quic/quic_stat_names_test.cc b/test/common/quic/quic_stat_names_test.cc new file mode 100644 index 0000000000000..4eeda27eabddb --- /dev/null +++ b/test/common/quic/quic_stat_names_test.cc @@ -0,0 +1,40 @@ +#include + +#include "common/quic/quic_stat_names.h" + +#include "test/mocks/stats/mocks.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Quic { + +class QuicStatNamesTest : public testing::Test { +public: + QuicStatNamesTest() : scope_(*symbol_table_), quic_stat_names_(*symbol_table_) {} + + Stats::TestUtil::TestSymbolTable symbol_table_; + Stats::TestUtil::TestStore scope_; + QuicStatNames quic_stat_names_; +}; + +TEST_F(QuicStatNamesTest, QuicConnectionCloseStats) { + quic_stat_names_.chargeQuicConnectionCloseStats(scope_, quic::QUIC_NO_ERROR, + quic::ConnectionCloseSource::FROM_SELF, false); + EXPECT_EQ( + 1U, + scope_.counter("http3.downstream.tx.quic_connection_close_error_code_QUIC_NO_ERROR").value()); +} + +TEST_F(QuicStatNamesTest, OutOfRangeQuicConnectionCloseStats) { + uint64_t bad_error_code = quic::QUIC_LAST_ERROR + 1; + quic_stat_names_.chargeQuicConnectionCloseStats(scope_, + static_cast(bad_error_code), + quic::ConnectionCloseSource::FROM_SELF, false); + EXPECT_EQ(1U, + scope_.counter("http3.downstream.tx.quic_connection_close_error_code_QUIC_LAST_ERROR") + .value()); +} + +} // namespace Quic +} // namespace Envoy diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index 184c2a7d96591..5ae48faf275bf 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -37,6 +37,7 @@ #if defined(ENVOY_ENABLE_QUIC) #include "common/quic/active_quic_listener.h" +#include "common/quic/quic_stat_names.h" #endif #include "server/active_raw_udp_listener_config.h" @@ -751,7 +752,7 @@ class FakeUpstream : Logger::Loggable, if (is_quic) { #if defined(ENVOY_ENABLE_QUIC) udp_listener_config_.listener_factory_ = std::make_unique( - envoy::config::listener::v3::QuicProtocolOptions(), 1); + envoy::config::listener::v3::QuicProtocolOptions(), 1, parent_.quic_stat_names_); #else ASSERT(false, "Running a test that requires QUIC without compiling QUIC"); #endif @@ -840,6 +841,9 @@ class FakeUpstream : Logger::Loggable, Http::Http1::CodecStats::AtomicPtr http1_codec_stats_; Http::Http2::CodecStats::AtomicPtr http2_codec_stats_; Http::Http3::CodecStats::AtomicPtr http3_codec_stats_; +#ifdef ENVOY_ENABLE_QUIC + Quic::QuicStatNames quic_stat_names_ = Quic::QuicStatNames(stats_store_.symbolTable()); +#endif }; using FakeUpstreamPtr = std::unique_ptr; diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 3790873bc89a6..7011445430d25 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -313,6 +313,16 @@ TEST_P(QuicHttpIntegrationTest, ZeroRtt) { ->EarlyDataAccepted()); // Close the second connection. codec_client_->close(); + if (GetParam().first == Network::Address::IpVersion::v4) { + test_server_->waitForCounterEq( + "listener.127.0.0.1_0.http3.downstream.rx.quic_connection_close_error_" + "code_QUIC_NO_ERROR", + 2u); + } else { + test_server_->waitForCounterEq("listener.[__1]_0.http3.downstream.rx.quic_connection_close_" + "error_code_QUIC_NO_ERROR", + 2u); + } } // Ensure multiple quic connections work, regardless of platform BPF support