better error handling for HeadersReader

This commit is contained in:
lganzzzo 2018-12-09 18:15:42 +02:00
parent 645666dbf9
commit 3a8fecee3d
18 changed files with 136 additions and 61 deletions

View File

@ -124,6 +124,8 @@ add_library(oatpp
web/client/HttpRequestExecutor.hpp
web/client/RequestExecutor.cpp
web/client/RequestExecutor.hpp
web/protocol/CommunicationError.cpp
web/protocol/CommunicationError.hpp
web/protocol/http/Http.cpp
web/protocol/http/Http.hpp
web/protocol/http/incoming/BodyDecoder.cpp
@ -162,8 +164,6 @@ add_library(oatpp
web/server/handler/Interceptor.hpp
web/server/HttpConnectionHandler.cpp
web/server/HttpConnectionHandler.hpp
web/server/HttpError.cpp
web/server/HttpError.hpp
web/server/HttpProcessor.cpp
web/server/HttpProcessor.hpp
web/server/HttpRouter.cpp

View File

@ -129,7 +129,11 @@ public:
}
void close() {
std::lock_guard<std::mutex> lock(m_mutex);
m_open = false;
m_mutex.unlock();
m_conditionRead.notify_one();
m_conditionWrite.notify_one();
}
};

View File

@ -52,7 +52,7 @@ bool FullTest::onRun() {
auto serverConnectionProvider = oatpp::network::virtual_::server::ConnectionProvider::createShared(interface);
auto clientConnectionProvider = oatpp::network::virtual_::client::ConnectionProvider::createShared(interface);
serverConnectionProvider->setSocketMaxAvailableToReadWrtie(20, -1);
serverConnectionProvider->setSocketMaxAvailableToReadWrtie(1, 10);
auto objectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared();
@ -72,9 +72,13 @@ bool FullTest::onRun() {
for(v_int32 i = 0; i < 1; i ++) {
auto response = client->getRoot();
auto text = response->readBodyToString();
OATPP_LOGD("client", "body='%s'", text->c_str());
try {
auto response = client->getRoot();
auto text = response->readBodyToString();
OATPP_LOGD("client", "body='%s'", text->c_str());
} catch(std::runtime_error e) {
OATPP_LOGD("client", "error='%s'", e.what());
}
}

View File

@ -107,14 +107,19 @@ HttpRequestExecutor::execute(const String& method,
upStream->flush();
oatpp::web::protocol::http::incoming::ResponseHeadersReader headerReader(ioBuffer->getData(), ioBuffer->getSize(), 4096);
oatpp::web::protocol::http::Status error;
oatpp::web::protocol::http::HttpError::Info error;
const auto& result = headerReader.readHeaders(connection, error);
if(error.code != 0) {
if(error.status.code != 0) {
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_PARSE_STARTING_LINE,
"[oatpp::web::client::HttpRequestExecutor::execute()]: Failed to parse response. Invalid response headers");
}
if(error.ioStatus < 0) {
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_PARSE_STARTING_LINE,
"[oatpp::web::client::HttpRequestExecutor::execute()]: Failed to read response.");
}
auto bodyStream = oatpp::data::stream::InputStreamBufferedProxy::createShared(connection,
ioBuffer,
result.bufferPosStart,

View File

@ -22,4 +22,22 @@
*
***************************************************************************/
#include "HttpError.hpp"
#include "CommunicationError.hpp"
namespace oatpp { namespace web { namespace protocol {
CommunicationError::CommunicationError(oatpp::os::io::Library::v_size ioStatus, const oatpp::String& message)
:std::runtime_error(message->std_str())
, m_ioStatus(ioStatus)
, m_message(message)
{}
oatpp::os::io::Library::v_size CommunicationError::getIOStatus() {
return m_ioStatus;
}
oatpp::String& CommunicationError::getMessage(){
return m_message;
}
}}}

View File

@ -22,40 +22,61 @@
*
***************************************************************************/
#ifndef oatpp_web_server_HttpError_hpp
#define oatpp_web_server_HttpError_hpp
#ifndef oatpp_web_protocol_CommunicationError_hpp
#define oatpp_web_protocol_CommunicationError_hpp
#include "oatpp/web/protocol/http/Http.hpp"
#include "oatpp/core/Types.hpp"
#include "oatpp/core/os/io/Library.hpp"
namespace oatpp { namespace web { namespace server {
namespace oatpp { namespace web { namespace protocol {
class HttpError : public std::runtime_error {
class CommunicationError : public std::runtime_error {
private:
oatpp::web::protocol::http::Status m_status;
oatpp::os::io::Library::v_size m_ioStatus;
oatpp::String m_message;
public:
HttpError(const oatpp::web::protocol::http::Status& status,
const oatpp::String& message)
:std::runtime_error(status.description)
, m_status(status)
, m_message(message)
CommunicationError(oatpp::os::io::Library::v_size ioStatus, const oatpp::String& message);
oatpp::os::io::Library::v_size getIOStatus();
oatpp::String& getMessage();
};
template<class Status>
class ProtocolError : public CommunicationError {
public:
struct Info {
Info()
: ioStatus(0)
{}
Info(oatpp::os::io::Library::v_size pIOStatus, const Status& pStatus)
: ioStatus(pIOStatus)
, status(pStatus)
{}
oatpp::os::io::Library::v_size ioStatus;
Status status;
};
private:
Info m_info;
public:
ProtocolError(const Info& info, const oatpp::String& message)
: CommunicationError(info.ioStatus, message)
, m_info(info)
{}
oatpp::web::protocol::http::Status getStatus() {
return m_status;
}
oatpp::String& getMessage(){
return m_message;
Info getInfo() {
return m_info;
}
};
#define OATPP_ASSERT_HTTP(COND, STATUS, MESSAGE) \
if(!(COND)) { throw oatpp::web::server::HttpError(STATUS, MESSAGE); }
}}}
#endif /* oatpp_web_server_HttpError_hpp */
#endif /* oatpp_web_protocol_CommunicationError_hpp */

View File

@ -22,11 +22,13 @@
*
***************************************************************************/
#ifndef oatpp_network_http_Protocol_hpp
#define oatpp_network_http_Protocol_hpp
#ifndef oatpp_web_protocol_http_Http_hpp
#define oatpp_web_protocol_http_Http_hpp
#include "oatpp/network/Connection.hpp"
#include "oatpp/web/protocol/CommunicationError.hpp"
#include "oatpp/core/parser/ParsingCaret.hpp"
#include "oatpp/core/data/share/MemoryLabel.hpp"
#include "oatpp/core/data/stream/Delegate.hpp"
@ -130,6 +132,22 @@ public:
};
class HttpError : public protocol::ProtocolError<Status> {
public:
HttpError(const Info& info, const oatpp::String& message)
: protocol::ProtocolError<Status>(info, message)
{}
HttpError(const Status& status, const oatpp::String& message)
: protocol::ProtocolError<Status>(Info(0, status), message)
{}
};
#define OATPP_ASSERT_HTTP(COND, STATUS, MESSAGE) \
if(!(COND)) { throw oatpp::web::protocol::http::HttpError(STATUS, MESSAGE); }
class Header {
public:
class Value {
@ -287,4 +305,4 @@ namespace std {
};
}
#endif /* oatpp_network_http_Protocol_hpp */
#endif /* oatpp_web_protocol_http_Http_hpp */

View File

@ -73,14 +73,14 @@ os::io::Library::v_size RequestHeadersReader::readHeadersSection(const std::shar
}
RequestHeadersReader::Result RequestHeadersReader::readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
http::Status& error) {
http::HttpError::Info& error) {
RequestHeadersReader::Result result;
oatpp::data::stream::ChunkedBuffer buffer;
auto res = readHeadersSection(connection, &buffer, result);
error.ioStatus = readHeadersSection(connection, &buffer, result);
if(res > 0) {
if(error.ioStatus > 0) {
auto headersText = buffer.toString();
oatpp::parser::ParsingCaret caret (headersText);
http::Status status;

View File

@ -62,7 +62,7 @@ public:
, m_maxHeadersSize(maxHeadersSize)
{}
Result readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection, http::Status& error);
Result readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection, http::HttpError::Info& error);
Action readHeadersAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback,
const std::shared_ptr<oatpp::data::stream::IOStream>& connection);

View File

@ -73,14 +73,14 @@ os::io::Library::v_size ResponseHeadersReader::readHeadersSection(const std::sha
}
ResponseHeadersReader::Result ResponseHeadersReader::readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
http::Status& error) {
http::HttpError::Info& error) {
Result result;
oatpp::data::stream::ChunkedBuffer buffer;
auto res = readHeadersSection(connection, &buffer, result);
error.ioStatus = readHeadersSection(connection, &buffer, result);
if(res > 0) {
if(error.ioStatus > 0) {
auto headersText = buffer.toString();
oatpp::parser::ParsingCaret caret (headersText);
http::Status status;

View File

@ -62,7 +62,7 @@ public:
, m_maxHeadersSize(maxHeadersSize)
{}
Result readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection, http::Status& error);
Result readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection, http::HttpError::Info& error);
Action readHeadersAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback,
const std::shared_ptr<oatpp::data::stream::IOStream>& connection);

View File

@ -26,6 +26,11 @@
namespace oatpp { namespace web { namespace protocol { namespace http { namespace outgoing {
bool CommunicationUtils::headerEqualsCI_FAST(const oatpp::data::share::MemoryLabel& headerValue, const char* value) {
v_int32 size = (v_int32) std::strlen(value);
return size == headerValue.getSize() && oatpp::base::StrBuffer::equalsCI_FAST(headerValue.getData(), value, size);
}
bool CommunicationUtils::considerConnectionKeepAlive(const std::shared_ptr<protocol::http::incoming::Request>& request,
const std::shared_ptr<protocol::http::outgoing::Response>& response){
@ -34,13 +39,12 @@ bool CommunicationUtils::considerConnectionKeepAlive(const std::shared_ptr<proto
/* Set keep-alive to value specified in the client's request, if no Connection header present in response. */
/* Set keep-alive to value specified in response otherwise */
auto it = request->headers.find(Header::CONNECTION);
if(it != request->headers.end() && oatpp::base::StrBuffer::equalsCI_FAST((const char*) it->second.getData(), Header::Value::CONNECTION_KEEP_ALIVE)) {
if(it != request->headers.end() && headerEqualsCI_FAST(it->second, Header::Value::CONNECTION_KEEP_ALIVE)) {
if(response->putHeaderIfNotExists(Header::CONNECTION, it->second)){
return true;
} else {
auto outKeepAlive = response->getHeaders().find(Header::CONNECTION);
return (outKeepAlive != response->getHeaders().end() &&
oatpp::base::StrBuffer::equalsCI_FAST((const char*)outKeepAlive->second.getData(), Header::Value::CONNECTION_KEEP_ALIVE));
return (outKeepAlive != response->getHeaders().end() && headerEqualsCI_FAST(outKeepAlive->second, Header::Value::CONNECTION_KEEP_ALIVE));
}
}
@ -48,11 +52,10 @@ bool CommunicationUtils::considerConnectionKeepAlive(const std::shared_ptr<proto
/* Set HTTP/1.1 default Connection header value (Keep-Alive), if no Connection header present in response. */
/* Set keep-alive to value specified in response otherwise */
auto& protocol = request->startingLine.protocol;
if(protocol.getData() != nullptr && oatpp::base::StrBuffer::equalsCI_FAST((const char*) protocol.getData(), "HTTP/1.1")) {
if(protocol.getData() != nullptr && headerEqualsCI_FAST(protocol, "HTTP/1.1")) {
if(!response->putHeaderIfNotExists(Header::CONNECTION, Header::Value::CONNECTION_KEEP_ALIVE)) {
auto outKeepAlive = response->getHeaders().find(Header::CONNECTION);
return (outKeepAlive != response->getHeaders().end() &&
oatpp::base::StrBuffer::equalsCI_FAST((const char*) outKeepAlive->second.getData(), Header::Value::CONNECTION_KEEP_ALIVE));
return (outKeepAlive != response->getHeaders().end() && headerEqualsCI_FAST(outKeepAlive->second, Header::Value::CONNECTION_KEEP_ALIVE));
}
return true;
}
@ -64,8 +67,7 @@ bool CommunicationUtils::considerConnectionKeepAlive(const std::shared_ptr<proto
/* Set keep-alive to value specified in response otherwise */
if(!response->putHeaderIfNotExists(Header::CONNECTION, Header::Value::CONNECTION_CLOSE)) {
auto outKeepAlive = response->getHeaders().find(Header::CONNECTION);
return (outKeepAlive != response->getHeaders().end() &&
oatpp::base::StrBuffer::equalsCI_FAST((const char*)outKeepAlive->second.getData(), Header::Value::CONNECTION_KEEP_ALIVE));
return (outKeepAlive != response->getHeaders().end() && headerEqualsCI_FAST(outKeepAlive->second, Header::Value::CONNECTION_KEEP_ALIVE));
}
return false;

View File

@ -31,6 +31,8 @@
namespace oatpp { namespace web { namespace protocol { namespace http { namespace outgoing {
class CommunicationUtils {
private:
static bool headerEqualsCI_FAST(const oatpp::data::share::MemoryLabel& headerValue, const char* value);
public:
/**

View File

@ -28,7 +28,6 @@
#include "oatpp/web/protocol/http/incoming/Request.hpp"
#include "oatpp/web/protocol/http/Http.hpp"
#include "oatpp/web/server/HttpError.hpp"
#include "oatpp/network/Connection.hpp"
#include "oatpp/test/Checker.hpp"

View File

@ -23,7 +23,6 @@
***************************************************************************/
#include "./HttpConnectionHandler.hpp"
#include "./HttpError.hpp"
#include "oatpp/web/protocol/http/outgoing/ChunkedBufferBody.hpp"
#include "oatpp/web/protocol/http/incoming/Request.hpp"

View File

@ -23,7 +23,6 @@
***************************************************************************/
#include "HttpProcessor.hpp"
#include "./HttpError.hpp"
#include "oatpp/web/protocol/http/outgoing/CommunicationUtils.hpp"
@ -43,11 +42,16 @@ HttpProcessor::processRequest(HttpRouter* router,
bool& keepAlive) {
RequestHeadersReader headersReader(buffer, bufferSize, 4096);
oatpp::web::protocol::http::Status error;
oatpp::web::protocol::http::HttpError::Info error;
auto headersReadResult = headersReader.readHeaders(connection, error);
if(error.code != 0) {
return errorHandler->handleError(error, "Invalid request headers");
if(error.status.code != 0) {
return errorHandler->handleError(error.status, "Invalid request headers");
}
if(error.ioStatus < 0) {
keepAlive = false;
return nullptr;
}
auto route = router->getRoute(headersReadResult.startingLine.method.toString(), headersReadResult.startingLine.path.toString());
@ -78,8 +82,8 @@ HttpProcessor::processRequest(HttpRouter* router,
if(!response) {
response = route.processUrl(request);
}
} catch (HttpError& error) {
return errorHandler->handleError(error.getStatus(), error.getMessage());
} catch (oatpp::web::protocol::http::HttpError& error) {
return errorHandler->handleError(error.getInfo().status, error.getMessage());
} catch (std::exception& error) {
return errorHandler->handleError(protocol::http::Status::CODE_500, error.what());
} catch (...) {
@ -175,8 +179,8 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::handleError(const oat
if (error.isExceptionThrown) {
try{
throw;
} catch (HttpError& error) {
m_currentResponse = m_errorHandler->handleError(error.getStatus(), error.getMessage());
} catch (oatpp::web::protocol::http::HttpError& error) {
m_currentResponse = m_errorHandler->handleError(error.getInfo().status, error.getMessage());
} catch (std::exception& error) {
m_currentResponse = m_errorHandler->handleError(protocol::http::Status::CODE_500, error.what());
} catch (...) {

View File

@ -51,7 +51,7 @@ std::shared_ptr<ApiController::OutgoingResponse> ApiController::handleError(cons
if(m_errorHandler) {
return m_errorHandler->handleError(status, message);
}
throw oatpp::web::server::HttpError(status, message);
throw oatpp::web::protocol::http::HttpError(status, message);
}
void ApiController::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){

View File

@ -27,7 +27,6 @@
#include "./Endpoint.hpp"
#include "oatpp/web/server/HttpError.hpp"
#include "oatpp/web/server/handler/ErrorHandler.hpp"
#include "oatpp/web/server/HttpConnectionHandler.hpp"
#include "oatpp/web/url/mapping/Router.hpp"