提交 7aef95b0 编写于 作者: Z zhang2014

ISSUES-5436 support custom http [part 5]

上级 fd007571
#include <Interpreters/CustomHTTP/CustomExecutor.h> #include <Interpreters/CustomHTTP/CustomExecutor.h>
#include <Interpreters/CustomHTTP/HTTPOutputStreams.h> #include <Interpreters/CustomHTTP/HTTPOutputStreams.h>
#include <Interpreters/CustomHTTP/CustomExecutorDefault.h> #include "CustomExecutor.h"
namespace DB namespace DB
...@@ -11,11 +11,11 @@ namespace ErrorCodes ...@@ -11,11 +11,11 @@ namespace ErrorCodes
extern const int SYNTAX_ERROR; extern const int SYNTAX_ERROR;
} }
bool CustomExecutor::match(HTTPRequest & request, HTMLForm & params) const bool CustomExecutor::match(Context & context, HTTPRequest & request, HTMLForm & params) const
{ {
for (const auto & matcher : matchers) for (const auto & matcher : matchers)
{ {
if (!matcher->match(request, params)) if (!matcher->match(context, request, params))
return false; return false;
} }
...@@ -56,14 +56,17 @@ void CustomExecutor::executeQuery( ...@@ -56,14 +56,17 @@ void CustomExecutor::executeQuery(
} }
CustomExecutor::CustomExecutor( CustomExecutor::CustomExecutor(
const std::vector<CustomMatcherPtr> & matchers_, const std::vector<CustomQueryExecutorPtr> & query_executors_) const std::vector<CustomExecutorMatcherPtr> & matchers_, const std::vector<CustomQueryExecutorPtr> & query_executors_)
: matchers(matchers_), query_executors(query_executors_) : matchers(matchers_), query_executors(query_executors_)
{ {
} }
CustomExecutors::CustomExecutors(const Configuration & config, const Settings & settings, const String & config_prefix) static CustomExecutorPtr createDefaultCustomExecutor()
{ {
updateCustomExecutors(config, settings, config_prefix); std::vector<CustomExecutorMatcherPtr> custom_matchers{std::make_shared<AlwaysMatchedCustomExecutorMatcher>()};
std::vector<CustomQueryExecutorPtr> custom_query_executors{std::make_shared<ExtractQueryParamCustomQueryExecutor>()};
return std::make_shared<CustomExecutor>(custom_matchers, custom_query_executors);
} }
void CustomExecutors::updateCustomExecutors(const Configuration & config, const Settings & settings, const String & config_prefix) void CustomExecutors::updateCustomExecutors(const Configuration & config, const Settings & settings, const String & config_prefix)
...@@ -71,7 +74,7 @@ void CustomExecutors::updateCustomExecutors(const Configuration & config, const ...@@ -71,7 +74,7 @@ void CustomExecutors::updateCustomExecutors(const Configuration & config, const
Configuration::Keys custom_executors_keys; Configuration::Keys custom_executors_keys;
config.keys(config_prefix, custom_executors_keys); config.keys(config_prefix, custom_executors_keys);
std::unordered_map<String, CustomExecutorPtr> new_custom_executors; std::vector<std::pair<String, CustomExecutorPtr>> new_custom_executors;
for (const auto & custom_executor_key : custom_executors_keys) for (const auto & custom_executor_key : custom_executors_keys)
{ {
...@@ -80,20 +83,48 @@ void CustomExecutors::updateCustomExecutors(const Configuration & config, const ...@@ -80,20 +83,48 @@ void CustomExecutors::updateCustomExecutors(const Configuration & config, const
else if (custom_executor_key.find('.') != String::npos) else if (custom_executor_key.find('.') != String::npos)
throw Exception("CustomExecutor names with dots are not supported: '" + custom_executor_key + "'", ErrorCodes::SYNTAX_ERROR); throw Exception("CustomExecutor names with dots are not supported: '" + custom_executor_key + "'", ErrorCodes::SYNTAX_ERROR);
new_custom_executors[custom_executor_key] = createCustomExecutor(config, settings, config_prefix + "." + custom_executor_key); new_custom_executors.push_back(
std::make_pair(custom_executor_key, createCustomExecutor(config, config_prefix + "." + custom_executor_key)));
} }
new_custom_executors["Default"] = CustomExecutorDefault::createDefaultCustomExecutor(); new_custom_executors.push_back(std::make_pair("Default", createDefaultCustomExecutor()));
std::unique_lock<std::shared_mutex> lock(rwlock); std::unique_lock<std::shared_mutex> lock(rwlock);
custom_executors = new_custom_executors; custom_executors = new_custom_executors;
} }
CustomExecutorPtr CustomExecutors::createCustomExecutor(const CustomExecutors::Configuration & config, const Settings & /*settings*/, const String & config_prefix) void CustomExecutors::registerQueryExecutor(const String & query_executor_name, const CustomExecutors::QueryExecutorCreator & creator)
{
const auto & matcher_creator_it = custom_matcher_creators.find(query_executor_name);
const auto & query_executor_creator_it = query_executor_creators.find(query_executor_name);
if (matcher_creator_it != custom_matcher_creators.end() && query_executor_creator_it != query_executor_creators.end())
throw Exception("LOGICAL_ERROR CustomQueryExecutor name must be unique between the CustomQueryExecutor and CustomExecutorMatcher.",
ErrorCodes::LOGICAL_ERROR);
query_executor_creators[query_executor_name] = creator;
}
void CustomExecutors::registerCustomMatcher(const String & matcher_name, const CustomExecutors::CustomMatcherCreator & creator)
{
const auto & matcher_creator_it = custom_matcher_creators.find(matcher_name);
const auto & query_executor_creator_it = query_executor_creators.find(matcher_name);
if (matcher_creator_it != custom_matcher_creators.end() && query_executor_creator_it != query_executor_creators.end())
throw Exception("LOGICAL_ERROR CustomExecutorMatcher name must be unique between the CustomQueryExecutor and CustomExecutorMatcher.",
ErrorCodes::LOGICAL_ERROR);
custom_matcher_creators[matcher_name] = creator;
}
CustomExecutorPtr CustomExecutors::createCustomExecutor(const Configuration & config, const String & config_prefix)
{ {
Configuration::Keys matchers_or_query_executors_type; Configuration::Keys matchers_or_query_executors_type;
config.keys(config_prefix, matchers_or_query_executors_type); config.keys(config_prefix, matchers_or_query_executors_type);
std::vector<CustomQueryExecutorPtr> custom_query_executors;
std::vector<CustomExecutorMatcherPtr> custom_executor_matchers;
for (const auto & matcher_or_query_executor_type : matchers_or_query_executors_type) for (const auto & matcher_or_query_executor_type : matchers_or_query_executors_type)
{ {
if (matcher_or_query_executor_type.find('.') != String::npos) if (matcher_or_query_executor_type.find('.') != String::npos)
...@@ -101,21 +132,43 @@ CustomExecutorPtr CustomExecutors::createCustomExecutor(const CustomExecutors::C ...@@ -101,21 +132,43 @@ CustomExecutorPtr CustomExecutors::createCustomExecutor(const CustomExecutors::C
"CustomMatcher or CustomQueryExecutor names with dots are not supported: '" + matcher_or_query_executor_type + "'", "CustomMatcher or CustomQueryExecutor names with dots are not supported: '" + matcher_or_query_executor_type + "'",
ErrorCodes::SYNTAX_ERROR); ErrorCodes::SYNTAX_ERROR);
// throw Exception("", ErrorCodes::NOT_IMPLEMENTED); const auto & matcher_creator_it = custom_matcher_creators.find(matcher_or_query_executor_type);
// new_custom_executors[matcher_or_query_executor_type] = createCustomExecutor(config, settings, config_prefix + "." + matcher_or_query_executor_type); const auto & query_executor_creator_it = query_executor_creators.find(matcher_or_query_executor_type);
if (matcher_creator_it == custom_matcher_creators.end() && query_executor_creator_it == query_executor_creators.end())
throw Exception("CustomMatcher or CustomQueryExecutor '" + matcher_or_query_executor_type + "' is not implemented.",
ErrorCodes::NOT_IMPLEMENTED);
if (matcher_creator_it != custom_matcher_creators.end())
custom_executor_matchers.push_back(matcher_creator_it->second(config, config_prefix + "." + matcher_or_query_executor_type));
if (query_executor_creator_it != query_executor_creators.end())
custom_query_executors.push_back(query_executor_creator_it->second(config, config_prefix + "." + matcher_or_query_executor_type));
} }
return DB::CustomExecutorPtr();
for (const auto & custom_executor_matcher : custom_executor_matchers)
custom_executor_matcher->checkQueryExecutor(custom_query_executors);
return std::make_shared<CustomExecutor>(custom_executor_matchers, custom_query_executors);
} }
std::pair<String, CustomExecutorPtr> CustomExecutors::getCustomExecutor(Poco::Net::HTTPServerRequest & request, HTMLForm & params) const std::pair<String, CustomExecutorPtr> CustomExecutors::getCustomExecutor(Context & context, Poco::Net::HTTPServerRequest & request, HTMLForm & params) const
{ {
std::shared_lock<std::shared_mutex> lock(rwlock); std::shared_lock<std::shared_mutex> lock(rwlock);
for (const auto & custom_executor : custom_executors) for (const auto & custom_executor : custom_executors)
if (custom_executor.second->match(request, params)) if (custom_executor.second->match(context, request, params))
return custom_executor; return custom_executor;
throw Exception("LOGICAL_ERROR not found custom executor.", ErrorCodes::LOGICAL_ERROR); throw Exception("LOGICAL_ERROR not found custom executor.", ErrorCodes::LOGICAL_ERROR);
} }
CustomExecutors::CustomExecutors(const Configuration & config, const Settings & settings, const String & config_prefix)
{
registerCustomMatcher("URL", [&](const auto & config, const auto & prefix)
{ return std::make_shared<HTTPURLCustomExecutorMatcher>(config, prefix); });
updateCustomExecutors(config, settings, config_prefix);
}
} }
...@@ -10,14 +10,15 @@ ...@@ -10,14 +10,15 @@
#include <Poco/Net/HTTPServerRequest.h> #include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h> #include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Util/AbstractConfiguration.h> #include <Poco/Util/AbstractConfiguration.h>
#include <Interpreters/Context.h>
#include <Interpreters/CustomHTTP/HTTPInputStreams.h>
#include <Interpreters/CustomHTTP/HTTPOutputStreams.h>
#include <Interpreters/CustomHTTP/CustomQueryExecutors.h>
#include <Interpreters/CustomHTTP/CustomExecutorMatchers.h>
namespace DB namespace DB
{ {
class Context;
class CustomExecutor; class CustomExecutor;
struct HTTPInputStreams;
struct HTTPOutputStreams;
using HTTPRequest = Poco::Net::HTTPServerRequest; using HTTPRequest = Poco::Net::HTTPServerRequest;
using HTTPResponse = Poco::Net::HTTPServerResponse; using HTTPResponse = Poco::Net::HTTPServerResponse;
...@@ -32,14 +33,22 @@ public: ...@@ -32,14 +33,22 @@ public:
CustomExecutors(const CustomExecutors &) = delete; CustomExecutors(const CustomExecutors &) = delete;
CustomExecutors & operator=(const CustomExecutors &) = delete; CustomExecutors & operator=(const CustomExecutors &) = delete;
using QueryExecutorCreator = std::function<CustomQueryExecutorPtr(const Configuration &, const String &)>;
void registerQueryExecutor(const String & query_executor_name, const QueryExecutorCreator & creator);
using CustomMatcherCreator = const std::function<CustomExecutorMatcherPtr(const Configuration &, const String &)>;
void registerCustomMatcher(const String & matcher_name, const CustomMatcherCreator & creator);
void updateCustomExecutors(const Configuration & config, const Settings & settings, const String & config_prefix); void updateCustomExecutors(const Configuration & config, const Settings & settings, const String & config_prefix);
std::pair<String, CustomExecutorPtr> getCustomExecutor(Poco::Net::HTTPServerRequest & request, HTMLForm & params) const; std::pair<String, CustomExecutorPtr> getCustomExecutor(Context & context, Poco::Net::HTTPServerRequest & request, HTMLForm & params) const;
private: private:
mutable std::shared_mutex rwlock; mutable std::shared_mutex rwlock;
std::unordered_map<String, CustomExecutorPtr> custom_executors; std::vector<std::pair<String, CustomExecutorPtr>> custom_executors;
std::unordered_map<String, QueryExecutorCreator> query_executor_creators;
std::unordered_map<String, CustomMatcherCreator> custom_matcher_creators;
CustomExecutorPtr createCustomExecutor(const Configuration & config, const Settings & settings, const String & config_prefix); CustomExecutorPtr createCustomExecutor(const Configuration & config, const String & config_prefix);
}; };
class CustomExecutor class CustomExecutor
...@@ -47,45 +56,19 @@ class CustomExecutor ...@@ -47,45 +56,19 @@ class CustomExecutor
public: public:
bool isQueryParam(const String & param_name) const; bool isQueryParam(const String & param_name) const;
bool match(HTTPRequest & request, HTMLForm & params) const;
bool canBeParseRequestBody(HTTPRequest & request, HTMLForm & params) const; bool canBeParseRequestBody(HTTPRequest & request, HTMLForm & params) const;
bool match(Context & context, HTTPRequest & request, HTMLForm & params) const;
void executeQuery( void executeQuery(
Context & context, HTTPRequest & request, HTTPResponse & response, Context & context, HTTPRequest & request, HTTPResponse & response,
HTMLForm & params, const HTTPInputStreams & input_streams, const HTTPOutputStreams & output_streams HTMLForm & params, const HTTPInputStreams & input_streams, const HTTPOutputStreams & output_streams
); );
public: CustomExecutor(const std::vector<CustomExecutorMatcherPtr> & matchers_, const std::vector<CustomQueryExecutorPtr> & query_executors_);
class CustomMatcher
{
public:
virtual ~CustomMatcher() = default;
virtual bool match(HTTPRequest & request, HTMLForm & params) const = 0;
};
class CustomQueryExecutor
{
public:
virtual ~CustomQueryExecutor() = default;
virtual bool isQueryParam(const String &) const = 0;
virtual bool canBeParseRequestBody(HTTPRequest &, HTMLForm &) const = 0;
virtual void executeQueryImpl(
Context & context, HTTPRequest & request, HTTPResponse & response,
HTMLForm & params, const HTTPInputStreams & input_streams, const HTTPOutputStreams & output_streams) const = 0;
};
public:
using CustomMatcherPtr = std::shared_ptr<CustomMatcher>;
using CustomQueryExecutorPtr = std::shared_ptr<CustomQueryExecutor>;
CustomExecutor(const std::vector<CustomMatcherPtr> & matchers_, const std::vector<CustomQueryExecutorPtr> & query_executors_);
private: private:
std::vector<CustomMatcherPtr> matchers; std::vector<CustomExecutorMatcherPtr> matchers;
std::vector<CustomQueryExecutorPtr> query_executors; std::vector<CustomQueryExecutorPtr> query_executors;
}; };
......
#pragma once
#include <Core/Types.h>
#include <Common/config.h>
#include <Common/HTMLForm.h>
#include <Interpreters/Context.h>
#include <Interpreters/CustomHTTP/CustomQueryExecutors.h>
#include <Poco/Net/HTTPServerRequest.h>
#if USE_RE2_ST
# include <re2_st/re2.h>
#else
# define re2_st re2
#endif
namespace DB
{
class CustomExecutorMatcher
{
public:
virtual ~CustomExecutorMatcher() = default;
virtual bool checkQueryExecutor(const std::vector<CustomQueryExecutorPtr> & check_executors) const = 0;
virtual bool match(Context & context, Poco::Net::HTTPServerRequest & request, HTMLForm & params) const = 0;
};
using CustomExecutorMatcherPtr = std::shared_ptr<CustomExecutorMatcher>;
class AlwaysMatchedCustomExecutorMatcher : public CustomExecutorMatcher
{
public:
bool checkQueryExecutor(const std::vector<CustomQueryExecutorPtr> & /*check_executors*/) const override { return true; }
bool match(Context & /*context*/, Poco::Net::HTTPServerRequest & /*request*/, HTMLForm & /*params*/) const override { return true; }
};
class HTTPURLCustomExecutorMatcher : public CustomExecutorMatcher
{
public:
HTTPURLCustomExecutorMatcher(const Poco::Util::AbstractConfiguration & configuration, const String & url_config_key)
: url_match_searcher(analyzeURLPatten(configuration.getString(url_config_key, ""), params_name_extract_from_url))
{
}
bool checkQueryExecutor(const std::vector<CustomQueryExecutorPtr> & custom_query_executors) const override
{
for (const auto & param_name_from_url : params_name_extract_from_url)
{
bool found_param_name = false;
for (const auto & custom_query_executor : custom_query_executors)
{
if (custom_query_executor->isQueryParam(param_name_from_url))
{
found_param_name = true;
break;
}
}
if (!found_param_name)
throw Exception("The param name '" + param_name_from_url + "' is uselessed.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
}
return true;
}
bool match(Context & /*context*/, Poco::Net::HTTPServerRequest & request, HTMLForm & /*params*/) const override
{
const String request_uri = request.getURI();
re2_st::StringPiece query_params_matches[params_name_extract_from_url.size()];
// re2_st::StringPiece input;
// if (url_match_searcher.Match(input, start_pos, input.length(), re2_st::RE2::Anchor::UNANCHORED, query_params_matches, num_captures))
// {
//
// }
return false;
}
private:
re2_st::RE2 url_match_searcher;
std::vector<String> params_name_extract_from_url;
String analyzeURLPatten(const String & /*url_patten*/, std::vector<String> & /*matches*/)
{
return ".+";
/// TODO: first we replace all capture group
/// TODO: second we replace all ${identifier}
}
};
}
#pragma once #pragma once
#include <Interpreters/CustomHTTP/CustomExecutor.h> #include <Core/Types.h>
#include <Common/HTMLForm.h>
#include <IO/ConcatReadBuffer.h> #include <IO/ConcatReadBuffer.h>
#include <IO/ReadBufferFromString.h> #include <IO/ReadBufferFromString.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Interpreters/executeQuery.h> #include <Interpreters/executeQuery.h>
#include <Interpreters/CustomHTTP/HTTPInputStreams.h> #include <Interpreters/CustomHTTP/HTTPInputStreams.h>
#include <Interpreters/CustomHTTP/HTTPOutputStreams.h> #include <Interpreters/CustomHTTP/HTTPOutputStreams.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
namespace DB namespace DB
{ {
class CustomExecutorDefault : public CustomExecutor::CustomMatcher, public CustomExecutor::CustomQueryExecutor class CustomQueryExecutor
{ {
public: public:
bool match(HTTPServerRequest & /*request*/, HTMLForm & /*params*/) const override { return true; } virtual ~CustomQueryExecutor() = default;
virtual bool isQueryParam(const String &) const = 0;
virtual bool canBeParseRequestBody(Poco::Net::HTTPServerRequest &, HTMLForm &) const = 0;
virtual void executeQueryImpl(
Context & context, Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response,
HTMLForm & params, const HTTPInputStreams & input_streams, const HTTPOutputStreams & output_streams) const = 0;
};
bool canBeParseRequestBody(HTTPServerRequest & /*request*/, HTMLForm & /*params*/) const override { return false; } using CustomQueryExecutorPtr = std::shared_ptr<CustomQueryExecutor>;
class ExtractQueryParamCustomQueryExecutor : public CustomQueryExecutor
{
public:
bool isQueryParam(const String & param_name) const override { return param_name == "query" || startsWith(param_name, "param_"); } bool isQueryParam(const String & param_name) const override { return param_name == "query" || startsWith(param_name, "param_"); }
bool canBeParseRequestBody(Poco::Net::HTTPServerRequest & /*request*/, HTMLForm & /*form*/) const override { return false; }
void executeQueryImpl( void executeQueryImpl(
Context & context, HTTPRequest & request, HTTPResponse & response, Context & context, Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response,
HTMLForm & params, const HTTPInputStreams & input_streams, const HTTPOutputStreams & output_streams) const override HTMLForm & params, const HTTPInputStreams & input_streams, const HTTPOutputStreams & output_streams) const override
{ {
const auto & execute_query = prepareQuery(context, params); const auto & execute_query = prepareQuery(context, params);
...@@ -42,16 +57,6 @@ public: ...@@ -42,16 +57,6 @@ public:
); );
} }
static CustomExecutorPtr createDefaultCustomExecutor()
{
const auto & default_custom_executor = std::make_shared<CustomExecutorDefault>();
std::vector<CustomExecutor::CustomMatcherPtr> custom_matchers{default_custom_executor};
std::vector<CustomExecutor::CustomQueryExecutorPtr> custom_query_executors{default_custom_executor};
return std::make_shared<CustomExecutor>(custom_matchers, custom_query_executors);
}
private: private:
String prepareQuery(Context & context, HTMLForm & params) const String prepareQuery(Context & context, HTMLForm & params) const
{ {
......
...@@ -244,7 +244,7 @@ void HTTPHandler::SessionContextHolder::authentication(HTTPServerRequest & reque ...@@ -244,7 +244,7 @@ void HTTPHandler::SessionContextHolder::authentication(HTTPServerRequest & reque
void HTTPHandler::processQuery(Context & context, HTTPRequest & request, HTMLForm & params, HTTPResponse & response) void HTTPHandler::processQuery(Context & context, HTTPRequest & request, HTMLForm & params, HTTPResponse & response)
{ {
const auto & name_with_custom_executor = context.getCustomExecutor(request, params); const auto & name_with_custom_executor = context.getCustomExecutor(request, params);
LOG_TRACE(log, "Using " << name_with_custom_executor.first << " to execute URI: " << request.getURI()); LOG_TRACE(log, "Using '" << name_with_custom_executor.first << "' CustomExecutor to execute URI: " << request.getURI());
ExtractorClientInfo{context.getClientInfo()}.extract(request); ExtractorClientInfo{context.getClientInfo()}.extract(request);
ExtractorContextChange{context, name_with_custom_executor.second}.extract(request, params); ExtractorContextChange{context, name_with_custom_executor.second}.extract(request, params);
......
...@@ -2059,7 +2059,7 @@ void Context::setCustomExecutorConfig(const ConfigurationPtr & config, const Str ...@@ -2059,7 +2059,7 @@ void Context::setCustomExecutorConfig(const ConfigurationPtr & config, const Str
shared->custom_executors->updateCustomExecutors(*shared->custom_executors_config, settings, config_prefix); shared->custom_executors->updateCustomExecutors(*shared->custom_executors_config, settings, config_prefix);
} }
std::pair<String, CustomExecutorPtr> Context::getCustomExecutor(Poco::Net::HTTPServerRequest & request, HTMLForm & params) const std::pair<String, CustomExecutorPtr> Context::getCustomExecutor(Poco::Net::HTTPServerRequest & request, HTMLForm & params)
{ {
std::lock_guard lock(shared->custom_executors_mutex); std::lock_guard lock(shared->custom_executors_mutex);
...@@ -2069,7 +2069,7 @@ std::pair<String, CustomExecutorPtr> Context::getCustomExecutor(Poco::Net::HTTPS ...@@ -2069,7 +2069,7 @@ std::pair<String, CustomExecutorPtr> Context::getCustomExecutor(Poco::Net::HTTPS
shared->custom_executors = std::make_unique<CustomExecutors>(config, settings); shared->custom_executors = std::make_unique<CustomExecutors>(config, settings);
} }
return shared->custom_executors->getCustomExecutor(request, params); return shared->custom_executors->getCustomExecutor(*this, request, params);
} }
......
...@@ -42,7 +42,7 @@ namespace zkutil ...@@ -42,7 +42,7 @@ namespace zkutil
class ZooKeeper; class ZooKeeper;
} }
class HTMLForm; struct HTMLForm;
namespace DB namespace DB
{ {
...@@ -492,7 +492,7 @@ public: ...@@ -492,7 +492,7 @@ public:
Compiler & getCompiler(); Compiler & getCompiler();
void setCustomExecutorConfig(const ConfigurationPtr & config, const String & config_prefix = "CustomHTTP"); void setCustomExecutorConfig(const ConfigurationPtr & config, const String & config_prefix = "CustomHTTP");
std::pair<String, CustomExecutorPtr> getCustomExecutor(Poco::Net::HTTPServerRequest &request, HTMLForm & params) const; std::pair<String, CustomExecutorPtr> getCustomExecutor(Poco::Net::HTTPServerRequest &request, HTMLForm & params);
/// Call after initialization before using system logs. Call for global context. /// Call after initialization before using system logs. Call for global context.
void initializeSystemLogs(); void initializeSystemLogs();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册