Coverage Report

Created: 2024-09-19 09:45

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