diff --git a/src/Server/HTTPHandler.cpp b/src/Server/HTTPHandler.cpp index 35a95c0534d..c504c2d2b99 100644 --- a/src/Server/HTTPHandler.cpp +++ b/src/Server/HTTPHandler.cpp @@ -884,6 +884,9 @@ void HTTPHandler::processQuery( { if (settings.http_write_exception_in_output_format && output_format.supportsWritingException()) { + ExecutionStatus status = ExecutionStatus::fromCurrentException("", false); + formatExceptionForClient(status.code, request, response, used_output); + output_format.setException(getCurrentExceptionMessage(false)); output_format.finalize(); used_output.exception_is_written = true; @@ -916,31 +919,7 @@ void HTTPHandler::trySendExceptionToClient( const std::string & s, int exception_code, HTTPServerRequest & request, HTTPServerResponse & response, Output & used_output) try { - if (used_output.out_holder) - used_output.out_holder->setExceptionCode(exception_code); - else - response.set("X-ClickHouse-Exception-Code", toString(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 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 - && !request.getStream().eof()) - { - request.getStream().ignoreAll(); - } - - if (exception_code == ErrorCodes::REQUIRED_PASSWORD) - { - response.requireAuthentication("ClickHouse server HTTP API"); - } - else - { - response.setStatusAndReason(exceptionCodeToHTTPStatus(exception_code)); - } + formatExceptionForClient(exception_code, request, response, used_output); if (!used_output.out_holder && !used_output.exception_is_written) { @@ -1001,6 +980,28 @@ catch (...) } } +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(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 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 && !request.getStream().eof()) + { + request.getStream().ignoreAll(); + } + + 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) { diff --git a/src/Server/HTTPHandler.h b/src/Server/HTTPHandler.h index fa2d0dae199..0e30b466694 100644 --- a/src/Server/HTTPHandler.h +++ b/src/Server/HTTPHandler.h @@ -148,6 +148,12 @@ private: HTTPServerResponse & response, Output & used_output); + void formatExceptionForClient( + int exception_code, + HTTPServerRequest & request, + HTTPServerResponse & response, + Output & used_output); + static void pushDelayedResults(Output & used_output); }; diff --git a/tests/queries/0_stateless/02899_use_default_format_on_http_exception.reference b/tests/queries/0_stateless/02899_use_default_format_on_http_exception.reference index a943df06764..624bcf80c71 100644 --- a/tests/queries/0_stateless/02899_use_default_format_on_http_exception.reference +++ b/tests/queries/0_stateless/02899_use_default_format_on_http_exception.reference @@ -1,25 +1,47 @@ +SELECT missing column WITH default_format=JSON +404NotFound +Content-Type:application/json;charset=UTF-8 +X-ClickHouse-Ex---tion-Code:47 +"exception":"Code:47. + INSERT WITH default_format=JSON -Content-Type:application/json;charset=UTF-8 -"exception":"Code:62. +501NotImplemented +Content-Type:application/json;charset=UTF-8 +X-ClickHouse-Ex---tion-Code:48 +"exception":"Code:48. INSERT WITH default_format=XML -Content-Type:application/xml;charset=UTF-8 -Code:62.DB::Ex---tion: +501NotImplemented +Content-Type:application/xml;charset=UTF-8 +X-ClickHouse-Ex---tion-Code:48 +Code:48.DB::Ex---tion: INSERT WITH default_format=BADFORMAT -Content-Type:text/plain;charset=UTF-8 -X-ClickHouse-Ex---tion-Code:62 -Code:62.DB::Ex---tion: +501NotImplemented +Content-Type:text/plain;charset=UTF-8 +X-ClickHouse-Ex---tion-Code:48 +Code:48.DB::Ex---tion: + +SELECT missing column WITH X-ClickHouse-Format: JSON +404NotFound +Content-Type:application/json;charset=UTF-8 +X-ClickHouse-Ex---tion-Code:47 +"exception":"Code:47. INSERT WITH X-ClickHouse-Format: JSON -Content-Type:application/json;charset=UTF-8 -"exception":"Code:62. +501NotImplemented +Content-Type:application/json;charset=UTF-8 +X-ClickHouse-Ex---tion-Code:48 +"exception":"Code:48. INSERT WITH X-ClickHouse-Format: XML -Content-Type:application/xml;charset=UTF-8 -Code:62.DB::Ex---tion: +501NotImplemented +Content-Type:application/xml;charset=UTF-8 +X-ClickHouse-Ex---tion-Code:48 +Code:48.DB::Ex---tion: INSERT WITH X-ClickHouse-Format: BADFORMAT -Content-Type:text/plain;charset=UTF-8 -X-ClickHouse-Ex---tion-Code:62 -Code:62.DB::Ex---tion: +501NotImplemented +Content-Type:text/plain;charset=UTF-8 +X-ClickHouse-Ex---tion-Code:48 +Code:48.DB::Ex---tion: diff --git a/tests/queries/0_stateless/02899_use_default_format_on_http_exception.sh b/tests/queries/0_stateless/02899_use_default_format_on_http_exception.sh index f92ab7db4fb..5e91fa13e91 100755 --- a/tests/queries/0_stateless/02899_use_default_format_on_http_exception.sh +++ b/tests/queries/0_stateless/02899_use_default_format_on_http_exception.sh @@ -6,28 +6,44 @@ CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) CH_URL="$CLICKHOUSE_URL&http_write_exception_in_output_format=1" +echo "SELECT missing column WITH default_format=JSON" +echo "SELECT x FROM system.numbers LIMIT 1;"\ + | ${CLICKHOUSE_CURL} -sS "${CH_URL}&default_format=JSON" -i --data-binary @- \ + | grep 'HTTP/1.1\|xception\|Content-Type' | sed 's/Exception/Ex---tion/;s/HTTP\/1.1//;s/\r//' | awk '{ print $1 $2 $3 }' +echo "" echo "INSERT WITH default_format=JSON" echo "INSERT INTO system.numbers Select * from numbers(10);" \ - | ${CLICKHOUSE_CURL} -sS "${CH_URL}&default_format=JSON" -i | grep 'xception\|Content-Type' | sed 's/Exception/Ex---tion/' | awk '{ print $1 $2 $3 }' + | ${CLICKHOUSE_CURL} -sS "${CH_URL}&default_format=JSON" -i --data-binary @- \ + | grep 'HTTP/1.1\|xception\|Content-Type' | sed 's/Exception/Ex---tion/;s/HTTP\/1.1//;s/\r//' | awk '{ print $1 $2 $3 }' echo "" echo "INSERT WITH default_format=XML" echo "INSERT INTO system.numbers Select * from numbers(10);" \ - | ${CLICKHOUSE_CURL} -sS "${CH_URL}&default_format=XML" -i | grep 'xception\|Content-Type' | sed 's/Exception/Ex---tion/' | awk '{ print $1 $2 $3 }' + | ${CLICKHOUSE_CURL} -sS "${CH_URL}&default_format=XML" -i --data-binary @- \ + | grep 'HTTP/1.1\|xception\|Content-Type' | sed 's/Exception/Ex---tion/;s/HTTP\/1.1//;s/\r//' | awk '{ print $1 $2 $3 }' echo "" echo "INSERT WITH default_format=BADFORMAT" echo "INSERT INTO system.numbers Select * from numbers(10);" \ - | ${CLICKHOUSE_CURL} -sS "${CH_URL}&default_format=BADFORMAT" -i | grep 'xception\|Content-Type' | sed 's/Exception/Ex---tion/' | awk '{ print $1 $2 $3 }' + | ${CLICKHOUSE_CURL} -sS "${CH_URL}&default_format=BADFORMAT" -i --data-binary @- \ + | grep 'HTTP/1.1\|xception\|Content-Type' | sed 's/Exception/Ex---tion/;s/HTTP\/1.1//;s/\r//' | awk '{ print $1 $2 $3 }' +echo "" +echo "SELECT missing column WITH X-ClickHouse-Format: JSON" +echo "SELECT x FROM system.numbers LIMIT 1;"\ + | ${CLICKHOUSE_CURL} -sS "${CH_URL}" -H 'X-ClickHouse-Format: JSON' -i --data-binary @- \ + | grep 'HTTP/1.1\|xception\|Content-Type' | sed 's/Exception/Ex---tion/;s/HTTP\/1.1//;s/\r//' | awk '{ print $1 $2 $3 }' echo "" echo "INSERT WITH X-ClickHouse-Format: JSON" echo "INSERT INTO system.numbers Select * from numbers(10);" \ - | ${CLICKHOUSE_CURL} -sS "${CH_URL}" -H 'X-ClickHouse-Format: JSON' -i | grep 'xception\|Content-Type' | sed 's/Exception/Ex---tion/' | awk '{ print $1 $2 $3 }' + | ${CLICKHOUSE_CURL} -sS "${CH_URL}" -H 'X-ClickHouse-Format: JSON' -i --data-binary @- \ + | grep 'HTTP/1.1\|xception\|Content-Type' | sed 's/Exception/Ex---tion/;s/HTTP\/1.1//;s/\r//' | awk '{ print $1 $2 $3 }' echo "" echo "INSERT WITH X-ClickHouse-Format: XML" echo "INSERT INTO system.numbers Select * from numbers(10);" \ - | ${CLICKHOUSE_CURL} -sS "${CH_URL}" -H 'X-ClickHouse-Format: XML' -i | grep 'xception\|Content-Type' | sed 's/Exception/Ex---tion/' | awk '{ print $1 $2 $3 }' + | ${CLICKHOUSE_CURL} -sS "${CH_URL}" -H 'X-ClickHouse-Format: XML' -i --data-binary @- \ + | grep 'HTTP/1.1\|xception\|Content-Type' | sed 's/Exception/Ex---tion/;s/HTTP\/1.1//;s/\r//' | awk '{ print $1 $2 $3 }' echo "" echo "INSERT WITH X-ClickHouse-Format: BADFORMAT" echo "INSERT INTO system.numbers Select * from numbers(10);" \ - | ${CLICKHOUSE_CURL} -sS "${CH_URL}" -H 'X-ClickHouse-Format: BADFORMAT' -i | grep 'xception\|Content-Type' | sed 's/Exception/Ex---tion/' | awk '{ print $1 $2 $3 }' + | ${CLICKHOUSE_CURL} -sS "${CH_URL}" -H 'X-ClickHouse-Format: BADFORMAT' -i --data-binary @- \ + | grep 'HTTP/1.1\|xception\|Content-Type' | sed 's/Exception/Ex---tion/;s/HTTP\/1.1//;s/\r//' | awk '{ print $1 $2 $3 }'