Coverage Report

Created: 2023-11-12 09:30

/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/memory/utils.h"
32
#include "source/common/network/listen_socket_impl.h"
33
#include "source/common/protobuf/protobuf.h"
34
#include "source/common/protobuf/utility.h"
35
#include "source/common/router/config_impl.h"
36
#include "source/extensions/listener_managers/listener_manager/listener_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
26.4k
ConfigTracker& AdminImpl::getConfigTracker() { return config_tracker_; }
49
50
AdminImpl::NullRouteConfigProvider::NullRouteConfigProvider(TimeSource& time_source)
51
5.34k
    : config_(new Router::NullConfigImpl()), time_source_(time_source) {}
52
53
void AdminImpl::startHttpListener(const std::list<AccessLog::InstanceSharedPtr>& access_logs,
54
                                  const std::string& address_out_path,
55
                                  Network::Address::InstanceConstSharedPtr address,
56
                                  const Network::Socket::OptionsSharedPtr& socket_options,
57
2.64k
                                  Stats::ScopeSharedPtr&& listener_scope) {
58
2.64k
  for (const auto& access_log : access_logs) {
59
2.64k
    access_logs_.emplace_back(access_log);
60
2.64k
  }
61
2.64k
  null_overload_manager_.start();
62
2.64k
  socket_ = std::make_shared<Network::TcpListenSocket>(address, socket_options, true);
63
2.64k
  RELEASE_ASSERT(0 == socket_->ioHandle().listen(ENVOY_TCP_BACKLOG_SIZE).return_value_,
64
2.64k
                 "listen() failed on admin listener");
65
2.64k
  socket_factories_.emplace_back(std::make_unique<AdminListenSocketFactory>(socket_));
66
2.64k
  listener_ = std::make_unique<AdminListener>(*this, std::move(listener_scope));
67
2.64k
  ENVOY_LOG(info, "admin address: {}",
68
2.64k
            socket().connectionInfoProvider().localAddress()->asString());
69
2.64k
  if (!address_out_path.empty()) {
70
0
    std::ofstream address_out_file(address_out_path);
71
0
    if (!address_out_file) {
72
0
      ENVOY_LOG(critical, "cannot open admin address output file {} for writing.",
73
0
                address_out_path);
74
0
    } else {
75
0
      address_out_file << socket_->connectionInfoProvider().localAddress()->asString();
76
0
    }
77
0
  }
78
2.64k
}
79
80
namespace {
81
// Prepends an element to an array, modifying it as passed in.
82
std::vector<absl::string_view> prepend(const absl::string_view first,
83
5.34k
                                       std::vector<absl::string_view> strings) {
84
5.34k
  strings.insert(strings.begin(), first);
85
5.34k
  return strings;
86
5.34k
}
87
88
Http::HeaderValidatorFactoryPtr createHeaderValidatorFactory(
89
5.34k
    [[maybe_unused]] Server::Configuration::ServerFactoryContext& context) {
90
5.34k
  Http::HeaderValidatorFactoryPtr header_validator_factory;
91
#ifdef ENVOY_ENABLE_UHV
92
  // Default UHV config matches the admin HTTP validation and normalization config
93
  ::envoy::extensions::http::header_validators::envoy_default::v3::HeaderValidatorConfig uhv_config;
94
95
  ::envoy::config::core::v3::TypedExtensionConfig config;
96
  config.set_name("default_universal_header_validator_for_admin");
97
  config.mutable_typed_config()->PackFrom(uhv_config);
98
99
  auto* factory = Envoy::Config::Utility::getFactory<Http::HeaderValidatorFactoryConfig>(config);
100
  ENVOY_BUG(factory != nullptr, "Default UHV is not linked into binary.");
101
102
  header_validator_factory = factory->createFromProto(config.typed_config(), context);
103
  ENVOY_BUG(header_validator_factory != nullptr, "Unable to create default UHV.");
104
#endif
105
5.34k
  return header_validator_factory;
106
5.34k
}
107
108
} // namespace
109
110
AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server,
111
                     bool ignore_global_conn_limit)
