mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-26 17:41:59 +00:00
Merge pull request #60389 from ClickHouse/allow-remap-web-ui-handlers
Allow to map UI handlers to different paths
This commit is contained in:
commit
bbf9561a65
@ -1298,9 +1298,7 @@ HTTPRequestHandlerFactoryPtr createDynamicHandlerFactory(IServer & server,
|
||||
};
|
||||
|
||||
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(std::move(creator));
|
||||
|
||||
factory->addFiltersFromConfig(config, config_prefix);
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,16 @@ static void addDefaultHandlersFactory(
|
||||
const Poco::Util::AbstractConfiguration & config,
|
||||
AsynchronousMetrics & async_metrics);
|
||||
|
||||
static auto createPingHandlerFactory(IServer & server)
|
||||
{
|
||||
auto creator = [&server]() -> std::unique_ptr<StaticRequestHandler>
|
||||
{
|
||||
constexpr auto ping_response_expression = "Ok.\n";
|
||||
return std::make_unique<StaticRequestHandler>(server, ping_response_expression);
|
||||
};
|
||||
return std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(std::move(creator));
|
||||
}
|
||||
|
||||
static inline auto createHandlersFactoryFromConfig(
|
||||
IServer & server,
|
||||
const Poco::Util::AbstractConfiguration & config,
|
||||
@ -60,15 +70,49 @@ static inline auto createHandlersFactoryFromConfig(
|
||||
"{}.{}.handler.type", prefix, key);
|
||||
|
||||
if (handler_type == "static")
|
||||
{
|
||||
main_handler_factory->addHandler(createStaticHandlerFactory(server, config, prefix + "." + key));
|
||||
}
|
||||
else if (handler_type == "dynamic_query_handler")
|
||||
{
|
||||
main_handler_factory->addHandler(createDynamicHandlerFactory(server, config, prefix + "." + key));
|
||||
}
|
||||
else if (handler_type == "predefined_query_handler")
|
||||
{
|
||||
main_handler_factory->addHandler(createPredefinedHandlerFactory(server, config, prefix + "." + key));
|
||||
}
|
||||
else if (handler_type == "prometheus")
|
||||
{
|
||||
main_handler_factory->addHandler(createPrometheusHandlerFactory(server, config, async_metrics, prefix + "." + key));
|
||||
}
|
||||
else if (handler_type == "replicas_status")
|
||||
{
|
||||
main_handler_factory->addHandler(createReplicasStatusHandlerFactory(server, config, prefix + "." + key));
|
||||
}
|
||||
else if (handler_type == "ping")
|
||||
{
|
||||
auto handler = createPingHandlerFactory(server);
|
||||
handler->addFiltersFromConfig(config, prefix + "." + key);
|
||||
main_handler_factory->addHandler(std::move(handler));
|
||||
}
|
||||
else if (handler_type == "play")
|
||||
{
|
||||
auto handler = std::make_shared<HandlingRuleHTTPHandlerFactory<PlayWebUIRequestHandler>>(server);
|
||||
handler->addFiltersFromConfig(config, prefix + "." + key);
|
||||
main_handler_factory->addHandler(std::move(handler));
|
||||
}
|
||||
else if (handler_type == "dashboard")
|
||||
{
|
||||
auto handler = std::make_shared<HandlingRuleHTTPHandlerFactory<DashboardWebUIRequestHandler>>(server);
|
||||
handler->addFiltersFromConfig(config, prefix + "." + key);
|
||||
main_handler_factory->addHandler(std::move(handler));
|
||||
}
|
||||
else if (handler_type == "binary")
|
||||
{
|
||||
auto handler = std::make_shared<HandlingRuleHTTPHandlerFactory<BinaryWebUIRequestHandler>>(server);
|
||||
handler->addFiltersFromConfig(config, prefix + "." + key);
|
||||
main_handler_factory->addHandler(std::move(handler));
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::INVALID_CONFIG_PARAMETER, "Unknown handler type '{}' in config here: {}.{}.handler.type",
|
||||
handler_type, prefix, key);
|
||||
@ -108,6 +152,7 @@ static inline HTTPRequestHandlerFactoryPtr createInterserverHTTPHandlerFactory(I
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
||||
HTTPRequestHandlerFactoryPtr createHandlerFactory(IServer & server, const Poco::Util::AbstractConfiguration & config, AsynchronousMetrics & async_metrics, const std::string & name)
|
||||
{
|
||||
if (name == "HTTPHandler-factory" || name == "HTTPSHandler-factory")
|
||||
@ -136,12 +181,7 @@ void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IS
|
||||
root_handler->allowGetAndHeadRequest();
|
||||
factory.addHandler(root_handler);
|
||||
|
||||
auto ping_creator = [&server]() -> std::unique_ptr<StaticRequestHandler>
|
||||
{
|
||||
constexpr auto ping_response_expression = "Ok.\n";
|
||||
return std::make_unique<StaticRequestHandler>(server, ping_response_expression);
|
||||
};
|
||||
auto ping_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(std::move(ping_creator));
|
||||
auto ping_handler = createPingHandlerFactory(server);
|
||||
ping_handler->attachStrictPath("/ping");
|
||||
ping_handler->allowGetAndHeadRequest();
|
||||
factory.addPathToHints("/ping");
|
||||
@ -153,25 +193,25 @@ void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IS
|
||||
factory.addPathToHints("/replicas_status");
|
||||
factory.addHandler(replicas_status_handler);
|
||||
|
||||
auto play_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<WebUIRequestHandler>>(server);
|
||||
auto play_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<PlayWebUIRequestHandler>>(server);
|
||||
play_handler->attachNonStrictPath("/play");
|
||||
play_handler->allowGetAndHeadRequest();
|
||||
factory.addPathToHints("/play");
|
||||
factory.addHandler(play_handler);
|
||||
|
||||
auto dashboard_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<WebUIRequestHandler>>(server);
|
||||
auto dashboard_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<DashboardWebUIRequestHandler>>(server);
|
||||
dashboard_handler->attachNonStrictPath("/dashboard");
|
||||
dashboard_handler->allowGetAndHeadRequest();
|
||||
factory.addPathToHints("/dashboard");
|
||||
factory.addHandler(dashboard_handler);
|
||||
|
||||
auto binary_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<WebUIRequestHandler>>(server);
|
||||
auto binary_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<BinaryWebUIRequestHandler>>(server);
|
||||
binary_handler->attachNonStrictPath("/binary");
|
||||
binary_handler->allowGetAndHeadRequest();
|
||||
factory.addPathToHints("/binary");
|
||||
factory.addHandler(binary_handler);
|
||||
|
||||
auto js_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<WebUIRequestHandler>>(server);
|
||||
auto js_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<JavaScriptWebUIRequestHandler>>(server);
|
||||
js_handler->attachNonStrictPath("/js/");
|
||||
js_handler->allowGetAndHeadRequest();
|
||||
factory.addHandler(js_handler);
|
||||
|
@ -36,7 +36,6 @@ public:
|
||||
creator = [&server]() -> std::unique_ptr<TEndpoint> { return std::make_unique<TEndpoint>(server); };
|
||||
}
|
||||
|
||||
|
||||
void addFilter(Filter cur_filter)
|
||||
{
|
||||
Filter prev_filter = filter;
|
||||
|
@ -24,67 +24,70 @@ INCBIN(resource_binary_html, SOURCE_DIR "/programs/server/binary.html");
|
||||
namespace DB
|
||||
{
|
||||
|
||||
WebUIRequestHandler::WebUIRequestHandler(IServer & server_)
|
||||
: server(server_)
|
||||
{
|
||||
}
|
||||
PlayWebUIRequestHandler::PlayWebUIRequestHandler(IServer & server_) : server(server_) {}
|
||||
DashboardWebUIRequestHandler::DashboardWebUIRequestHandler(IServer & server_) : server(server_) {}
|
||||
BinaryWebUIRequestHandler::BinaryWebUIRequestHandler(IServer & server_) : server(server_) {}
|
||||
JavaScriptWebUIRequestHandler::JavaScriptWebUIRequestHandler(IServer & server_) : server(server_) {}
|
||||
|
||||
|
||||
void WebUIRequestHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & /*write_event*/)
|
||||
static void handle(const IServer & server, HTTPServerRequest & request, HTTPServerResponse & response, std::string_view html)
|
||||
{
|
||||
auto keep_alive_timeout = server.context()->getServerSettings().keep_alive_timeout.totalSeconds();
|
||||
|
||||
response.setContentType("text/html; charset=UTF-8");
|
||||
|
||||
if (request.getVersion() == HTTPServerRequest::HTTP_1_1)
|
||||
response.setChunkedTransferEncoding(true);
|
||||
|
||||
setResponseDefaultHeaders(response, keep_alive_timeout);
|
||||
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
WriteBufferFromHTTPServerResponse(response, request.getMethod() == HTTPRequest::HTTP_HEAD, keep_alive_timeout).write(html.data(), html.size());
|
||||
|
||||
if (request.getURI().starts_with("/play"))
|
||||
}
|
||||
|
||||
void PlayWebUIRequestHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event &)
|
||||
{
|
||||
handle(server, request, response, {reinterpret_cast<const char *>(gresource_play_htmlData), gresource_play_htmlSize});
|
||||
}
|
||||
|
||||
void DashboardWebUIRequestHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event &)
|
||||
{
|
||||
std::string html(reinterpret_cast<const char *>(gresource_dashboard_htmlData), gresource_dashboard_htmlSize);
|
||||
|
||||
/// Replace a link to external JavaScript file to embedded file.
|
||||
/// This allows to open the HTML without running a server and to host it on server.
|
||||
/// Note: we can embed the JavaScript file inline to the HTML,
|
||||
/// but we don't do it to keep the "view-source" perfectly readable.
|
||||
|
||||
static re2::RE2 uplot_url = R"(https://[^\s"'`]+u[Pp]lot[^\s"'`]*\.js)";
|
||||
RE2::Replace(&html, uplot_url, "/js/uplot.js");
|
||||
|
||||
static re2::RE2 lz_string_url = R"(https://[^\s"'`]+lz-string[^\s"'`]*\.js)";
|
||||
RE2::Replace(&html, lz_string_url, "/js/lz-string.js");
|
||||
|
||||
handle(server, request, response, html);
|
||||
}
|
||||
|
||||
void BinaryWebUIRequestHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event &)
|
||||
{
|
||||
handle(server, request, response, {reinterpret_cast<const char *>(gresource_binary_htmlData), gresource_binary_htmlSize});
|
||||
}
|
||||
|
||||
void JavaScriptWebUIRequestHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event &)
|
||||
{
|
||||
if (request.getURI() == "/js/uplot.js")
|
||||
{
|
||||
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
WriteBufferFromHTTPServerResponse(response, request.getMethod() == HTTPRequest::HTTP_HEAD, keep_alive_timeout).write(reinterpret_cast<const char *>(gresource_play_htmlData), gresource_play_htmlSize);
|
||||
}
|
||||
else if (request.getURI().starts_with("/dashboard"))
|
||||
{
|
||||
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
|
||||
std::string html(reinterpret_cast<const char *>(gresource_dashboard_htmlData), gresource_dashboard_htmlSize);
|
||||
|
||||
/// Replace a link to external JavaScript file to embedded file.
|
||||
/// This allows to open the HTML without running a server and to host it on server.
|
||||
/// Note: we can embed the JavaScript file inline to the HTML,
|
||||
/// but we don't do it to keep the "view-source" perfectly readable.
|
||||
|
||||
static re2::RE2 uplot_url = R"(https://[^\s"'`]+u[Pp]lot[^\s"'`]*\.js)";
|
||||
RE2::Replace(&html, uplot_url, "/js/uplot.js");
|
||||
|
||||
static re2::RE2 lz_string_url = R"(https://[^\s"'`]+lz-string[^\s"'`]*\.js)";
|
||||
RE2::Replace(&html, lz_string_url, "/js/lz-string.js");
|
||||
|
||||
WriteBufferFromHTTPServerResponse(response, request.getMethod() == HTTPRequest::HTTP_HEAD, keep_alive_timeout).write(html);
|
||||
}
|
||||
else if (request.getURI().starts_with("/binary"))
|
||||
{
|
||||
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
WriteBufferFromHTTPServerResponse(response, request.getMethod() == HTTPRequest::HTTP_HEAD, keep_alive_timeout).write(reinterpret_cast<const char *>(gresource_binary_htmlData), gresource_binary_htmlSize);
|
||||
}
|
||||
else if (request.getURI() == "/js/uplot.js")
|
||||
{
|
||||
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
WriteBufferFromHTTPServerResponse(response, request.getMethod() == HTTPRequest::HTTP_HEAD, keep_alive_timeout).write(reinterpret_cast<const char *>(gresource_uplot_jsData), gresource_uplot_jsSize);
|
||||
handle(server, request, response, {reinterpret_cast<const char *>(gresource_uplot_jsData), gresource_uplot_jsSize});
|
||||
}
|
||||
else if (request.getURI() == "/js/lz-string.js")
|
||||
{
|
||||
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
WriteBufferFromHTTPServerResponse(response, request.getMethod() == HTTPRequest::HTTP_HEAD, keep_alive_timeout).write(reinterpret_cast<const char *>(gresource_lz_string_jsData), gresource_lz_string_jsSize);
|
||||
handle(server, request, response, {reinterpret_cast<const char *>(gresource_lz_string_jsData), gresource_lz_string_jsSize});
|
||||
}
|
||||
else
|
||||
{
|
||||
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
|
||||
*response.send() << "Not found.\n";
|
||||
}
|
||||
|
||||
handle(server, request, response, {reinterpret_cast<const char *>(gresource_binary_htmlData), gresource_binary_htmlSize});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,13 +9,40 @@ namespace DB
|
||||
class IServer;
|
||||
|
||||
/// Response with HTML page that allows to send queries and show results in browser.
|
||||
class WebUIRequestHandler : public HTTPRequestHandler
|
||||
|
||||
class PlayWebUIRequestHandler : public HTTPRequestHandler
|
||||
{
|
||||
private:
|
||||
IServer & server;
|
||||
|
||||
public:
|
||||
WebUIRequestHandler(IServer & server_);
|
||||
PlayWebUIRequestHandler(IServer & server_);
|
||||
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
||||
};
|
||||
|
||||
class DashboardWebUIRequestHandler : public HTTPRequestHandler
|
||||
{
|
||||
private:
|
||||
IServer & server;
|
||||
public:
|
||||
DashboardWebUIRequestHandler(IServer & server_);
|
||||
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
||||
};
|
||||
|
||||
class BinaryWebUIRequestHandler : public HTTPRequestHandler
|
||||
{
|
||||
private:
|
||||
IServer & server;
|
||||
public:
|
||||
BinaryWebUIRequestHandler(IServer & server_);
|
||||
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
||||
};
|
||||
|
||||
class JavaScriptWebUIRequestHandler : public HTTPRequestHandler
|
||||
{
|
||||
private:
|
||||
IServer & server;
|
||||
public:
|
||||
JavaScriptWebUIRequestHandler(IServer & server_);
|
||||
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user