dbms: returning proper HTTP code in case of errors [#CONV-2944].

This commit is contained in:
Alexey Milovidov 2012-10-29 07:19:47 +00:00
parent b205d51215
commit 274f2d0828
4 changed files with 81 additions and 10 deletions

View File

@ -0,0 +1,55 @@
#pragma once
#include <Poco/Net/HTTPServerResponse.h>
#include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h>
#include <DB/IO/WriteBuffer.h>
#include <DB/IO/BufferWithOwnMemory.h>
namespace DB
{
/** Отличается от WriteBufferFromOStream тем, что инициализируется не std::ostream, а Poco::Net::HTTPServerResponse.
* При первом сбросе данных, получает из него std::ostream (с помощью метода send).
* Это нужно в HTTP серверах, чтобы после передачи в какой-нибудь метод WriteBuffer-а,
* но до вывода первых данных клиенту, можно было изменить какие-нибудь HTTP заголовки (например, код ответа).
* (После вызова Poco::Net::HTTPServerResponse::send() изменить заголовки уже нельзя.)
* То есть, суть в том, чтобы вызывать метод Poco::Net::HTTPServerResponse::send() не сразу.
*/
class WriteBufferFromHTTPServerResponse : public BufferWithOwnMemory<WriteBuffer>
{
private:
Poco::Net::HTTPServerResponse & response;
std::ostream * ostr;
void nextImpl()
{
if (!ostr)
ostr = &response.send();
if (!offset())
return;
ostr->write(working_buffer.begin(), offset());
ostr->flush();
if (!ostr->good())
throw Exception("Cannot write to ostream", ErrorCodes::CANNOT_WRITE_TO_OSTREAM);
}
public:
WriteBufferFromHTTPServerResponse(Poco::Net::HTTPServerResponse & response_, size_t size = DBMS_DEFAULT_BUFFER_SIZE)
: BufferWithOwnMemory<WriteBuffer>(size), response(response_), ostr(NULL) {}
~WriteBufferFromHTTPServerResponse()
{
if (!std::uncaught_exception())
next();
}
};
}

View File

@ -12,7 +12,7 @@
#include <DB/IO/ConcatReadBuffer.h>
#include <DB/IO/CompressedReadBuffer.h>
#include <DB/IO/CompressedWriteBuffer.h>
#include <DB/IO/WriteBufferFromOStream.h>
#include <DB/IO/WriteBufferFromHTTPServerResponse.h>
#include <DB/IO/WriteBufferFromString.h>
#include <DB/IO/WriteHelpers.h>
@ -40,7 +40,7 @@ struct HTMLForm : public Poco::Net::HTMLForm
};
void HTTPHandler::processQuery(Poco::Net::NameValueCollection & params, std::ostream & ostr, std::istream & istr)
void HTTPHandler::processQuery(Poco::Net::NameValueCollection & params, Poco::Net::HTTPServerResponse & response, std::istream & istr)
{
BlockInputStreamPtr query_plan;
@ -64,7 +64,7 @@ void HTTPHandler::processQuery(Poco::Net::NameValueCollection & params, std::ost
ConcatReadBuffer in(in_param, *in_post_maybe_compressed);
/// Если указано compress, то будем сжимать результат.
SharedPtr<WriteBuffer> out = new WriteBufferFromOStream(ostr);
SharedPtr<WriteBuffer> out = new WriteBufferFromHTTPServerResponse(response);
SharedPtr<WriteBuffer> out_maybe_compressed;
if (0 != Poco::NumberParser::parseUnsigned(params.get("compress", "0")))
@ -130,28 +130,41 @@ void HTTPHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Ne
if (is_browser)
response.setContentType("text/plain; charset=UTF-8");
std::ostream & ostr = response.send();
try
{
LOG_TRACE(log, "Request URI: " << request.getURI());
HTMLForm params(request);
std::istream & istr = request.stream();
processQuery(params, ostr, istr);
processQuery(params, response, istr);
LOG_INFO(log, "Done processing query");
}
catch (DB::Exception & e)
{
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
std::ostream & ostr = response.send();
std::stringstream s;
s << "Code: " << e.code()
<< ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what();
ostr << s.str() << std::endl;
LOG_ERROR(log, s.str());
}
catch (Poco::Exception & e)
{
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
std::ostream & ostr = response.send();
std::stringstream s;
s << "Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code()
<< ", e.message() = " << e.message() << ", e.what() = " << e.what();
<< ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what();
ostr << s.str() << std::endl;
LOG_ERROR(log, s.str());
}
catch (std::exception & e)
{
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
std::ostream & ostr = response.send();
std::stringstream s;
s << "Code: " << ErrorCodes::STD_EXCEPTION << ". " << e.what();
ostr << s.str() << std::endl;
@ -159,6 +172,8 @@ void HTTPHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Ne
}
catch (...)
{
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
std::ostream & ostr = response.send();
std::stringstream s;
s << "Code: " << ErrorCodes::UNKNOWN_EXCEPTION << ". Unknown exception.";
ostr << s.str() << std::endl;

View File

@ -25,7 +25,7 @@ private:
Logger * log;
void processQuery(Poco::Net::NameValueCollection & params, std::ostream & ostr, std::istream & istr);
void processQuery(Poco::Net::NameValueCollection & params, Poco::Net::HTTPServerResponse & response, std::istream & istr);
};
}

View File

@ -93,7 +93,7 @@ void TCPHandler::runImpl()
}
catch (DB::Exception & e)
{
LOG_ERROR(log, "DB::Exception. Code: " << e.code() << ", e.displayText() = " << e.displayText()
LOG_ERROR(log, "DB::Exception. Code: " << e.code() << ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what()
<< ", Stack trace:\n\n" << e.getStackTrace().toString());
state.exception = e.clone();
@ -102,7 +102,8 @@ void TCPHandler::runImpl()
}
catch (Poco::Exception & e)
{
LOG_ERROR(log, "Poco::Exception. Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code() << ", e.displayText() = " << e.displayText());
LOG_ERROR(log, "Poco::Exception. Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code()
<< ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what());
state.exception = new Exception(e.message(), e.code());
}
catch (std::exception & e)