112
    : server_(server),
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()),
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
5.34k
      header_validator_factory_(createHeaderValidatorFactory(server.serverFactoryContext())) {
259
5.34k
#ifndef NDEBUG
260
  // Verify that no duplicate handlers exist.
261
5.34k
  absl::flat_hash_set<absl::string_view> handlers;
262
160k
  for (const UrlHandler& handler : handlers_) {
263
160k
    ASSERT(handlers.insert(handler.prefix_).second);
264
160k
  }
265
5.34k
#endif
266
5.34k
}
267
268
Http::ServerConnectionPtr AdminImpl::createCodec(Network::Connection& connection,
269
                                                 const Buffer::Instance& data,
270
                                                 Http::ServerConnectionCallbacks& callbacks,
271
2.64k
                                                 Server::OverloadManager& overload_manager) {
272
2.64k
  return Http::ConnectionManagerUtility::autoCreateCodec(
273
2.64k
      connection, data, callbacks, *server_.stats().rootScope(), server_.api().randomGenerator(),
274
2.64k
      http1_codec_stats_, http2_codec_stats_, Http::Http1Settings(),
275
2.64k
      ::Envoy::Http2::Utility::initializeAndValidateOptions(
276
2.64k
          envoy::config::core::v3::Http2ProtocolOptions()),
277
2.64k
      maxRequestHeadersKb(), maxRequestHeadersCount(), headersWithUnderscoresAction(),
278
2.64k
      overload_manager);
279
2.64k
}
280
281
bool AdminImpl::createNetworkFilterChain(Network::Connection& connection,
282
2.64k
                                         const Filter::NetworkFilterFactoriesList&) {
283
  // Pass in the null overload manager so that the admin interface is accessible even when Envoy
284
  // is overloaded.
285
2.64k
  connection.addReadFilter(Network::ReadFilterSharedPtr{new Http::ConnectionManagerImpl(
286
2.64k
      *this, server_.drainManager(), server_.api().randomGenerator(), server_.httpContext(),
287
2.64k
      server_.runtime(), server_.localInfo(), server_.clusterManager(), null_overload_manager_,
288
2.64k
      server_.timeSource())});
289
2.64k
  return true;
290
2.64k
}
291
292
bool AdminImpl::createFilterChain(Http::FilterChainManager& manager, bool,
293
2.64k
                                  const Http::FilterChainOptions&) const {
294
2.64k
  Http::FilterFactoryCb factory = [this](Http::FilterChainFactoryCallbacks& callbacks) {
295
2.64k
    callbacks.addStreamFilter(std::make_shared<AdminFilter>(createRequestFunction()));
296
2.64k
  };
297
2.64k
  manager.applyFilterFactoryCb({}, factory);
298
2.64k
  return true;
299
2.64k
}
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
2.64k
      : handler_cb_(handler_cb), admin_stream_(admin_stream) {}
329
330
155k
  static Admin::GenRequestFn makeGen(Admin::HandlerCb callback) {
331
155k
    return [callback](AdminStream& admin_stream) -> Server::Admin::RequestPtr {
332
2.64k
      return std::make_unique<RequestGasket>(callback, admin_stream);
333
2.64k
    };
334
155k
  }
335
336
2.64k
  Http::Code start(Http::ResponseHeaderMap& response_headers) override {
337
2.64k
    return handler_cb_(response_headers, response_, admin_stream_);
338
2.64k
  }
339
340
2.64k
  bool nextChunk(Buffer::Instance& response) override {
341
2.64k
    response.move(response_);
342
2.64k
    return false;
343
2.64k
  }
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
2.64k
Admin::RequestPtr AdminImpl::makeRequest(AdminStream& admin_stream) const {
374
2.64k
  absl::string_view path_and_query = admin_stream.getRequestHeaders().getPathValue();
375
2.64k
  std::string::size_type query_index = path_and_query.find('?');
376
2.64k
  if (query_index == std::string::npos) {
377
2.64k
    query_index = path_and_query.size();
378
2.64k
  }
379
380
10.5k
  for (const UrlHandler& handler : handlers_) {
381
10.5k
    if (path_and_query.compare(0, query_index, handler.prefix_) == 0) {
382
2.64k
      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
2.64k
      ASSERT(admin_stream.getRequestHeaders().getPathValue() == path_and_query);
394
2.64k
      return handler.handler_(admin_stream);
395
2.64k
    }
396
10.5k
  }
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
2.64k
}
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
2.64k
const Network::Address::Instance& AdminImpl::localAddress() {
441
2.64k
  return *server_.localInfo().address();
442
2.64k
}
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
155k
                                             const ParamDescriptorVec& params) {
448
155k
  return UrlHandler{prefix,    help_text,     RequestGasket::makeGen(callback),
449
155k
                    removable, mutates_state, params};
450
155k
}
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
2.64k
void AdminImpl::addListenerToHandler(Network::ConnectionHandler* handler) {
517
2.64k
  if (listener_) {
518
2.64k
    handler->addListener(absl::nullopt, *listener_, server_.runtime());
519
2.64k
  }
520
2.64k
}
521
522
#ifdef ENVOY_ENABLE_UHV
523
::Envoy::Http::HeaderValidatorStats&
524
AdminImpl::getHeaderValidatorStats([[maybe_unused]] Http::Protocol protocol) {
525
  switch (protocol) {
526
  case Http::Protocol::Http10:
527
  case Http::Protocol::Http11:
528
    return Http::Http1::CodecStats::atomicGet(http1_codec_stats_, *server_.stats().rootScope());
529
  case Http::Protocol::Http3:
530
    IS_ENVOY_BUG("HTTP/3 is not supported for admin UI");
531
    // Return H/2 stats object, since we do not have H/3 stats.
532
    ABSL_FALLTHROUGH_INTENDED;
533
  case Http::Protocol::Http2:
534
    return Http::Http2::CodecStats::atomicGet(http2_codec_stats_, *server_.stats().rootScope());
535
  }
536
  PANIC_DUE_TO_CORRUPT_ENUM;
537
}
538
#endif
539
540
} // namespace Server
541
} // namespace Envoy