-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
admin: Streaming /stats implementation #19693
Changes from 95 commits
acc5325
34ea762
75861f1
74fcba9
faccfd7
ca5aba0
582951d
5878ecd
87d89aa
29f80b4
99c8777
e2fa8e2
7820e00
44d2d0a
b2d5a8d
b8ca697
5afc02a
1b7695a
0f58de7
0613f53
cc6243f
82d32b9
aa0d5a0
ba7c63b
b4c8141
4b421af
08d5475
c006b04
f2cbfc3
766381a
29e32ad
0eb9ffb
059a32f
bda9297
0d79f4c
ddcbcb0
c9473da
80de8a8
71444c0
554c0cd
4a941bd
c702167
7ea16f3
37f9b9a
aff664f
e6de771
d3742cd
c287a96
6e52482
d02ab70
4f28d1e
920123a
47ca61b
6609dbc
234da74
59526dd
17e9f73
0170399
fd76570
7035035
ecc2708
4e62747
49a632e
f35e21b
805e7b5
dee72d8
ca81c8d
b8660f5
ddeaba1
d2405e9
a1140e6
27b748c
b9152a7
256d681
b5ed846
4e34dba
91cd012
cb935f3
2f4c4b9
d8218ca
8ffed3c
126bddc
cad4d12
a099aa5
d8529f3
94be4b7
81e37cc
0e0bf3d
7dc9a55
72743e5
5e62221
9fc63d0
f594088
9731b03
5d628e4
59c7a0f
92e115e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
5.0.0 | ||
5.1.0 | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -199,8 +199,7 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, | |
MAKE_ADMIN_HANDLER(server_info_handler_.handlerServerInfo), false, false), | ||
makeHandler("/ready", "print server state, return 200 if LIVE, otherwise return 503", | ||
MAKE_ADMIN_HANDLER(server_info_handler_.handlerReady), false, false), | ||
makeHandler("/stats", "print server stats", | ||
MAKE_ADMIN_HANDLER(stats_handler_.handlerStats), false, false), | ||
makeStreamingHandler("/stats", "print server stats", stats_handler_, false, false), | ||
makeHandler("/stats/prometheus", "print server stats in prometheus format", | ||
MAKE_ADMIN_HANDLER(stats_handler_.handlerPrometheusStats), false, false), | ||
makeHandler("/stats/recentlookups", "Show recent stat-name lookups", | ||
|
@@ -251,41 +250,45 @@ bool AdminImpl::createNetworkFilterChain(Network::Connection& connection, | |
} | ||
|
||
void AdminImpl::createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) { | ||
callbacks.addStreamFilter(std::make_shared<AdminFilter>(createHandlerFunction())); | ||
callbacks.addStreamFilter(std::make_shared<AdminFilter>(createRequestFunction())); | ||
} | ||
|
||
namespace { | ||
|
||
// Implements a chunked handler for static text. | ||
class StaticTextHandler : public Admin::Handler { | ||
// Implements a chunked request for static text. | ||
class StaticTextRequest : public Admin::Request { | ||
public: | ||
StaticTextHandler(absl::string_view response_text, Http::Code code) | ||
: response_text_(std::string(response_text)), code_(code) {} | ||
StaticTextRequest(absl::string_view response_text, Http::Code code) : code_(code) { | ||
response_text_.add(response_text); | ||
} | ||
StaticTextRequest(Buffer::Instance& response_text, Http::Code code) : code_(code) { | ||
response_text_.move(response_text); | ||
} | ||
|
||
Http::Code start(Http::ResponseHeaderMap&) override { return code_; } | ||
bool nextChunk(Buffer::Instance& response) override { | ||
response.add(response_text_); | ||
response.move(response_text_); | ||
return false; | ||
} | ||
|
||
private: | ||
const std::string response_text_; | ||
Buffer::OwnedImpl response_text_; | ||
const Http::Code code_; | ||
}; | ||
|
||
// Implements a Chunked Handler implementation based on a non-chunked callback | ||
// that generates the entire admin output in one shot. | ||
class HandlerGasket : public Admin::Handler { | ||
// Implements a streaming Request based on a non-streaming callback that | ||
// generates the entire admin output in one shot. | ||
class RequestGasket : public Admin::Request { | ||
public: | ||
HandlerGasket(Admin::HandlerCb handler_cb, absl::string_view path_and_query, | ||
RequestGasket(Admin::HandlerCb handler_cb, absl::string_view path_and_query, | ||
AdminStream& admin_stream) | ||
: path_and_query_(std::string(path_and_query)), handler_cb_(handler_cb), | ||
admin_stream_(admin_stream) {} | ||
|
||
static Admin::GenHandlerCb makeGen(Admin::HandlerCb callback) { | ||
static Admin::GenRequestFn makeGen(Admin::HandlerCb callback) { | ||
return [callback](absl::string_view path_and_query, | ||
AdminStream& admin_stream) -> Server::Admin::HandlerPtr { | ||
return std::make_unique<HandlerGasket>(callback, path_and_query, admin_stream); | ||
AdminStream& admin_stream) -> Server::Admin::RequestPtr { | ||
return std::make_unique<RequestGasket>(callback, path_and_query, admin_stream); | ||
}; | ||
} | ||
|
||
|
@@ -307,24 +310,28 @@ class HandlerGasket : public Admin::Handler { | |
|
||
} // namespace | ||
|
||
Admin::HandlerPtr AdminImpl::makeStaticTextHandler(absl::string_view response, Http::Code code) { | ||
return std::make_unique<StaticTextHandler>(response, code); | ||
Admin::RequestPtr Admin::makeStaticTextRequest(absl::string_view response, Http::Code code) { | ||
return std::make_unique<StaticTextRequest>(response, code); | ||
} | ||
|
||
Admin::RequestPtr Admin::makeStaticTextRequest(Buffer::Instance& response, Http::Code code) { | ||
return std::make_unique<StaticTextRequest>(response, code); | ||
} | ||
|
||
Http::Code AdminImpl::runCallback(absl::string_view path_and_query, | ||
Http::ResponseHeaderMap& response_headers, | ||
Buffer::Instance& response, AdminStream& admin_stream) { | ||
HandlerPtr handler = findHandler(path_and_query, admin_stream); | ||
Http::Code code = handler->start(response_headers); | ||
RequestPtr request = makeRequest(path_and_query, admin_stream); | ||
Http::Code code = request->start(response_headers); | ||
bool more_data; | ||
do { | ||
more_data = handler->nextChunk(response); | ||
more_data = request->nextChunk(response); | ||
} while (more_data); | ||
Memory::Utils::tryShrinkHeap(); | ||
return code; | ||
} | ||
|
||
Admin::HandlerPtr AdminImpl::findHandler(absl::string_view path_and_query, | ||
Admin::RequestPtr AdminImpl::makeRequest(absl::string_view path_and_query, | ||
AdminStream& admin_stream) { | ||
std::string::size_type query_index = path_and_query.find('?'); | ||
if (query_index == std::string::npos) { | ||
|
@@ -338,8 +345,9 @@ Admin::HandlerPtr AdminImpl::findHandler(absl::string_view path_and_query, | |
if (method != Http::Headers::get().MethodValues.Post) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense making handlers_ a flat_hash_set? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It wouldn't work as a flat_hash_set because it does prefix matching. It could be an rb_tree or std::map though. This iterative loop already exists though and I don't want to change it in this PR. I will leave a TODO |
||
ENVOY_LOG(error, "admin path \"{}\" mutates state, method={} rather than POST", | ||
handler.prefix_, method); | ||
return makeStaticTextHandler(fmt::format("Method {} not allowed, POST required.", method), | ||
Http::Code::MethodNotAllowed); | ||
return Admin::makeStaticTextRequest( | ||
fmt::format("Method {} not allowed, POST required.", method), | ||
Http::Code::MethodNotAllowed); | ||
} | ||
} | ||
|
||
|
@@ -352,7 +360,7 @@ Admin::HandlerPtr AdminImpl::findHandler(absl::string_view path_and_query, | |
Buffer::OwnedImpl error_response; | ||
error_response.add("invalid path. "); | ||
getHelp(error_response); | ||
return makeStaticTextHandler(error_response.toString(), Http::Code::NotFound); | ||
return Admin::makeStaticTextRequest(error_response, Http::Code::NotFound); | ||
} | ||
|
||
std::vector<const AdminImpl::UrlHandler*> AdminImpl::sortedHandlers() const { | ||
|
@@ -435,11 +443,11 @@ const Network::Address::Instance& AdminImpl::localAddress() { | |
AdminImpl::UrlHandler AdminImpl::makeHandler(const std::string& prefix, | ||
const std::string& help_text, HandlerCb callback, | ||
bool removable, bool mutates_state) { | ||
return UrlHandler{prefix, help_text, HandlerGasket::makeGen(callback), removable, mutates_state}; | ||
return UrlHandler{prefix, help_text, RequestGasket::makeGen(callback), removable, mutates_state}; | ||
} | ||
|
||
bool AdminImpl::addChunkedHandler(const std::string& prefix, const std::string& help_text, | ||
GenHandlerCb callback, bool removable, bool mutates_state) { | ||
bool AdminImpl::addStreamingHandler(const std::string& prefix, const std::string& help_text, | ||
GenRequestFn callback, bool removable, bool mutates_state) { | ||
ASSERT(prefix.size() > 1); | ||
ASSERT(prefix[0] == '/'); | ||
|
||
|
@@ -464,8 +472,8 @@ bool AdminImpl::addChunkedHandler(const std::string& prefix, const std::string& | |
|
||
bool AdminImpl::addHandler(const std::string& prefix, const std::string& help_text, | ||
HandlerCb callback, bool removable, bool mutates_state) { | ||
return addChunkedHandler(prefix, help_text, HandlerGasket::makeGen(callback), removable, | ||
mutates_state); | ||
return addStreamingHandler(prefix, help_text, RequestGasket::makeGen(callback), removable, | ||
mutates_state); | ||
} | ||
|
||
bool AdminImpl::removeHandler(const std::string& prefix) { | ||
|
@@ -480,7 +488,7 @@ bool AdminImpl::removeHandler(const std::string& prefix) { | |
|
||
Http::Code AdminImpl::request(absl::string_view path_and_query, absl::string_view method, | ||
Http::ResponseHeaderMap& response_headers, std::string& body) { | ||
AdminFilter filter(createHandlerFunction()); | ||
AdminFilter filter(createRequestFunction()); | ||
|
||
auto request_headers = Http::RequestHeaderMapImpl::create(); | ||
request_headers->setMethod(method); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be part of this PR