2012-03-09 04:45:27 +00:00
|
|
|
|
#include <iomanip>
|
|
|
|
|
|
2013-08-10 07:46:45 +00:00
|
|
|
|
#include <Poco/Net/HTTPBasicCredentials.h>
|
2012-03-09 03:06:09 +00:00
|
|
|
|
|
2012-03-09 04:45:27 +00:00
|
|
|
|
#include <statdaemons/Stopwatch.h>
|
|
|
|
|
|
2012-03-09 03:06:09 +00:00
|
|
|
|
#include <DB/Core/ErrorCodes.h>
|
|
|
|
|
|
|
|
|
|
#include <DB/IO/ReadBufferFromIStream.h>
|
2012-03-09 06:36:29 +00:00
|
|
|
|
#include <DB/IO/ReadBufferFromString.h>
|
|
|
|
|
#include <DB/IO/ConcatReadBuffer.h>
|
2012-03-09 15:46:52 +00:00
|
|
|
|
#include <DB/IO/CompressedReadBuffer.h>
|
|
|
|
|
#include <DB/IO/CompressedWriteBuffer.h>
|
2012-10-29 07:19:47 +00:00
|
|
|
|
#include <DB/IO/WriteBufferFromHTTPServerResponse.h>
|
2012-03-09 03:06:09 +00:00
|
|
|
|
#include <DB/IO/WriteBufferFromString.h>
|
|
|
|
|
#include <DB/IO/WriteHelpers.h>
|
|
|
|
|
|
2012-03-09 04:45:27 +00:00
|
|
|
|
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
|
|
|
|
|
2012-03-09 03:06:09 +00:00
|
|
|
|
#include <DB/Interpreters/executeQuery.h>
|
|
|
|
|
|
2012-03-09 15:46:52 +00:00
|
|
|
|
#include "HTTPHandler.h"
|
|
|
|
|
|
2012-03-09 03:06:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
2013-08-10 07:46:45 +00:00
|
|
|
|
void HTTPHandler::processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response)
|
2012-03-09 03:06:09 +00:00
|
|
|
|
{
|
2013-08-10 07:46:45 +00:00
|
|
|
|
LOG_TRACE(log, "Request URI: " << request.getURI());
|
|
|
|
|
|
|
|
|
|
HTMLForm params(request);
|
|
|
|
|
std::istream & istr = request.stream();
|
|
|
|
|
bool readonly = request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_GET;
|
|
|
|
|
|
2012-03-09 03:06:09 +00:00
|
|
|
|
BlockInputStreamPtr query_plan;
|
2013-08-10 07:46:45 +00:00
|
|
|
|
|
2013-02-16 13:48:25 +00:00
|
|
|
|
/** Часть запроса может быть передана в параметре query, а часть - POST-ом
|
|
|
|
|
* (точнее - в теле запроса, а метод не обязательно должен быть POST).
|
2012-03-09 06:36:29 +00:00
|
|
|
|
* В таком случае, считается, что запрос - параметр query, затем перевод строки, а затем - данные POST-а.
|
|
|
|
|
*/
|
|
|
|
|
std::string query_param = params.get("query", "");
|
|
|
|
|
if (!query_param.empty())
|
|
|
|
|
query_param += '\n';
|
|
|
|
|
|
|
|
|
|
ReadBufferFromString in_param(query_param);
|
2012-03-09 15:46:52 +00:00
|
|
|
|
SharedPtr<ReadBuffer> in_post = new ReadBufferFromIStream(istr);
|
|
|
|
|
SharedPtr<ReadBuffer> in_post_maybe_compressed;
|
|
|
|
|
|
|
|
|
|
/// Если указано decompress, то будем разжимать то, что передано POST-ом.
|
2013-06-21 21:05:16 +00:00
|
|
|
|
if (parse<bool>(params.get("decompress", "0")))
|
2012-03-09 15:46:52 +00:00
|
|
|
|
in_post_maybe_compressed = new CompressedReadBuffer(*in_post);
|
|
|
|
|
else
|
|
|
|
|
in_post_maybe_compressed = in_post;
|
|
|
|
|
|
|
|
|
|
ConcatReadBuffer in(in_param, *in_post_maybe_compressed);
|
2012-03-09 06:36:29 +00:00
|
|
|
|
|
2012-03-09 15:46:52 +00:00
|
|
|
|
/// Если указано compress, то будем сжимать результат.
|
2012-10-29 07:19:47 +00:00
|
|
|
|
SharedPtr<WriteBuffer> out = new WriteBufferFromHTTPServerResponse(response);
|
2012-03-09 15:46:52 +00:00
|
|
|
|
SharedPtr<WriteBuffer> out_maybe_compressed;
|
|
|
|
|
|
2013-06-21 21:05:16 +00:00
|
|
|
|
if (parse<bool>(params.get("compress", "0")))
|
2012-03-09 15:46:52 +00:00
|
|
|
|
out_maybe_compressed = new CompressedWriteBuffer(*out);
|
|
|
|
|
else
|
|
|
|
|
out_maybe_compressed = out;
|
2013-08-10 07:46:45 +00:00
|
|
|
|
|
|
|
|
|
/// Имя пользователя и пароль могут быть заданы как в параметрах URL, так и с помощью HTTP Basic authentification (и то, и другое не секъюрно).
|
|
|
|
|
std::string user = params.get("user", "default");
|
|
|
|
|
std::string password = params.get("password", "");
|
|
|
|
|
|
|
|
|
|
if (request.hasCredentials())
|
|
|
|
|
{
|
|
|
|
|
Poco::Net::HTTPBasicCredentials credentials(request);
|
|
|
|
|
|
|
|
|
|
user = credentials.getUsername();
|
|
|
|
|
password = credentials.getPassword();
|
|
|
|
|
}
|
2013-08-12 00:36:18 +00:00
|
|
|
|
|
|
|
|
|
std::string quota_key = params.get("quota_key", "");
|
2012-03-09 15:46:52 +00:00
|
|
|
|
|
2013-09-14 05:14:22 +00:00
|
|
|
|
Context context = *server.global_context;
|
|
|
|
|
context.setGlobalContext(*server.global_context);
|
2012-03-09 03:06:09 +00:00
|
|
|
|
|
2013-08-12 00:36:18 +00:00
|
|
|
|
context.setUser(user, password, request.clientAddress().host(), quota_key);
|
2013-08-10 07:46:45 +00:00
|
|
|
|
|
2013-02-17 19:54:32 +00:00
|
|
|
|
/// Настройки могут быть переопределены в запросе.
|
|
|
|
|
for (Poco::Net::NameValueCollection::ConstIterator it = params.begin(); it != params.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
if (it->first == "database")
|
|
|
|
|
{
|
|
|
|
|
context.setCurrentDatabase(it->second);
|
|
|
|
|
}
|
2013-06-29 18:03:57 +00:00
|
|
|
|
else if (it->first == "default_format")
|
|
|
|
|
{
|
|
|
|
|
context.setDefaultFormat(it->second);
|
|
|
|
|
}
|
2013-02-17 19:54:32 +00:00
|
|
|
|
else if (readonly && it->first == "readonly")
|
|
|
|
|
{
|
|
|
|
|
throw Exception("Setting 'readonly' cannot be overrided in readonly mode", ErrorCodes::READONLY);
|
|
|
|
|
}
|
|
|
|
|
else if (it->first == "query"
|
|
|
|
|
|| it->first == "compress"
|
2013-08-10 07:46:45 +00:00
|
|
|
|
|| it->first == "decompress"
|
|
|
|
|
|| it->first == "user"
|
|
|
|
|
|| it->first == "password")
|
2013-02-17 19:54:32 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
else /// Все неизвестные параметры запроса рассматриваются, как настройки.
|
|
|
|
|
context.getSettingsRef().set(it->first, it->second);
|
|
|
|
|
}
|
2012-08-02 17:33:31 +00:00
|
|
|
|
|
2013-02-16 13:48:25 +00:00
|
|
|
|
if (readonly)
|
2013-02-17 19:54:32 +00:00
|
|
|
|
context.getSettingsRef().limits.readonly = true;
|
2012-03-09 03:56:12 +00:00
|
|
|
|
|
2012-03-09 04:45:27 +00:00
|
|
|
|
Stopwatch watch;
|
2012-03-09 15:46:52 +00:00
|
|
|
|
executeQuery(in, *out_maybe_compressed, context, query_plan);
|
2012-03-09 04:45:27 +00:00
|
|
|
|
watch.stop();
|
2012-03-09 03:06:09 +00:00
|
|
|
|
|
|
|
|
|
if (query_plan)
|
|
|
|
|
{
|
|
|
|
|
std::stringstream log_str;
|
2012-06-25 05:07:34 +00:00
|
|
|
|
log_str << "Query pipeline:\n";
|
2012-03-09 03:06:09 +00:00
|
|
|
|
query_plan->dumpTree(log_str);
|
|
|
|
|
LOG_DEBUG(log, log_str.str());
|
2012-03-09 04:45:27 +00:00
|
|
|
|
|
|
|
|
|
/// Выведем информацию о том, сколько считано строк и байт.
|
|
|
|
|
size_t rows = 0;
|
|
|
|
|
size_t bytes = 0;
|
|
|
|
|
|
2012-08-23 23:49:28 +00:00
|
|
|
|
query_plan->getLeafRowsBytes(rows, bytes);
|
2012-03-09 04:45:27 +00:00
|
|
|
|
|
|
|
|
|
if (rows != 0)
|
|
|
|
|
{
|
|
|
|
|
LOG_INFO(log, std::fixed << std::setprecision(3)
|
2012-07-23 06:19:17 +00:00
|
|
|
|
<< "Read " << rows << " rows, " << bytes / 1048576.0 << " MiB in " << watch.elapsedSeconds() << " sec., "
|
|
|
|
|
<< static_cast<size_t>(rows / watch.elapsedSeconds()) << " rows/sec., " << bytes / 1048576.0 / watch.elapsedSeconds() << " MiB/sec.");
|
2012-03-09 04:45:27 +00:00
|
|
|
|
}
|
2012-03-09 03:06:09 +00:00
|
|
|
|
}
|
2013-08-28 20:47:22 +00:00
|
|
|
|
|
|
|
|
|
QuotaForIntervals & quota = context.getQuota();
|
|
|
|
|
if (!quota.empty())
|
|
|
|
|
LOG_INFO(log, "Quota:\n" << quota.toString());
|
2012-03-09 03:06:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-03-09 15:46:52 +00:00
|
|
|
|
void HTTPHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response)
|
2012-03-09 03:06:09 +00:00
|
|
|
|
{
|
2012-06-25 05:07:34 +00:00
|
|
|
|
bool is_browser = false;
|
|
|
|
|
if (request.has("Accept"))
|
|
|
|
|
{
|
|
|
|
|
String accept = request.get("Accept");
|
|
|
|
|
if (0 == strncmp(accept.c_str(), "text/html", strlen("text/html")))
|
|
|
|
|
is_browser = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_browser)
|
|
|
|
|
response.setContentType("text/plain; charset=UTF-8");
|
2012-10-29 07:19:47 +00:00
|
|
|
|
|
2013-08-13 18:59:51 +00:00
|
|
|
|
/// Для того, чтобы работал keep-alive.
|
|
|
|
|
if (request.getVersion() == Poco::Net::HTTPServerRequest::HTTP_1_1)
|
|
|
|
|
response.setChunkedTransferEncoding(true);
|
|
|
|
|
|
2012-03-09 03:06:09 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2013-08-10 07:46:45 +00:00
|
|
|
|
processQuery(request, response);
|
2012-03-09 03:06:09 +00:00
|
|
|
|
LOG_INFO(log, "Done processing query");
|
|
|
|
|
}
|
2013-10-26 03:20:51 +00:00
|
|
|
|
catch (Exception & e)
|
2012-10-29 07:19:47 +00:00
|
|
|
|
{
|
|
|
|
|
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
|
|
|
|
|
std::stringstream s;
|
|
|
|
|
s << "Code: " << e.code()
|
|
|
|
|
<< ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what();
|
2013-02-16 13:14:41 +00:00
|
|
|
|
if (!response.sent())
|
|
|
|
|
response.send() << s.str() << std::endl;
|
2012-10-29 07:19:47 +00:00
|
|
|
|
LOG_ERROR(log, s.str());
|
|
|
|
|
}
|
2012-03-09 03:06:09 +00:00
|
|
|
|
catch (Poco::Exception & e)
|
|
|
|
|
{
|
2012-10-29 07:19:47 +00:00
|
|
|
|
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
|
2012-03-09 03:06:09 +00:00
|
|
|
|
std::stringstream s;
|
|
|
|
|
s << "Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code()
|
2012-10-29 07:19:47 +00:00
|
|
|
|
<< ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what();
|
2013-02-16 13:14:41 +00:00
|
|
|
|
if (!response.sent())
|
|
|
|
|
response.send() << s.str() << std::endl;
|
2012-03-09 03:06:09 +00:00
|
|
|
|
LOG_ERROR(log, s.str());
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception & e)
|
|
|
|
|
{
|
2012-10-29 07:19:47 +00:00
|
|
|
|
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
|
2012-03-09 03:06:09 +00:00
|
|
|
|
std::stringstream s;
|
|
|
|
|
s << "Code: " << ErrorCodes::STD_EXCEPTION << ". " << e.what();
|
2013-02-16 13:14:41 +00:00
|
|
|
|
if (!response.sent())
|
|
|
|
|
response.send() << s.str() << std::endl;
|
2012-03-09 03:06:09 +00:00
|
|
|
|
LOG_ERROR(log, s.str());
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
2012-10-29 07:19:47 +00:00
|
|
|
|
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
|
2012-03-09 03:06:09 +00:00
|
|
|
|
std::stringstream s;
|
|
|
|
|
s << "Code: " << ErrorCodes::UNKNOWN_EXCEPTION << ". Unknown exception.";
|
2013-02-16 13:14:41 +00:00
|
|
|
|
if (!response.sent())
|
|
|
|
|
response.send() << s.str() << std::endl;
|
2012-03-09 03:06:09 +00:00
|
|
|
|
LOG_ERROR(log, s.str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|