diff --git a/programs/server/CMakeLists.txt b/programs/server/CMakeLists.txt index b3dcf1955fe..198d9081168 100644 --- a/programs/server/CMakeLists.txt +++ b/programs/server/CMakeLists.txt @@ -4,7 +4,7 @@ set(CLICKHOUSE_SERVER_SOURCES ) if (OS_LINUX) - set (LINK_CONFIG_LIB INTERFACE "-Wl,${WHOLE_ARCHIVE} $ -Wl,${NO_WHOLE_ARCHIVE}") + set (LINK_RESOURCE_LIB INTERFACE "-Wl,${WHOLE_ARCHIVE} $ -Wl,${NO_WHOLE_ARCHIVE}") endif () set (CLICKHOUSE_SERVER_LINK @@ -20,7 +20,7 @@ set (CLICKHOUSE_SERVER_LINK clickhouse_table_functions string_utils - ${LINK_CONFIG_LIB} + ${LINK_RESOURCE_LIB} PUBLIC daemon @@ -37,20 +37,20 @@ if (OS_LINUX) # 1. Allow to run the binary without download of any other files. # 2. Allow to implement "sudo clickhouse install" tool. - foreach(CONFIG_FILE config users embedded) - set(CONFIG_OBJ ${CONFIG_FILE}.o) - set(CONFIG_OBJS ${CONFIG_OBJS} ${CONFIG_OBJ}) + foreach(RESOURCE_FILE config.xml users.xml embedded.xml play.html) + set(RESOURCE_OBJ ${RESOURCE_FILE}.o) + set(RESOURCE_OBJS ${RESOURCE_OBJS} ${RESOURCE_OBJ}) # https://stackoverflow.com/questions/14776463/compile-and-add-an-object-file-from-a-binary-with-cmake - add_custom_command(OUTPUT ${CONFIG_OBJ} - COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${OBJCOPY_PATH} -I binary ${OBJCOPY_ARCH_OPTIONS} ${CONFIG_FILE}.xml ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_OBJ} + add_custom_command(OUTPUT ${RESOURCE_OBJ} + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${OBJCOPY_PATH} -I binary ${OBJCOPY_ARCH_OPTIONS} ${RESOURCE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${RESOURCE_OBJ} COMMAND ${OBJCOPY_PATH} --rename-section .data=.rodata,alloc,load,readonly,data,contents - ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_OBJ} ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_OBJ}) + ${CMAKE_CURRENT_BINARY_DIR}/${RESOURCE_OBJ} ${CMAKE_CURRENT_BINARY_DIR}/${RESOURCE_OBJ}) - set_source_files_properties(${CONFIG_OBJ} PROPERTIES EXTERNAL_OBJECT true GENERATED true) - endforeach(CONFIG_FILE) + set_source_files_properties(${RESOURCE_OBJ} PROPERTIES EXTERNAL_OBJECT true GENERATED true) + endforeach(RESOURCE_FILE) - add_library(clickhouse_server_configs STATIC ${CONFIG_OBJS}) + add_library(clickhouse_server_configs STATIC ${RESOURCE_OBJS}) set_target_properties(clickhouse_server_configs PROPERTIES LINKER_LANGUAGE C) # whole-archive prevents symbols from being discarded for unknown reason diff --git a/src/Server/HTTPHandlerFactory.cpp b/src/Server/HTTPHandlerFactory.cpp index f34852054d1..8915ea747ca 100644 --- a/src/Server/HTTPHandlerFactory.cpp +++ b/src/Server/HTTPHandlerFactory.cpp @@ -8,6 +8,7 @@ #include "ReplicasStatusHandler.h" #include "InterserverIOHTTPHandler.h" #include "PrometheusRequestHandler.h" +#include "WebUIRequestHandler.h" namespace DB @@ -78,7 +79,9 @@ static inline auto createHandlersFactoryFromConfig( for (const auto & key : keys) { if (key == "defaults") + { addDefaultHandlersFactory(*main_handler_factory, server, async_metrics); + } else if (startsWith(key, "rule")) { const auto & handler_type = server.config().getString(prefix + "." + key + ".handler.type", ""); @@ -112,7 +115,9 @@ static inline auto createHandlersFactoryFromConfig( static inline Poco::Net::HTTPRequestHandlerFactory * createHTTPHandlerFactory(IServer & server, const std::string & name, AsynchronousMetrics & async_metrics) { if (server.config().has("http_handlers")) + { return createHandlersFactoryFromConfig(server, name, "http_handlers", async_metrics); + } else { auto factory = std::make_unique(name); @@ -168,6 +173,10 @@ void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IS auto replicas_status_handler = std::make_unique>(server); replicas_status_handler->attachNonStrictPath("/replicas_status")->allowGetAndHeadRequest(); factory.addHandler(replicas_status_handler.release()); + + auto web_ui_handler = std::make_unique>(server, "play.html"); + web_ui_handler->attachNonStrictPath("/play")->allowGetAndHeadRequest(); + factory.addHandler(web_ui_handler.release()); } void addDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IServer & server, AsynchronousMetrics & async_metrics) diff --git a/src/Server/StaticRequestHandler.cpp b/src/Server/StaticRequestHandler.cpp index 22f32e6a0e7..7f63099c972 100644 --- a/src/Server/StaticRequestHandler.cpp +++ b/src/Server/StaticRequestHandler.cpp @@ -1,4 +1,5 @@ #include "StaticRequestHandler.h" +#include "IServer.h" #include "HTTPHandlerFactory.h" #include "HTTPHandlerRequestFilter.h" @@ -17,6 +18,8 @@ #include #include #include +#include + namespace DB { diff --git a/src/Server/StaticRequestHandler.h b/src/Server/StaticRequestHandler.h index a5ac44683a1..0a29384ad0e 100644 --- a/src/Server/StaticRequestHandler.h +++ b/src/Server/StaticRequestHandler.h @@ -1,16 +1,15 @@ #pragma once -#include "IServer.h" - #include -#include #include -#include namespace DB { +class IServer; +class WriteBuffer; + /// Response with custom string. Can be used for browser. class StaticRequestHandler : public Poco::Net::HTTPRequestHandler { @@ -22,7 +21,11 @@ private: String response_expression; public: - StaticRequestHandler(IServer & server, const String & expression, int status_ = 200, const String & content_type_ = "text/html; charset=UTF-8"); + StaticRequestHandler( + IServer & server, + const String & expression, + int status_ = 200, + const String & content_type_ = "text/html; charset=UTF-8"); void writeResponse(WriteBuffer & out); diff --git a/src/Server/WebUIRequestHandler.cpp b/src/Server/WebUIRequestHandler.cpp new file mode 100644 index 00000000000..6159a27971f --- /dev/null +++ b/src/Server/WebUIRequestHandler.cpp @@ -0,0 +1,35 @@ +#include "WebUIRequestHandler.h" +#include "IServer.h" + +#include +#include +#include + +#include +#include + + +namespace DB +{ + +WebUIRequestHandler::WebUIRequestHandler(IServer & server_, std::string resource_name_) + : server(server_), resource_name(std::move(resource_name_)) +{ +} + + +void WebUIRequestHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) +{ + auto keep_alive_timeout = server.config().getUInt("keep_alive_timeout", 10); + + response.setContentType("text/html; charset=UTF-8"); + + if (request.getVersion() == Poco::Net::HTTPServerRequest::HTTP_1_1) + response.setChunkedTransferEncoding(true); + + setResponseDefaultHeaders(response, keep_alive_timeout); + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK); + response.send() << getResource(resource_name); +} + +} diff --git a/src/Server/WebUIRequestHandler.h b/src/Server/WebUIRequestHandler.h new file mode 100644 index 00000000000..98efba27a35 --- /dev/null +++ b/src/Server/WebUIRequestHandler.h @@ -0,0 +1,23 @@ +#pragma once + +#include + + +namespace DB +{ + +class IServer; + +/// Response with HTML page that allow to send queries and show results in browser. +class WebUIRequestHandler : public Poco::Net::HTTPRequestHandler +{ +private: + IServer & server; + std::string resource_name; +public: + WebUIRequestHandler(IServer & server_, std::string resource_name_); + void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override; +}; + +} + diff --git a/tests/queries/0_stateless/01528_play.reference b/tests/queries/0_stateless/01528_play.reference new file mode 100644 index 00000000000..11d6850993f --- /dev/null +++ b/tests/queries/0_stateless/01528_play.reference @@ -0,0 +1 @@ +🌞 diff --git a/tests/queries/0_stateless/01528_play.sh b/tests/queries/0_stateless/01528_play.sh new file mode 100755 index 00000000000..7182f4dd6e5 --- /dev/null +++ b/tests/queries/0_stateless/01528_play.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. "$CURDIR"/../shell_config.sh + +${CLICKHOUSE_CURL} -sS "${CLICKHOUSE_PORT_HTTP_PROTO}://${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_HTTP}/play" | grep -o '🌞'