Move function HTTPHandler::formatExceptionForClient() to a separate header in order to reuse it.

This commit is contained in:
Vitaly Baranov 2024-05-27 16:59:46 +02:00
parent 49b982747a
commit ecfe6fddcf
4 changed files with 110 additions and 43 deletions

View File

@ -0,0 +1,80 @@
#include <Server/HTTP/sendExceptionToHTTPClient.h>
#include <Server/HTTP/HTTPServerRequest.h>
#include <Server/HTTP/WriteBufferFromHTTPServerResponse.h>
#include <Server/HTTP/exceptionCodeToHTTPStatus.h>
namespace DB
{
namespace ErrorCodes
{
extern const int HTTP_LENGTH_REQUIRED;
extern const int REQUIRED_PASSWORD;
}
void sendExceptionToHTTPClient(
const String & exception_message,
int exception_code,
HTTPServerRequest & request,
HTTPServerResponse & response,
WriteBufferFromHTTPServerResponse * out,
LoggerPtr log)
{
setHTTPResponseStatusAndHeadersForException(exception_code, request, response, out, log);
if (!out)
{
/// If nothing was sent yet.
WriteBufferFromHTTPServerResponse out_for_message{response, request.getMethod() == HTTPRequest::HTTP_HEAD, DEFAULT_HTTP_KEEP_ALIVE_TIMEOUT};
out_for_message.writeln(exception_message);
out_for_message.finalize();
}
else
{
/// If buffer has data, and that data wasn't sent yet, then no need to send that data
bool data_sent = (out->count() != out->offset());
if (!data_sent)
out->position() = out->buffer().begin();
out->writeln(exception_message);
out->finalize();
}
}
void setHTTPResponseStatusAndHeadersForException(
int exception_code, HTTPServerRequest & request, HTTPServerResponse & response, WriteBufferFromHTTPServerResponse * out, LoggerPtr log)
{
if (out)
out->setExceptionCode(exception_code);
else
response.set("X-ClickHouse-Exception-Code", toString<int>(exception_code));
/// If HTTP method is POST and Keep-Alive is turned on, we should try to read the whole request body
/// to avoid reading part of the current request body in the next request.
if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_POST && response.getKeepAlive()
&& exception_code != ErrorCodes::HTTP_LENGTH_REQUIRED)
{
try
{
if (!request.getStream().eof())
request.getStream().ignoreAll();
}
catch (...)
{
tryLogCurrentException(log, "Cannot read remaining request body during exception handling");
response.setKeepAlive(false);
}
}
if (exception_code == ErrorCodes::REQUIRED_PASSWORD)
response.requireAuthentication("ClickHouse server HTTP API");
else
response.setStatusAndReason(exceptionCodeToHTTPStatus(exception_code));
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <Common/logger_useful.h>
#include <base/types.h>
namespace DB
{
class HTTPServerRequest;
class HTTPServerResponse;
class WriteBufferFromHTTPServerResponse;
/// Sends an exception to HTTP client. This function doesn't handle its own exceptions so it needs to be wrapped in try-catch.
/// Argument `out` may be either created from `response` or be nullptr (if it wasn't created before the exception).
void sendExceptionToHTTPClient(
const String & exception_message,
int exception_code,
HTTPServerRequest & request,
HTTPServerResponse & response,
WriteBufferFromHTTPServerResponse * out,
LoggerPtr log);
/// Sets "X-ClickHouse-Exception-Code" header and the correspondent HTTP status in the response for an exception.
/// This is a part of what sendExceptionToHTTPClient() does.
void setHTTPResponseStatusAndHeadersForException(
int exception_code, HTTPServerRequest & request, HTTPServerResponse & response, WriteBufferFromHTTPServerResponse * out, LoggerPtr log);
}

View File

@ -33,7 +33,7 @@
#include <base/scope_guard.h>
#include <Server/HTTP/HTTPResponse.h>
#include <Server/HTTP/authenticateUserByHTTP.h>
#include <Server/HTTP/exceptionCodeToHTTPStatus.h>
#include <Server/HTTP/sendExceptionToHTTPClient.h>
#include <Server/HTTP/setReadOnlyIfHTTPMethodIdempotent.h>
#include <boost/container/flat_set.hpp>
@ -60,8 +60,6 @@ namespace ErrorCodes
extern const int NO_ELEMENTS_IN_CONFIG;
extern const int REQUIRED_PASSWORD;
extern const int INVALID_SESSION_TIMEOUT;
extern const int HTTP_LENGTH_REQUIRED;
}
@ -519,7 +517,7 @@ void HTTPHandler::processQuery(
{
bool with_stacktrace = (params.getParsed<bool>("stacktrace", false) && server.config().getBool("enable_http_stacktrace", true));
ExecutionStatus status = ExecutionStatus::fromCurrentException("", with_stacktrace);
formatExceptionForClient(status.code, request, response, used_output);
setHTTPResponseStatusAndHeadersForException(status.code, request, response, used_output.out_holder.get(), log);
current_output_format.setException(status.message);
current_output_format.finalize();
used_output.exception_is_written = true;
@ -553,7 +551,7 @@ void HTTPHandler::trySendExceptionToClient(
const std::string & s, int exception_code, HTTPServerRequest & request, HTTPServerResponse & response, Output & used_output)
try
{
formatExceptionForClient(exception_code, request, response, used_output);
setHTTPResponseStatusAndHeadersForException(exception_code, request, response, used_output.out_holder.get(), log);
if (!used_output.out_holder && !used_output.exception_is_written)
{
@ -615,38 +613,6 @@ catch (...)
used_output.cancel();
}
void HTTPHandler::formatExceptionForClient(int exception_code, HTTPServerRequest & request, HTTPServerResponse & response, Output & used_output)
{
if (used_output.out_holder)
used_output.out_holder->setExceptionCode(exception_code);
else
response.set("X-ClickHouse-Exception-Code", toString<int>(exception_code));
/// FIXME: make sure that no one else is reading from the same stream at the moment.
/// If HTTP method is POST and Keep-Alive is turned on, we should try to read the whole request body
/// to avoid reading part of the current request body in the next request.
if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_POST && response.getKeepAlive()
&& exception_code != ErrorCodes::HTTP_LENGTH_REQUIRED)
{
try
{
if (!request.getStream().eof())
request.getStream().ignoreAll();
}
catch (...)
{
tryLogCurrentException(log, "Cannot read remaining request body during exception handling");
response.setKeepAlive(false);
}
}
if (exception_code == ErrorCodes::REQUIRED_PASSWORD)
response.requireAuthentication("ClickHouse server HTTP API");
else
response.setStatusAndReason(exceptionCodeToHTTPStatus(exception_code));
}
void HTTPHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event)
{
setThreadName("HTTPHandler");

View File

@ -173,12 +173,6 @@ private:
HTTPServerResponse & response,
Output & used_output);
void formatExceptionForClient(
int exception_code,
HTTPServerRequest & request,
HTTPServerResponse & response,
Output & used_output);
static void pushDelayedResults(Output & used_output);
};