LCOV - code coverage report
Current view: top level - source/server/admin - admin.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 87 208 41.8 %
Date: 2024-01-05 06:35:25 Functions: 19 37 51.4 %

          Line data    Source code
       1             : #include "source/server/admin/admin.h"
       2             : 
       3             : #include <algorithm>
       4             : #include <cstdint>
       5             : #include <fstream>
       6             : #include <string>
       7             : #include <utility>
       8             : #include <vector>
       9             : 
      10             : #include "envoy/extensions/http/header_validators/envoy_default/v3/header_validator.pb.h"
      11             : #include "envoy/http/header_validator_factory.h"
      12             : #include "envoy/server/hot_restart.h"
      13             : #include "envoy/server/instance.h"
      14             : #include "envoy/server/options.h"
      15             : #include "envoy/upstream/cluster_manager.h"
      16             : #include "envoy/upstream/outlier_detection.h"
      17             : #include "envoy/upstream/upstream.h"
      18             : 
      19             : #include "source/common/access_log/access_log_impl.h"
      20             : #include "source/common/buffer/buffer_impl.h"
      21             : #include "source/common/common/assert.h"
      22             : #include "source/common/common/empty_string.h"
      23             : #include "source/common/common/fmt.h"
      24             : #include "source/common/common/mutex_tracer_impl.h"
      25             : #include "source/common/common/utility.h"
      26             : #include "source/common/formatter/substitution_formatter.h"
      27             : #include "source/common/http/codes.h"
      28             : #include "source/common/http/conn_manager_utility.h"
      29             : #include "source/common/http/header_map_impl.h"
      30             : #include "source/common/http/headers.h"
      31             : #include "source/common/listener_manager/listener_impl.h"
      32             : #include "source/common/memory/utils.h"
      33             : #include "source/common/network/listen_socket_impl.h"
      34             : #include "source/common/protobuf/protobuf.h"
      35             : #include "source/common/protobuf/utility.h"
      36             : #include "source/common/router/config_impl.h"
      37             : #include "source/extensions/request_id/uuid/config.h"
      38             : #include "source/server/admin/utils.h"
      39             : 
      40             : #include "absl/strings/str_join.h"
      41             : #include "absl/strings/str_replace.h"
      42             : #include "absl/strings/string_view.h"
      43             : #include "spdlog/spdlog.h"
      44             : 
      45             : namespace Envoy {
      46             : namespace Server {
      47             : 
      48         771 : ConfigTracker& AdminImpl::getConfigTracker() { return config_tracker_; }
      49             : 
      50             : AdminImpl::NullRouteConfigProvider::NullRouteConfigProvider(TimeSource& time_source)
      51         134 :     : config_(new Router::NullConfigImpl()), time_source_(time_source) {}
      52             : 
      53             : void AdminImpl::startHttpListener(std::list<AccessLog::InstanceSharedPtr> access_logs,
      54             :                                   Network::Address::InstanceConstSharedPtr address,
      55          98 :                                   Network::Socket::OptionsSharedPtr socket_options) {
      56          98 :   access_logs_ = std::move(access_logs);
      57             : 
      58          98 :   null_overload_manager_.start();
      59          98 :   socket_ = std::make_shared<Network::TcpListenSocket>(address, socket_options, true);
      60          98 :   RELEASE_ASSERT(0 == socket_->ioHandle().listen(ENVOY_TCP_BACKLOG_SIZE).return_value_,
      61          98 :                  "listen() failed on admin listener");
      62          98 :   socket_factories_.emplace_back(std::make_unique<AdminListenSocketFactory>(socket_));
      63          98 :   listener_ = std::make_unique<AdminListener>(*this, factory_context_.listenerScope());
      64             : 
      65          98 :   ENVOY_LOG(info, "admin address: {}",
      66          98 :             socket().connectionInfoProvider().localAddress()->asString());
      67             : 
      68          98 :   if (!server_.options().adminAddressPath().empty()) {
      69           0 :     std::ofstream address_out_file(server_.options().adminAddressPath());
      70           0 :     if (!address_out_file) {
      71           0 :       ENVOY_LOG(critical, "cannot open admin address output file {} for writing.",
      72           0 :                 server_.options().adminAddressPath());
      73           0 :     } else {
      74           0 :       address_out_file << socket_->connectionInfoProvider().localAddress()->asString();
      75           0 :     }
      76           0 :   }
      77          98 : }
      78             : 
      79             : namespace {
      80             : // Prepends an element to an array, modifying it as passed in.
      81             : std::vector<absl::string_view> prepend(const absl::string_view first,
      82         134 :                                        std::vector<absl::string_view> strings) {
      83         134 :   strings.insert(strings.begin(), first);
      84         134 :   return strings;
      85         134 : }
      86             : 
      87             : Http::HeaderValidatorFactoryPtr createHeaderValidatorFactory(
      88         134 :     [[maybe_unused]] Server::Configuration::ServerFactoryContext& context) {
      89         134 :   Http::HeaderValidatorFactoryPtr header_validator_factory;
      90             : #ifdef ENVOY_ENABLE_UHV
      91             :   // Default UHV config matches the admin HTTP validation and normalization config
      92             :   ::envoy::extensions::http::header_validators::envoy_default::v3::HeaderValidatorConfig uhv_config;
      93             : 
      94             :   ::envoy::config::core::v3::TypedExtensionConfig config;
      95             :   config.set_name("default_universal_header_validator_for_admin");
      96             :   config.mutable_typed_config()->PackFrom(uhv_config);
      97             : 
      98             :   auto* factory = Envoy::Config::Utility::getFactory<Http::HeaderValidatorFactoryConfig>(config);
      99             :   ENVOY_BUG(factory != nullptr, "Default UHV is not linked into binary.");
     100             : 
     101             :   header_validator_factory = factory->createFromProto(config.typed_config(), context);
     102             :   ENVOY_BUG(header_validator_factory != nullptr, "Unable to create default UHV.");
     103             : #endif
     104         134 :   return header_validator_factory;
     105         134 : }
     106             : 
     107             : } // namespace
     108             : 
     109             : AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server,
     110             :                      bool ignore_global_conn_limit)
     111             :     : server_(server), listener_info_(std::make_shared<ListenerInfoImpl>()),
     112             :       factory_context_(server, listener_info_),
     113             :       request_id_extension_(Extensions::RequestId::UUIDRequestIDExtension::defaultInstance(
     114             :           server_.api().randomGenerator())),
     115             :       profile_path_(profile_path), stats_(Http::ConnectionManagerImpl::generateStats(
     116             :                                        "http.admin.", *server_.stats().rootScope())),
     117             :       null_overload_manager_(server_.threadLocal(), false),
     118             :       tracing_stats_(Http::ConnectionManagerImpl::generateTracingStats("http.admin.",
     119             :                                                                        *no_op_store_.rootScope())),
     120             :       route_config_provider_(server.timeSource()),
     121             :       scoped_route_config_provider_(server.timeSource()), clusters_handler_(server),
     122             :       config_dump_handler_(config_tracker_, server), init_dump_handler_(server),
     123             :       stats_handler_(server), logs_handler_(server), profiling_handler_(profile_path),
     124             :       runtime_handler_(server), listeners_handler_(server), server_cmd_handler_(server),
     125             :       server_info_handler_(server),
     126             :       // TODO(jsedgwick) add /runtime_reset endpoint that removes all admin-set values
     127             :       handlers_{
     128             :           makeHandler("/", "Admin home page", MAKE_ADMIN_HANDLER(handlerAdminHome), false, false),
     129             :           makeHandler("/certs", "print certs on machine",
     130             :                       MAKE_ADMIN_HANDLER(server_info_handler_.handlerCerts), false, false),
     131             :           makeHandler("/clusters", "upstream cluster status",
     132             :                       MAKE_ADMIN_HANDLER(clusters_handler_.handlerClusters), false, false),
     133             :           makeHandler(
     134             :               "/config_dump", "dump current Envoy configs (experimental)",
     135             :               MAKE_ADMIN_HANDLER(config_dump_handler_.handlerConfigDump), false, false,
     136             :               {{Admin::ParamDescriptor::Type::String, "resource", "The resource to dump"},
     137             :                {Admin::ParamDescriptor::Type::String, "mask",
     138             :                 "The mask to apply. When both resource and mask are specified, "
     139             :                 "the mask is applied to every element in the desired repeated field so that only a "
     140             :                 "subset of fields are returned. The mask is parsed as a ProtobufWkt::FieldMask"},
     141             :                {Admin::ParamDescriptor::Type::String, "name_regex",
     142             :                 "Dump only the currently loaded configurations whose names match the specified "
     143             :                 "regex. Can be used with both resource and mask query parameters."},
     144             :                {Admin::ParamDescriptor::Type::Boolean, "include_eds",
     145             :                 "Dump currently loaded configuration including EDS. See the response definition "
     146             :                 "for more information"}}),
     147             :           makeHandler("/init_dump", "dump current Envoy init manager information (experimental)",
     148             :                       MAKE_ADMIN_HANDLER(init_dump_handler_.handlerInitDump), false, false,
     149             :                       {{Admin::ParamDescriptor::Type::String, "mask",
     150             :                         "The desired component to dump unready targets. The mask is parsed as "
     151             :                         "a ProtobufWkt::FieldMask. For example, get the unready targets of "
     152             :                         "all listeners with /init_dump?mask=listener`"}}),
     153             :           makeHandler("/contention", "dump current Envoy mutex contention stats (if enabled)",
     154             :                       MAKE_ADMIN_HANDLER(stats_handler_.handlerContention), false, false),
     155             :           makeHandler("/cpuprofiler", "enable/disable the CPU profiler",
     156             :                       MAKE_ADMIN_HANDLER(profiling_handler_.handlerCpuProfiler), false, true,
     157             :                       {{Admin::ParamDescriptor::Type::Enum,
     158             :                         "enable",
     159             :                         "enables the CPU profiler",
     160             :                         {"y", "n"}}}),
     161             :           makeHandler("/heapprofiler", "enable/disable the heap profiler",
     162             :                       MAKE_ADMIN_HANDLER(profiling_handler_.handlerHeapProfiler), false, true,
     163             :                       {{Admin::ParamDescriptor::Type::Enum,
     164             :                         "enable",
     165             :                         "enable/disable the heap profiler",
     166             :                         {"y", "n"}}}),
     167             :           makeHandler("/heap_dump", "dump current Envoy heap (if supported)",
     168             :                       MAKE_ADMIN_HANDLER(tcmalloc_profiling_handler_.handlerHeapDump), false,
     169             :                       false),
     170             :           makeHandler("/healthcheck/fail", "cause the server to fail health checks",
     171             :                       MAKE_ADMIN_HANDLER(server_cmd_handler_.handlerHealthcheckFail), false, true),
     172             :           makeHandler("/healthcheck/ok", "cause the server to pass health checks",
     173             :                       MAKE_ADMIN_HANDLER(server_cmd_handler_.handlerHealthcheckOk), false, true),
     174             :           makeHandler("/help", "print out list of admin commands", MAKE_ADMIN_HANDLER(handlerHelp),
     175             :                       false, false),
     176             :           makeHandler("/hot_restart_version", "print the hot restart compatibility version",
     177             :                       MAKE_ADMIN_HANDLER(server_info_handler_.handlerHotRestartVersion), false,
     178             :                       false),
     179             : 
     180             :           // The logging "level" parameter, if specified as a non-blank entry,
     181             :           // changes all the logging-paths to that level. So the enum parameter
     182             :           // needs to include a an empty string as the default (first) option.
     183             :           // Thus we prepend an empty string to the logging-levels list.
     184             :           makeHandler("/logging", "query/change logging levels",
     185             :                       MAKE_ADMIN_HANDLER(logs_handler_.handlerLogging), false, true,
     186             :                       {{Admin::ParamDescriptor::Type::String, "paths",
     187             :                         "Change multiple logging levels by setting to "
     188             :                         "<logger_name1>:<desired_level1>,<logger_name2>:<desired_level2>."},
     189             :                        {Admin::ParamDescriptor::Type::Enum, "level", "desired logging level",
     190             :                         prepend("", LogsHandler::levelStrings())}}),
     191             :           makeHandler("/memory", "print current allocation/heap usage",
     192             :                       MAKE_ADMIN_HANDLER(server_info_handler_.handlerMemory), false, false),
     193             :           makeHandler("/quitquitquit", "exit the server",
     194             :                       MAKE_ADMIN_HANDLER(server_cmd_handler_.handlerQuitQuitQuit), false, true),
     195             :           makeHandler("/reset_counters", "reset all counters to zero",
     196             :                       MAKE_ADMIN_HANDLER(stats_handler_.handlerResetCounters), false, true),
     197             :           makeHandler(
     198             :               "/drain_listeners", "drain listeners",
     199             :               MAKE_ADMIN_HANDLER(listeners_handler_.handlerDrainListeners), false, true,
     200             :               {{ParamDescriptor::Type::Boolean, "graceful",
     201             :                 "When draining listeners, enter a graceful drain period prior to closing "
     202             :                 "listeners. This behaviour and duration is configurable via server options "
     203             :                 "or CLI"},
     204             :                {ParamDescriptor::Type::Boolean, "skip_exit",
     205             :                 "When draining listeners, do not exit after the drain period. "
     206             :                 "This must be used with graceful"},
     207             :                {ParamDescriptor::Type::Boolean, "inboundonly",
     208             :                 "Drains all inbound listeners. traffic_direction field in "
     209             :                 "envoy_v3_api_msg_config.listener.v3.Listener is used to determine whether a "
     210             :                 "listener is inbound or outbound."}}),
     211             :           makeHandler("/server_info", "print server version/status information",
     212             :                       MAKE_ADMIN_HANDLER(server_info_handler_.handlerServerInfo), false, false),
     213             :           makeHandler("/ready", "print server state, return 200 if LIVE, otherwise return 503",
     214             :                       MAKE_ADMIN_HANDLER(server_info_handler_.handlerReady), false, false),
     215             :           stats_handler_.statsHandler(false /* not active mode */),
     216             :           makeHandler("/stats/prometheus", "print server stats in prometheus format",
     217             :                       MAKE_ADMIN_HANDLER(stats_handler_.handlerPrometheusStats), false, false,
     218             :                       {{ParamDescriptor::Type::Boolean, "usedonly",
     219             :                         "Only include stats that have been written by system since restart"},
     220             :                        {ParamDescriptor::Type::Boolean, "text_readouts",
     221             :                         "Render text_readouts as new gaugues with value 0 (increases Prometheus "
     222             :                         "data size)"},
     223             :                        {ParamDescriptor::Type::String, "filter",
     224             :                         "Regular expression (Google re2) for filtering stats"}}),
     225             :           makeHandler("/stats/recentlookups", "Show recent stat-name lookups",
     226             :                       MAKE_ADMIN_HANDLER(stats_handler_.handlerStatsRecentLookups), false, false),
     227             :           makeHandler("/stats/recentlookups/clear", "clear list of stat-name lookups and counter",
     228             :                       MAKE_ADMIN_HANDLER(stats_handler_.handlerStatsRecentLookupsClear), false,
     229             :                       true),
     230             :           makeHandler(
     231             :               "/stats/recentlookups/disable", "disable recording of reset stat-name lookup names",
     232             :               MAKE_ADMIN_HANDLER(stats_handler_.handlerStatsRecentLookupsDisable), false, true),
     233             :           makeHandler(
     234             :               "/stats/recentlookups/enable", "enable recording of reset stat-name lookup names",
     235             :               MAKE_ADMIN_HANDLER(stats_handler_.handlerStatsRecentLookupsEnable), false, true),
     236             :           makeHandler("/listeners", "print listener info",
     237             :                       MAKE_ADMIN_HANDLER(listeners_handler_.handlerListenerInfo), false, false,
     238             :                       {{Admin::ParamDescriptor::Type::Enum,
     239             :                         "format",
     240             :                         "File format to use",
     241             :                         {"text", "json"}}}),
     242             :           makeHandler("/runtime", "print runtime values",
     243             :                       MAKE_ADMIN_HANDLER(runtime_handler_.handlerRuntime), false, false),
     244             :           makeHandler("/runtime_modify",
     245             :                       "Adds or modifies runtime values as passed in query parameters. To delete a "
     246             :                       "previously added key, use an empty string as the value. Note that deletion "
     247             :                       "only applies to overrides added via this endpoint; values loaded from disk "
     248             :                       "can be modified via override but not deleted. E.g. "
     249             :                       "?key1=value1&key2=value2...",
     250             :                       MAKE_ADMIN_HANDLER(runtime_handler_.handlerRuntimeModify), false, true),
     251             :           makeHandler("/reopen_logs", "reopen access logs",
     252             :                       MAKE_ADMIN_HANDLER(logs_handler_.handlerReopenLogs), false, true),
     253             :       },
     254             :       date_provider_(server.dispatcher().timeSource()),
     255             :       admin_filter_chain_(std::make_shared<AdminFilterChain>()),
     256             :       local_reply_(LocalReply::Factory::createDefault()),
     257             :       ignore_global_conn_limit_(ignore_global_conn_limit),
     258         134 :       header_validator_factory_(createHeaderValidatorFactory(server.serverFactoryContext())) {
     259             : #ifndef NDEBUG
     260             :   // Verify that no duplicate handlers exist.
     261             :   absl::flat_hash_set<absl::string_view> handlers;
     262             :   for (const UrlHandler& handler : handlers_) {
     263             :     ASSERT(handlers.insert(handler.prefix_).second);
     264             :   }
     265             : #endif
     266         134 : }
     267             : 
     268             : Http::ServerConnectionPtr AdminImpl::createCodec(Network::Connection& connection,
     269             :                                                  const Buffer::Instance& data,
     270             :                                                  Http::ServerConnectionCallbacks& callbacks,
     271          98 :                                                  Server::OverloadManager& overload_manager) {
     272          98 :   return Http::ConnectionManagerUtility::autoCreateCodec(
     273          98 :       connection, data, callbacks, *server_.stats().rootScope(), server_.api().randomGenerator(),
     274          98 :       http1_codec_stats_, http2_codec_stats_, Http::Http1Settings(),
     275          98 :       ::Envoy::Http2::Utility::initializeAndValidateOptions(
     276          98 :           envoy::config::core::v3::Http2ProtocolOptions()),
     277          98 :       maxRequestHeadersKb(), maxRequestHeadersCount(), headersWithUnderscoresAction(),
     278          98 :       overload_manager);
     279          98 : }
     280             : 
     281             : bool AdminImpl::createNetworkFilterChain(Network::Connection& connection,
     282          98 :                                          const Filter::NetworkFilterFactoriesList&) {
     283             :   // Pass in the null overload manager so that the admin interface is accessible even when Envoy
     284             :   // is overloaded.
     285          98 :   connection.addReadFilter(Network::ReadFilterSharedPtr{new Http::ConnectionManagerImpl(
     286          98 :       *this, server_.drainManager(), server_.api().randomGenerator(), server_.httpContext(),
     287          98 :       server_.runtime(), server_.localInfo(), server_.clusterManager(), null_overload_manager_,
     288          98 :       server_.timeSource())});
     289          98 :   return true;
     290          98 : }
     291             : 
     292             : bool AdminImpl::createFilterChain(Http::FilterChainManager& manager, bool,
     293          98 :                                   const Http::FilterChainOptions&) const {
     294          98 :   Http::FilterFactoryCb factory = [this](Http::FilterChainFactoryCallbacks& callbacks) {
     295          98 :     callbacks.addStreamFilter(std::make_shared<AdminFilter>(createRequestFunction()));
     296          98 :   };
     297          98 :   manager.applyFilterFactoryCb({}, factory);
     298          98 :   return true;
     299          98 : }
     300             : 
     301             : namespace {
     302             : // Implements a chunked request for static text.
     303             : class StaticTextRequest : public Admin::Request {
     304             : public:
     305           0 :   StaticTextRequest(absl::string_view response_text, Http::Code code) : code_(code) {
     306           0 :     response_text_.add(response_text);
     307           0 :   }
     308           0 :   StaticTextRequest(Buffer::Instance& response_text, Http::Code code) : code_(code) {
     309           0 :     response_text_.move(response_text);
     310           0 :   }
     311             : 
     312           0 :   Http::Code start(Http::ResponseHeaderMap&) override { return code_; }
     313           0 :   bool nextChunk(Buffer::Instance& response) override {
     314           0 :     response.move(response_text_);
     315           0 :     return false;
     316           0 :   }
     317             : 
     318             : private:
     319             :   Buffer::OwnedImpl response_text_;
     320             :   const Http::Code code_;
     321             : };
     322             : 
     323             : // Implements a streaming Request based on a non-streaming callback that
     324             : // generates the entire admin output in one shot.
     325             : class RequestGasket : public Admin::Request {
     326             : public:
     327             :   RequestGasket(Admin::HandlerCb handler_cb, AdminStream& admin_stream)
     328          98 :       : handler_cb_(handler_cb), admin_stream_(admin_stream) {}
     329             : 
     330        3886 :   static Admin::GenRequestFn makeGen(Admin::HandlerCb callback) {
     331        3886 :     return [callback](AdminStream& admin_stream) -> Server::Admin::RequestPtr {
     332          98 :       return std::make_unique<RequestGasket>(callback, admin_stream);
     333          98 :     };
     334        3886 :   }
     335             : 
     336          98 :   Http::Code start(Http::ResponseHeaderMap& response_headers) override {
     337          98 :     return handler_cb_(response_headers, response_, admin_stream_);
     338          98 :   }
     339             : 
     340          98 :   bool nextChunk(Buffer::Instance& response) override {
     341          98 :     response.move(response_);
     342          98 :     return false;
     343          98 :   }
     344             : 
     345             : private:
     346             :   Admin::HandlerCb handler_cb_;
     347             :   AdminStream& admin_stream_;
     348             :   Buffer::OwnedImpl response_;
     349             : };
     350             : 
     351             : } // namespace
     352             : 
     353           0 : Admin::RequestPtr Admin::makeStaticTextRequest(absl::string_view response, Http::Code code) {
     354           0 :   return std::make_unique<StaticTextRequest>(response, code);
     355           0 : }
     356             : 
     357           0 : Admin::RequestPtr Admin::makeStaticTextRequest(Buffer::Instance& response, Http::Code code) {
     358           0 :   return std::make_unique<StaticTextRequest>(response, code);
     359           0 : }
     360             : 
     361             : Http::Code AdminImpl::runCallback(Http::ResponseHeaderMap& response_headers,
     362           0 :                                   Buffer::Instance& response, AdminStream& admin_stream) {
     363           0 :   RequestPtr request = makeRequest(admin_stream);
     364           0 :   Http::Code code = request->start(response_headers);
     365           0 :   bool more_data;
     366           0 :   do {
     367           0 :     more_data = request->nextChunk(response);
     368           0 :   } while (more_data);
     369           0 :   Memory::Utils::tryShrinkHeap();
     370           0 :   return code;
     371           0 : }
     372             : 
     373          98 : Admin::RequestPtr AdminImpl::makeRequest(AdminStream& admin_stream) const {
     374          98 :   absl::string_view path_and_query = admin_stream.getRequestHeaders().getPathValue();
     375          98 :   std::string::size_type query_index = path_and_query.find('?');
     376          98 :   if (query_index == std::string::npos) {
     377          98 :     query_index = path_and_query.size();
     378          98 :   }
     379             : 
     380         392 :   for (const UrlHandler& handler : handlers_) {
     381         392 :     if (path_and_query.compare(0, query_index, handler.prefix_) == 0) {
     382          98 :       if (handler.mutates_server_state_) {
     383           0 :         const absl::string_view method = admin_stream.getRequestHeaders().getMethodValue();
     384           0 :         if (method != Http::Headers::get().MethodValues.Post) {
     385           0 :           ENVOY_LOG(error, "admin path \"{}\" mutates state, method={} rather than POST",
     386           0 :                     handler.prefix_, method);
     387           0 :           return Admin::makeStaticTextRequest(
     388           0 :               fmt::format("Method {} not allowed, POST required.", method),
     389           0 :               Http::Code::MethodNotAllowed);
     390           0 :         }
     391           0 :       }
     392             : 
     393          98 :       ASSERT(admin_stream.getRequestHeaders().getPathValue() == path_and_query);
     394          98 :       return handler.handler_(admin_stream);
     395          98 :     }
     396         392 :   }
     397             : 
     398             :   // Extra space is emitted below to have "invalid path." be a separate sentence in the
     399             :   // 404 output from "admin commands are:" in handlerHelp.
     400           0 :   Buffer::OwnedImpl error_response;
     401           0 :   error_response.add("invalid path. ");
     402           0 :   getHelp(error_response);
     403           0 :   return Admin::makeStaticTextRequest(error_response, Http::Code::NotFound);
     404          98 : }
     405             : 
     406           0 : std::vector<const AdminImpl::UrlHandler*> AdminImpl::sortedHandlers() const {
     407           0 :   std::vector<const UrlHandler*> sorted_handlers;
     408           0 :   for (const UrlHandler& handler : handlers_) {
     409           0 :     sorted_handlers.push_back(&handler);
     410           0 :   }
     411             :   // Note: it's generally faster to sort a vector with std::vector than to construct a std::map.
     412           0 :   std::sort(sorted_handlers.begin(), sorted_handlers.end(),
     413           0 :             [](const UrlHandler* h1, const UrlHandler* h2) { return h1->prefix_ < h2->prefix_; });
     414           0 :   return sorted_handlers;
     415           0 : }
     416             : 
     417             : Http::Code AdminImpl::handlerHelp(Http::ResponseHeaderMap&, Buffer::Instance& response,
     418           0 :                                   AdminStream&) {
     419           0 :   getHelp(response);
     420           0 :   return Http::Code::OK;
     421           0 : }
     422             : 
     423           0 : void AdminImpl::getHelp(Buffer::Instance& response) const {
     424           0 :   response.add("admin commands are:\n");
     425             : 
     426             :   // Prefix order is used during searching, but for printing do them in alpha order.
     427           0 :   for (const UrlHandler* handler : sortedHandlers()) {
     428           0 :     const absl::string_view method = handler->mutates_server_state_ ? " (POST)" : "";
     429           0 :     response.add(fmt::format("  {}{}: {}\n", handler->prefix_, method, handler->help_text_));
     430           0 :     for (const ParamDescriptor& param : handler->params_) {
     431           0 :       response.add(fmt::format("      {}: {}", param.id_, param.help_));
     432           0 :       if (param.type_ == ParamDescriptor::Type::Enum) {
     433           0 :         response.addFragments({"; One of (", absl::StrJoin(param.enum_choices_, ", "), ")"});
     434           0 :       }
     435           0 :       response.add("\n");
     436           0 :     }
     437           0 :   }
     438           0 : }
     439             : 
     440          98 : const Network::Address::Instance& AdminImpl::localAddress() {
     441          98 :   return *server_.localInfo().address();
     442          98 : }
     443             : 
     444             : AdminImpl::UrlHandler AdminImpl::makeHandler(const std::string& prefix,
     445             :                                              const std::string& help_text, HandlerCb callback,
     446             :                                              bool removable, bool mutates_state,
     447        3886 :                                              const ParamDescriptorVec& params) {
     448        3886 :   return UrlHandler{prefix,    help_text,     RequestGasket::makeGen(callback),
     449        3886 :                     removable, mutates_state, params};
     450        3886 : }
     451             : 
     452             : bool AdminImpl::addStreamingHandler(const std::string& prefix, const std::string& help_text,
     453             :                                     GenRequestFn callback, bool removable, bool mutates_state,
     454           0 :                                     const ParamDescriptorVec& params) {
     455           0 :   ASSERT(prefix.size() > 1);
     456           0 :   ASSERT(prefix[0] == '/');
     457             : 
     458             :   // Sanitize prefix and help_text to ensure no XSS can be injected, as
     459             :   // we are injecting these strings into HTML that runs in a domain that
     460             :   // can mutate Envoy server state. Also rule out some characters that
     461             :   // make no sense as part of a URL path: ? and :.
     462           0 :   const std::string::size_type pos = prefix.find_first_of("&\"'<>?:");
     463           0 :   if (pos != std::string::npos) {
     464           0 :     ENVOY_LOG(error, "filter \"{}\" contains invalid character '{}'", prefix, prefix[pos]);
     465           0 :     return false;
     466           0 :   }
     467             : 
     468           0 :   auto it = std::find_if(handlers_.cbegin(), handlers_.cend(),
     469           0 :                          [&prefix](const UrlHandler& entry) { return prefix == entry.prefix_; });
     470           0 :   if (it == handlers_.end()) {
     471           0 :     handlers_.push_back({prefix, help_text, callback, removable, mutates_state, params});
     472           0 :     return true;
     473           0 :   }
     474           0 :   return false;
     475           0 : }
     476             : 
     477             : bool AdminImpl::addHandler(const std::string& prefix, const std::string& help_text,
     478             :                            HandlerCb callback, bool removable, bool mutates_state,
     479           0 :                            const ParamDescriptorVec& params) {
     480           0 :   return addStreamingHandler(prefix, help_text, RequestGasket::makeGen(callback), removable,
     481           0 :                              mutates_state, params);
     482           0 : }
     483             : 
     484           0 : bool AdminImpl::removeHandler(const std::string& prefix) {
     485           0 :   const size_t size_before_removal = handlers_.size();
     486           0 :   handlers_.remove_if(
     487           0 :       [&prefix](const UrlHandler& entry) { return prefix == entry.prefix_ && entry.removable_; });
     488           0 :   if (handlers_.size() != size_before_removal) {
     489           0 :     return true;
     490           0 :   }
     491           0 :   return false;
     492           0 : }
     493             : 
     494             : Http::Code AdminImpl::request(absl::string_view path_and_query, absl::string_view method,
     495           0 :                               Http::ResponseHeaderMap& response_headers, std::string& body) {
     496           0 :   AdminFilter filter(createRequestFunction());
     497             : 
     498           0 :   auto request_headers = Http::RequestHeaderMapImpl::create();
     499           0 :   request_headers->setMethod(method);
     500           0 :   request_headers->setPath(path_and_query);
     501           0 :   filter.decodeHeaders(*request_headers, false);
     502           0 :   Buffer::OwnedImpl response;
     503             : 
     504           0 :   Http::Code code = runCallback(response_headers, response, filter);
     505           0 :   Utility::populateFallbackResponseHeaders(code, response_headers);
     506           0 :   body = response.toString();
     507           0 :   return code;
     508           0 : }
     509             : 
     510           0 : void AdminImpl::closeSocket() {
     511           0 :   if (socket_) {
     512           0 :     socket_->close();
     513           0 :   }
     514           0 : }
     515             : 
     516          98 : void AdminImpl::addListenerToHandler(Network::ConnectionHandler* handler) {
     517          98 :   if (listener_) {
     518          98 :     handler->addListener(absl::nullopt, *listener_, server_.runtime(),
     519          98 :                          server_.api().randomGenerator());
     520          98 :   }
     521          98 : }
     522             : 
     523             : #ifdef ENVOY_ENABLE_UHV
     524             : ::Envoy::Http::HeaderValidatorStats&
     525             : AdminImpl::getHeaderValidatorStats([[maybe_unused]] Http::Protocol protocol) {
     526             :   switch (protocol) {
     527             :   case Http::Protocol::Http10:
     528             :   case Http::Protocol::Http11:
     529             :     return Http::Http1::CodecStats::atomicGet(http1_codec_stats_, *server_.stats().rootScope());
     530             :   case Http::Protocol::Http3:
     531             :     IS_ENVOY_BUG("HTTP/3 is not supported for admin UI");
     532             :     // Return H/2 stats object, since we do not have H/3 stats.
     533             :     ABSL_FALLTHROUGH_INTENDED;
     534             :   case Http::Protocol::Http2:
     535             :     return Http::Http2::CodecStats::atomicGet(http2_codec_stats_, *server_.stats().rootScope());
     536             :   }
     537             :   PANIC_DUE_TO_CORRUPT_ENUM;
     538             : }
     539             : #endif
     540             : 
     541             : } // namespace Server
     542             : } // namespace Envoy

Generated by: LCOV version 1.15